TGViewer
EvApps EvApps @evapps_team · 199 subscribers
Post #1526 69
📄 Почему пагинация тормозит на дальних страницах
Пагинация через LIMIT / OFFSET есть почти везде — и работает отлично ровно до тех пор, пока юзер не улистал далеко
Первая страница летает, а где-нибудь на 500-й всё еле ползёт
Хотя отдаёшь ты те же 20 строк
Как сделать, чтобы скорость не зависела от номера страницы

🐌 Что не так с OFFSET:
SELECT * FROM posts
ORDER BY created_at DESC
LIMIT 20 OFFSET 100000;

Ты думаешь «дай мне 20 штук со сдвигом»
А база понимает это буквально: она проходит первые 100 000 строк, отсчитывает их одну за другой, выбрасывает — и только потом отдаёт следующие 20
То есть чем дальше страница, тем больше строк она перелопачивает впустую
На первой странице OFFSET 0, летает
На тысячной — прогоняет сотню тысяч строк ради двадцати

🚀 Keyset-пагинация
Идея простая: не считать сдвиг, а запоминать, на чём остановились, и просить «дай мне то, что идёт после вот этого»
SELECT * FROM posts
WHERE created_at < :last_seen_created_at
ORDER BY created_at DESC
LIMIT 20;

Здесь база не отсчитывает ничего — она по индексу сразу прыгает в нужное место и берёт двадцать штук
Скорость одинаковая что на первой странице, что на миллионной
Ты просто передаёшь с каждой страницей «якорь» последней строки (обычно id или дату), а на следующий запрос отдаёшь его обратно

⚖️ В чём подвох Keyset не панацея, у него есть ограничение: нельзя прыгнуть сразу на «страницу 500»
Ты можешь идти только вперёд и назад, от текущего места
Для нумерации страниц 1-2-3...500 это не годится
Но честно — а где тебе реально нужны номера страниц?
В бесконечной ленте, подгрузке по скроллу, выгрузке данных пачками, API с курсором — везде листают последовательно, и keyset там идеален
А классический OFFSET оставь для админок и мест, где страниц пара десятков и на скорость плевать

🎭 Бонусная беда OFFSET — он ещё и врёт
Про скорость понятно, но есть второй, менее очевидный косяк
Пока юзер листает, в таблицу сыплются новые записи
Добавили пару строк в начало (а сортировка-то по свежести) — и весь список поехал на две позиции вниз
В итоге на второй странице юзер снова видит записи, которые уже пролистал на первой
Классика лент, где при скролле «мелькают одинаковые посты»
Keyset этим не болеет: якорь привязан к конкретной строке, а не к порядковому номеру, поэтому новые вставки список не сдвигают
И маленький нюанс по самому keyset: сортируй по чему-то уникальному
Если у двух строк одинаковая дата, а якорь только по дате — на границе страниц можно потерять или задублировать запись
С уникальным ключом такого не будет

💡 Про счётчик «страница 3 из 500»
Частый вопрос: а как же показать общее число страниц, если мы листаем по Keyset?
На больших таблицах точный total ты и с OFFSET безболезненно не получишь, COUNT(*) по миллионам строк сам по себе тормозит
Поэтому в больших лентах его обычно и не показывают: либо примерное число («около 10k+»), либо просто кнопка «ещё»
Если точный счётчик реально нужен — это отдельная история с кэшированием, а не то, ради чего стоит держать медленный OFFSET

А вы как реализуете большие списки? 🤔

#database #postgres #backend #performance #sql #dev
  • 👍 2
More from @evapps_team
  1. Sep 23, 2026🔁 Ретраи, которые добивают лежачий сервис Сервис-сосед начал тупить, отвечает через раз Л…
  2. Sep 23, 2026Хотите не теряться среди сотен откликов? На hh сейчас бывает ощущение, что до HR просто не…
  3. Sep 23, 2026photo post
  4. Sep 21, 2026🧩 Что тут не так? Код-загадка Формат новый - показываю код, ты угадываешь подвох, ниже ра…
  5. Sep 18, 2026🎭 Мифы про производительность, в которые верят даже опытные Миф 1: "Меньше строк кода - б…
  6. Sep 16, 2026🚨 Как перевод денег уронил нам прод ⏰ 19:10 Задеплоили долгожданное - переводы между коше…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →