Представьте, что вы проектируете интернет-магазин с тысячами товаров. Показывать их все сразу - плохая идея: страница будет грузиться очень медленно и это может увеличить число отказов.
Здесь на помощь приходит пагинация - способ разбить большой объём данных на равные «порции».
Благодаря пагинации мы можем подгружать контент частями: например, по 20 товаров за раз или по страницам результатов поиска. Это не только ускоряет загрузку, но и делает интерфейс чище и отзывчивее.
Но непосредственная реализация пагинации может так же влиять на производительность и консистентность данных.
Существует два основных подхода к реализации пагинации: offset и cursor.
🟣Offset пагинация
Классический способ, основанный на смещении. Это способ выдачи данных, при котором мы работаем с двумя параметрами: limit (количество элементов на странице) и offset (отступ).
limit: number; // Количество элементов на странице
offset: number; // Смещение (сколько записей пропустить)
Из плюсов:
- Удобно для произвольной навигации: можно легко получить данные по конкретной странице;
- Легко интерпретируется на бекенда: offset вычисляется как
(page - 1) * limit;- Простая реализация (особенно в SQL через LIMIT ... OFFSET);
Из минусов:
- С ростом offset запросы замедляются, так как СУБД должна пройти все записи до нужной позиции. В больших таблицах будет работать медленно (O(n));
- Могут дублироваться записи в ленте для часто обновляемых данных (например, соц. сетей).
Мы посмотрели 5 постов, но за время пока мы их смотрели появилось еще 5 записей в БД. Мы грузим следующую пачку данных, и у нас происходит отступ и мы видим опять те же 5 постов, которые только что посмотрели.
Поправить это можно только дополнительными махинациями на фронте. например дедупликацией данных, где будем отфильтровывать записи перед добавлением в ленту.
🟣Cursor пагинация
Cursor пагинация в свою очередь работает с параметрами size (количество элементов на странице) и cursor (указатель на id, timestamp или другой специфичный параметр), который указывает от какого элемента получать следующую порцию данных.
size: number; // Количество элементов на странице
cursor: string | number; // Указатель на id или timestamp
Из плюсов:
- Нет проблем с замедлением запроса при больших отступах (O(1));
- Нет проблем с дублированием записей. Идеально подходит для часто меняющихся данных, например, лент соц. сетей и infinite scroll;
Из минусов:
- Нет возможности перейти на конкретную страницу - только листать вперед/назад;
- Реализация сложнее, особенно если нужны сортировки по нескольким полям;
- Возможны коллизии, когда пропускаются некоторые записи, если использовать указатель по timestamp.
Последнюю проблему можно решить используя гибридный указатель, например по id + timestamp.
🟣Для системы каталога в интернет магазине нужно использовать offset-пагинацию потому что:
- Нам нужно уметь навигироваться и переходить на конкретную страницу в каталоге
- В результатах по товарам проблема «устаревших» результатов не так критична: новые товары добавляются не очень быстро и не слишком часто.
- Нам нужно знать общее количество результатов, а при offset‑пагинации это проще реализовать
В целом для финального выбора можно опираться на следующий принцип:
Если нужны переходы на конкретные страницы и данные мало меняются - можно спокойно использовать offset.
Если же у вас длинные ленты, большой объём данных и частые обновления - разумнее сразу закладываться на cursor, чтобы не упереться в лаги и дубли в проде.
✈️ Telegram | 🎓 Менторство | 📹YouTube | 👩💻 Roadmap
