Стратегии пагинации для сложных (медленных) наборов данных - PullRequest
6 голосов
/ 09 ноября 2009

Какие стратегии используются для разбиения на страницы наборов данных, которые включают сложные запросы? count (*) занимает ~ 1,5 сек, поэтому мы не хотим использовать БД для каждого просмотра страницы. В настоящее время по этому запросу возвращено ~ 45 тыс. Строк.

Вот некоторые из подходов, которые я рассмотрел:

  • Кэшировать счетчик строк и обновлять его каждые X минут
  • Ограничить (и сместить) строки, подсчитанные до 41 (например), и отобразить средство выбора страницы как "1 2 3 4 ..."; затем повторно вычислите, если кто-то действительно перейдет на страницу 4, и отобразите «... 3 4 5 6 7 ...»
  • Получите количество строк один раз и сохраните его в сеансе пользователя
  • Избавьтесь от средства выбора страниц и просто добавьте ссылку «Следующая страница»

Ответы [ 5 ]

4 голосов
/ 09 ноября 2009

Я предлагаю попросить MySQL на 1 строку больше, чем вам нужно в каждом запросе, и на основе количества строк в наборе результатов решить, показывать или нет next page -линку.

4 голосов
/ 09 ноября 2009

Мне пришлось разработать несколько стратегий разбиения на страницы с использованием PHP и MySQL для сайта, который просматривает более миллиона страниц в день. Я угадал стратегию поэтапно:

Многостолбцовые индексы Я должен был сделать это первым, прежде чем пытаться материализовать представление.

Создание материализованного представления . Я создал задание cron, которое выполняло обычную денормализацию таблиц документов, которые я использовал. Я бы SELECT ... INTO OUTFILE ... затем создал бы новую таблицу и повернул ее:

SELECT ... INTO OUTFILE '/tmp/ondeck.txt' FROM mytable ...;
CREATE TABLE ondeck_mytable LIKE mytable;
LOAD DATA INFILE '/tmp/ondeck.txt' INTO TABLE ondeck_mytable...;
DROP TABLE IF EXISTS dugout_mytable;
RENAME TABLE atbat_mytable TO dugout_mytable, ondeck_mytable TO atbat_mytable;

Это позволило сократить время блокировки на заявленную запись mytable до минимума, и запросы на нумерацию страниц могли отбросить в материализованном представлении atbat. Я упростил вышесказанное, оставив фактические манипуляции, которые не важны.

Memcache Затем я создал оболочку для соединения с моей базой данных, чтобы кэшировать эти разбитые на страницы результаты в memcache. Это была огромная победа. Тем не менее, это все еще не было достаточно хорошо.

Пакетная генерация Я написал демон PHP и извлек в него логику разбиения на страницы. Он будет обнаруживать изменения mytable и периодически преобразовывать из самой старой измененной записи в самую последнюю запись все страницы в файловой системе веб-сервера. С небольшим значением mod_rewrite я могу проверить, существует ли страница на диске, и обработать ее. Это также позволило мне эффективно использовать обратное проксирование , позволяя Apache обнаруживать If-Modified-Since заголовки и отвечать с помощью 304 кодов ответов. (Очевидно, я удалил любую опцию, позволяющую пользователям выбирать количество результатов на страницу, неважную функцию.)

Обновлено: RE count(*): При использовании таблиц MyISAM COUNT не создавало проблемы, когда мне удавалось уменьшить количество конфликтов чтения-записи в таблице. Если бы я делал InnoDB, я бы создал триггер, который обновил соседнюю таблицу с количеством строк. Этот триггер будет просто +1 или -1 в зависимости от операторов INSERT или DELETE.

RE сборщики страниц (колесики) Когда я перешел к агрессивному кешированию запросов, запросы колесика большого пальца также кэшировались, и когда дело дошло до пакетной генерации страниц, я использовал временные таблицы - так что колесико не было проблемой. Многочисленные упрощенные вычисления упрощались, потому что они стали предсказуемым шаблоном файловой системы, который фактически нуждался только в наибольшем числе страниц. Наименьший номер страницы всегда был 1.

Windowed thumbweel Пример, приведенный выше для windowed thumbwheel (<< 4 [5] 6 >>), должен быть довольно простым без каких-либо запросов, если вы знаете свое максимальное число страниц.

2 голосов
/ 09 ноября 2009

MySQL имеет специальный механизм для вычисления приблизительного количества результирующего набора без предложения LIMIT: FOUND_ROWS().

1 голос
/ 09 ноября 2009

MySQL неплохо подходит для оптимизации LIMIT запросов.

Это означает, что он выбирает соответствующий буфер объединения, буфер файловой сортировки и т. Д., Достаточный для удовлетворения условия LIMIT.

Также обратите внимание, что с 45k строками вам, вероятно, не нужно точное количество. Приблизительное количество может быть вычислено с помощью отдельных запросов к индексированным полям. Скажем, этот запрос:

SELECT  COUNT(*)
FROM    mytable
WHERE   col1 = :myvalue
        AND col2 = :othervalue

может быть аппроксимировано этим:

SELECT  COUNT(*) *
        (
        SELECT  COUNT(*)
        FROM    mytable
        ) / 1000
FROM    (
        SELECT  1
        FROM    mytable
        WHERE   col1 = :myvalue
                AND col2 = :othervalue
        LIMIT 1000
        )

, что гораздо эффективнее в MyISAM.

Если вы приведете пример вашего сложного запроса, возможно, я могу сказать что-то более определенное о том, как улучшить его нумерацию страниц.

0 голосов
/ 09 ноября 2009

Я ни в коем случае не эксперт MySQL, но, возможно, я отказался от COUNT (*) и продолжил работу с COUNT (id)?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...