Какие стратегии пагинации можно использовать вместо LIMIT OFFSET?
Что проверяют
Знаете ли вы, почему OFFSET тормозит на глубоких страницах, и умеете ли написать keyset-запрос без дублей и пропусков. Следом просят показать сам SQL и спрашивают, как сортировать по неуникальному полю.
Как отвечать
Сначала причина: при OFFSET 100000 база всё равно находит и отбрасывает сто тысяч строк, время растёт линейно с номером страницы. Вдобавок при вставке новых записей строки сдвигаются, и пользователь видит дубли на соседних страницах.
Альтернативы:
- keyset (seek): запоминаете значения сортировки последней строки и продолжаете с них:
WHERE (created_at, id) < ($1, $2) ORDER BY created_at DESC, id DESC LIMIT 20. С составным индексом по(created_at, id)каждая страница стоит одинаково; - непрозрачный курсор в API: те же значения кладутся в структуру, сериализуются через
encoding/jsonи кодируютсяbase64.URLEncoding. Клиент получаетnext_cursorи не знает, что внутри, а вы можете сменить формат; - серверный курсор:
DECLARE ... CURSORв PostgreSQL или потоковое чтение черезrows.Next()вpgx. Подходит для выгрузок и фоновых задач, но держит транзакцию открытой; - отложенный join: сначала
OFFSETпо узкому индексу только заid, потом join за полными строками. Смягчает проблему, но не убирает её.
Ответ сильнее, если вы честно назовёте цену keyset: к произвольной странице номер 57 не перейти, есть только «дальше» и «назад».
Где спотыкаются
Сортируют только по created_at. У двух записей одинаковое время, и на границе страниц одна из них теряется. Уникальный id вторым ключом сортировки обязателен.
Пишут keyset без подходящего индекса. Условие есть, индекса нет, и база снова сканирует и сортирует всё.
Считают COUNT(*) на каждый запрос ради надписи «найдено 12 345». На больших таблицах это дороже самой страницы: лучше приблизительная оценка или флаг has_more через LIMIT 21 при странице в 20 строк.