Выборки со смещением (LIMIT/OFFSET) могут работать очень медленно при больших значениях смещений. Это происходит по причине того, что MySQL перебирает и отбрасывает все строки результата, пока их количество не будет равно значению OFFSET. Например:
SELECT * FROM articles ORDER BY id LIMIT **20000, 20**
Такой запрос будет использоваться для показа 1000й страницы статей (по 20 статей на страницу). В таком случае, MySQL переберет и отбросит 20 тыс. записей до того, как вернет 20 нужных нам.
Естественно, с ростом номера страницы, запрос будет работать все медленнее.
Обычно реализация постраничного вывода подразумевает:
В стандартном случае все реализуется довольно просто:
Количество всех результатов:
SELECT count(*) FROM articles
Ссылки на страницы:
for ( $i = 1; $i <= $count; $i++ )
{
echo "<a href="?page={$i}">страница {$i}</a>";
} Проведем эксперимент. В нашей таблице есть 500 000 записей. Выполним ряд запросов со смещением:
# Запрос с выборкой первой страницы SELECT * FROM articles ORDER BY id DESC LIMIT 0, 20 # Время исполнения: 0.08 секунды
# Запрос с выборкой одной из последних страниц SELECT * FROM articles ORDER BY id DESC LIMIT 480000, 20 # Время исполнения: 0.8 секунды
Заметно, что второй запрос выполнялся в 10 раз дольше, чем первый. Причем мы рассматриваем достаточно простой вариант, с элементарным запросом и структурой таблицы.
Если Вы используете таблицы InnoDB, то count(*) будет работать очень медленно. Это вторая проблема, которая приводит к замедлению работы постраничной выборки.
Лучшее решение проблемы — избежать ее:
Стоит подумать о переносе задачи постраничного вывода на другую технологию. Тогда Вы делаете выборку абсолютно всех строк, кэшируете ее и организовываете постраничный вывод на уровне, например, PHP. Для того, что-бы не хранить в кеше результаты огромной выборки, стоит кешировать только первичные ключи записей и список ID:
<?
$list = [1,2,3,4,5]; # список ID статей
$page = 1;
...
memcache_set('list-page' . $page, $list); # сохраняем список в кеш
...
foreach ( $list as $id )
{
$article = memcache_get('article' . $id); # каждую статью достаем из кеша
...
} Попробуйте избавиться от LIMIT OFFSET, если это возможно. Например, можно передавать первичный ключ первой записи на странице, и тогда выборка результатов страницы будет выглядеть так:
SELEСT * FROM articles WHERE id > 350 LIMIT 20
Чтобы узнать, нужно ли делать ссылку на след. страницу можно выбрать не 20, а 21 запись. Показывать все равно будем только 20. А отсутствие 21й в результатах будет говорить о том, что ссылка не нужна.
В случае, если таблица меняется редко (несколько раз в сутки), кэшируйте полный результат каждой страницы.
Для замены count(*) можно парсить результаты EXPLAIN. Это даст оценочный результат (с погрешностью около 30%), но сработает намного быстрее.
Стандартная реализация постраничного вывода может быть очень медленной. Откажитесь от списков страниц и показа дальних страниц если это возможно. В другом случае используйте кеширование идентификаторов или замените OFFSET на выборку по ID.
Visual Code от Microsoft, вероятно, один из самых популярных редакторов кода. Разработчики любят его за…
Япония сама по себе — сплошной киберпанк. Это заметил даже культовый писатель жанра Уильям Гибсон,…
Сам по себе телефон Айфон 17 Про Макс – отличный подарок. У него красивая заводская…
На фоне роста спроса на ликвидность в бычьем рынке 2025 года, криптозаймы снова выходят на…
Прокси (proxy), или прокси-сервер — это программа-посредник, которая обеспечивает соединение между пользователем и интернет-ресурсом. Принцип…
Согласитесь, было бы неплохо соединить в одно сайт и приложение для смартфона. Если вы еще…