aprenoir
@aprenoir
программный архитектор

Как оптимизировать постраничный вывод из большого объема данных?

Есть большая выборка данных (50 млн.), надо вывести из нее первые 10 записей по наиболее свежей дате (после чего подзагружать еще по 10 записей). Сложность в том, что данные сгруппированы в 10 разделов (в каждом от 2-3 до 10 млн. записей) и выводить надо только данные из этого раздела.

SELECT * FROM posts WHERE type=1 ORDER BY updated DESC LIMIT 10;


По этому запросу EXPLAIN показывает, что MySQL сначала выбирает несколько млн. строк, соответствующих нужному типу, после чего их сортирует по дате.

В таблице стоит составной индекс (type, updated), но он не сильно помогает. MySQL использует только первую его часть.

Тоже самое при выводе последующих страниц

SELECT * FROM posts WHERE type=1 AND updated<offset
ORDER BY updated DESC LIMIT 10;


Как оптимизировать этот запрос?
  • Вопрос задан
  • 395 просмотров
Решения вопроса 1
aprenoir
@aprenoir Автор вопроса
программный архитектор
В итоге пришел к решению на уровне логики приложения. В основной базе постоянно содержится 0.5-1 млн наиболее свежих записей, а остальные выносятся в архивную. Скрипт-архиватор раз в сутки переносит старые записи в архив. Первоначально пользователям выдаются только наиболее свежие данные из основной базы (99% запросов), а если их недостаточно, то производится поиск в архивной.

P.S. В дополнение, если пользователь начал листать ленту - сделал вывод не 10, а 50 записей. При скроллинге клиент 1 раз выдает данные из запроса, а остальные 4 пакета помещает в массив и выдает по мере необходимости. Когда данные клиента заканчиваются, делается новый запрос. Сразу 50 не вывожу, чтобы не тормозить браузер (там много графики). Что-то подобное встречается в vk и ряде других крупных сайтов.

P.P.S. Итоговое решение на уровне MySQL получилось следующим. Провел вручную тестирование с выборками на основе жестко прописанного (use index) простого или составного индекса. Наилучшим по производительности оказалось использование составного индекса (например, (type, update)), использование которого жестко прописано в коде приложения в зависимости от конкретного типа выборки (по-умолчанию, в ряде случает MySQL выбирает не самый производительный индекс). Удивило расхождение данных, выдаваемых EXPLAIN с реальными показателями производительности. Так, простой индекс (EXPLAIN показывает rows 10) работал в сотни раз медленнее составного с rows в несколько млн. записей.

P.P.P.S. В общем, проблема была в неправильном выборе движком MySQL индекса по которому проводился поиск (в некоторых случаях использовался только простой индекс, когда лучше было использовать составной, а в некоторых поиск проводился сразу по 2-м индексам с объединением результатов). При прописывании USE INDEX вручную (на каждый конкретный случай) производительность многократно возросла.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
DrunkMaster
@DrunkMaster
Гуглите "Курсоры MySQL"
Ответ написан
ThunderCat
@ThunderCat Куратор тега MySQL
{PHP, MySql, HTML, JS, CSS} developer
SELECT * FROM (
  SELECT * 
  FROM posts 
  WHERE updated < offset
  ORDER BY updated DESC
 ) AS `table` 
WHERE type = 1
LIMIT 10


или так, кажется, пусть меня поправит кто нть если косяк увидит, пишу по памяти

SELECT * FROM post AS p1
INNER JOIN
     (SELECT *
     FROM post
     WHERE updated < offset
     ORDER BY updated DESC
     ) as p2
ON p1.id = p2.id
WHERE p2.type = 1
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы