> Как реализовать эффективную пагинацию с курсорами и индексами (Go)
Уровень: senior · Роль: backend · Категория: Технические вопросы
Компании: sferaplatform.ru
Стек: Go
> Пример ответа
Для эффективной пагинации с курсорами в Go используйте комбинацию индекса B-tree и курсора на основе уникального сортируемого поля (например, id или created_at). Вместо OFFSET, который сканирует пропущенные строки, применяйте условие WHERE cursor > last_seen_value ORDER BY cursor LIMIT n. Это позволяет базе данных использовать индекс для прямого доступа к следующей порции данных.
Пример реализации:
GOtype CursorPaginator struct {db *sql.DB}type PageParams struct {Cursor string // последнее значение курсора (например, ID)Limit int}type PageResult struct {Items []ItemNextCursor stringHasMore bool}func (p *CursorPaginator) GetItems(params PageParams) (*PageResult, error) {query := `SELECT id, name, created_atFROM itemsWHERE id > $1ORDER BY id ASCLIMIT $2`rows, err := p.db.Query(query, params.Cursor, params.Limit+1)if err != nil {return nil, err}defer rows.Close()var items []Itemfor rows.Next() {var item Itemif err := rows.Scan(&item.ID, &item.Name, &item.CreatedAt); err != nil {return nil, err}items = append(items, item)}hasMore := len(items) > params.Limitif hasMore {items = items[:params.Limit]}nextCursor := ""if len(items) > 0 {nextCursor = items[len(items)-1].ID}return &PageResult{Items: items,NextCursor: nextCursor,HasMore: hasMore,}, nil}
Ключевые моменты:
- Индекс: обязательно создайте составной индекс на поле сортировки (
idилиcreated_at), чтобы запрос выполнялся за O(log n). - Курсор: используйте уникальное поле (например, UUID или автоинкрементный ID). Для временных меток добавляйте вторичный ключ для разрешения коллизий.
- Limit+1: запрашивайте на одну запись больше, чтобы определить наличие следующей страницы без дополнительного запроса.
- Безопасность: курсор должен быть закодирован (например, base64) для передачи в URL, но в БД храните сырое значение.
Такой подход обеспечивает O(1) сложность для каждой страницы при наличии индекса, в отличие от O(n) у OFFSET.
> Похожие задачи по backend
Как бороться с большими объемами данных в запросах, например шардирование
Как устроена пагинация в Go проектах
Напиши функцию на Go, которая принимает слайс каналов int и возвращает канал с суммами чисел из каждого канала
Как гарантировать, что заказ не будет выполнен без исполнителя после резерва товара
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью