Пагинация больших списков
Вопрос
Как бы ты сделал пагинацию для ленты из десятков миллионов записей? Чем плох OFFSET?
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
OFFSET заставляет базу прочитать и выбросить все пропущенные строки, стоимость растёт с номером страницы
Без однозначного ORDER BY страницы пересекаются или теряют строки
Keyset (cursor) пагинация: WHERE по последнему ключу и ORDER BY по тем же колонкам, плюс индекс под них
Ключ сортировки должен быть уникальным: добавляют id вторым ключом
Минусы keyset: нельзя прыгнуть на страницу N, курсор передают клиенту непрозрачной строкой
Точный COUNT(*) по огромной таблице дорогой: показывают «есть ещё» или оценку
Что могут спросить следом
- Как сделать курсор для сортировки по дате и id?
- Что будет, если между запросами страниц вставили новые записи?
- Как быть, если продукт требует номера страниц?
Разбор
На ленте из десятков миллионов строк OFFSET плох тем, что база не умеет прыгать на позицию: для OFFSET 1000000 она пройдёт и выбросит миллион строк, и глубокие страницы становятся всё медленнее. Плюс при вставках между запросами строки съезжают, и пользователь видит дубли или пропуски. Я бы сделал keyset-пагинацию: сортировка по (created_at, id), индекс по этим колонкам, а следующая страница запрашивается условием WHERE (created_at, id) < ($1, $2) ORDER BY created_at DESC, id DESC LIMIT 20. id вторым ключом делает порядок однозначным. Клиенту отдаю курсор как непрозрачную строку, например base64 от этих двух значений. Каждая страница стоит одинаково, вставки не ломают выдачу. Минус: нельзя открыть сразу страницу 500, но лентам это и не нужно. Общее число записей тоже дорого считать точно, поэтому показываю «загрузить ещё» или приблизительную оценку из статистики.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.