День 2276. #ЗаметкиНаПолях
Разбираем Курсорную Пагинацию. Начало
Пагинация (разбиение на страницы) имеет решающее значение для эффективной доставки больших наборов данных. Хотя офсетная пагинация (offset pagination) широко используется, курсорная пагинация (cursor pagination) предлагает некоторые интересные преимущества для определённых сценариев. Она особенно ценна для каналов в реальном времени, интерфейсов бесконечной прокрутки и API, где производительность имеет значение в масштабе, например, для журналов активности или потоков событий, где пользователи часто просматривают большие наборы данных. Рассмотрим детали реализации и обсудим, где каждый подход имеет наибольший смысл.
Создадим простое хранилище записей пользователя с помощью следующего объекта:
public record UserNote (
Guid Id,
Guid UserId,
string? Note,
DateOnly Date
);
Традиционный подход: Офсетная пагинация
Офсетная пагинация использует Skip и Take. Мы пропускаем определённое количество строк и берём фиксированное количество строк. Обычно они преобразуются в OFFSET и LIMIT в запросах SQL:
var query = dbContext.UserNotes
.OrderByDescending(x => x.Date)
.ThenByDescending(x => x.Id);
// При офсетной пагинации мы обычно сначала считаем общее количество элементов
var total = await query
.CountAsync(cancellationToken);
var pages = (int)Math.Ceiling(total / (double)pageSize);
var query = dbContext.UserNotes
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync(cancellationToken);
Заметьте, что мы сортируем результаты по дате и Id в обратном порядке. Это обеспечивает единообразие результатов при разбиении на страницы. Вот сгенерированный SQL (в PostgreSql) для офсетной пагинации:
// запрос общего количества
SELECT count(*)::int FROM user_notes AS u;
// запрос данных
SELECT u.id, u.date, u.note, u.user_id
FROM user_notes AS u
ORDER BY u.date DESC, u.id DESC
LIMIT @pageSize OFFSET @offset;
Ограничения офсетной пагинации:
- Производительность ухудшается по мере увеличения смещения, поскольку база данных должна сканировать и отбрасывать все строки до параметра OFFSET;
- Риск потери или дублирования элементов при изменении данных между страницами;
- Несогласованные результаты при одновременных обновлениях.
Продолжение следует…
Источник: https://www.milanjovanovic.tech/blog/understanding-cursor-pagination-and-why-its-so-fast-deep-dive
>>Click here to continue<<