Оптимизация SQL запросов для постраничного вывода больших каталогов

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

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

Звучит логично, но я задался вопросом, а какую же именно скорость выборок ожидать клиенту при разных объемах данных. Сколько будет формироваться страница в каталоге на 5 позиций, сколько на 500, сколько на 5 000, и сколько на 1 000 000 записей? Допустим, потенциальный клиент хочет знать к чему ему готовится при работе с каталогом на 200 000 позиций, что ему ответить?

Чтобы получить точный ответ на этот вопрос, я собрал демо-стенд с тестовыми данными на PHP фреймворке CodeIgniter 3, он устарел много лет назад, но тем не менее имеет стабильные версии, совместимые с современными версиями PHP, и для этой задачи подошел идеально. Итого, в таблице 10 столбцов целого типа (int), в каждом из этих столбцов может находится число от 0 до 100. В таблице 1 000 000 (один миллион) строк. Каждая строка может быть отфильтрована по точному значению, или в диапазоне от и до, плюс есть сортировка в любом направлении по любому из полей. На странице выводится максимальное 10 элементов и есть переход между страницами. Подобная система имитирует работу фильтров в типовом каталоге.

Какие же результаты мы имеем?

  • Загрузка первой страницы в каталоге: 0,02 секунды
  • Загрузка последней страницы в каталоге: 0,2 секунды
  • Загрузка 20 000 страницы в каталоге: 0.09 секунды (последняя страница в каталоге на 200 000 позиций)

При больших объемах данных производительность обратным образом зависит от количества фильтров которые мы применяем к выборке. Это значит, что если мы применяем 10 фильтров, то все работает очень быстро потому что базе данных приходится работать с меньшим объемом данных, чем с более простыми фильтрами, или вообще без них. Но главное на что надо обратить внимание, это то что проблемы с производительностью зависят не от количества фильтров, а от того какую страницу в выборке мы хотим открыть. И самое неприятное то что медленно работает запрос определения общего количества элементов под условия фильтра. Первая страница каталога будет открываться примерно также медленно как и последняя, если на первой странице мы будем получать общее количество элементов.

Нет никакого секретного рецепта волшебных запросов к БД от профессионалов SQL, чтобы ваш сайт полетел. Оптимизация происходит не на программном уровне, а на уровне разработки технического задания. Если ТЗ и макеты не учитывают особенности работы с большими данными, то программистам остается только вводить кеширование всего чего можно, там где можно, и даже там где нельзя.

Особенности формирования больших каталогов:

  • Не делать выводов больших каталогов с постраничной пагинацией, ваши пользователи не будут кликать назад/вперед по сотням и тысячам страниц. Скорее всего никто и никогда не будет смотреть данные дальше 100 страницы, а если кому-то нужно попасть в конец списка, то его нужно просто отсортировать в обратном порядке, и открыть первую страницу.

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

  • Не писать общее количество данных, вроде "найдено 100 элементов из 10 000", т.к. подобная строчка в 20 раз увеличивает время загрузки первой страницы каталога, она содержит целых два дополнительных тяжелых запроса.

  • Общее количество данных в каталоге уместно кешировать.

  • Не делать прямую ссылку на последнюю страницу в каталоге в пагинации. Если такая ссылка находится на каждой странице каталога, то независимо от того какая страница открыта, будет выполняться тяжелый запрос на выборку всех элементов. Я сделал отдельный эндпойнт для последней страницы и делаю ссылку на него, а он уже определяет последнюю страницу и делает переадресацию. Так тяжелый запрос выполняется один раз, только когда это действительно нужно.

С первого взгляда, может показаться что выполнение этих советов сильно ограничивает ваш сайт, но присмотритесь к другим крупным сайтам в сети. Когда последний раз вы видели пагинацию с более чем 1 000 страницами? А на всех ли сайтах написано сколько элементов найдено? А вы уверены, что ссылка на последнюю страницу каталога есть на всех сайтах которыми вы пользуетесь?

Обсудим как будем выводить данные, или будете дальше искать волшебную SQL пилюлю?

09.03.2026

Комментарии

  • Нет комментариев.

Добавить комментарий

Сайт не собирает персональных данных через форму добавления комментариев.