> Как разделить большую таблицу по студентам для оптимизации запросов (Go)
Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы
Компании: Wildberries
Стек: Go
> Пример ответа
Короткий ответ
Основной подход - горизонтальное партиционирование (sharding) по ключу студента, например по student_id через hash или range. В PostgreSQL/MySQL - это native partitioning, в распределённых системах - sharding на уровне приложения или через middleware. Критично выбрать ключ шардирования так, чтобы данные одного студента всегда попадали в один шард - это сохраняет возможность эффективных запросов по конкретному студенту без cross-shard операций. Также стоит рассмотреть индексы, покрывающие запросы, и архивацию старых данных.
Подробное объяснение
Проблема: большая таблица с данными студентов замедляет запросы из-за роста объёма сканирования, увеличения размера индексов и cache miss.
Варианты оптимизации:
-
Партиционирование по диапазону (range partitioning) - например, по
student_idили дате. Подходит, если есть естественный порядок и запросы часто идут по диапазону. Минус - возможен дисбаланс данных. -
Hash-партиционирование - равномерное распределение по шардам, но теряется возможность range-сканирования по ключу.
-
Sharding на уровне приложения - маршрутизация запросов через lookup-сервис или вычисление шарда по
student_id % N. Требует управления миграцией данных и обработки cross-shard запросов. -
Индексы - композитный индекс
(student_id, ...)покрывает большинство запросов по конкретному студенту. Если запросы всегда фильтруют поstudent_id, это почти всегда решает проблему без партиционирования. -
Архивация - вынос старых данных в отдельную таблицу или хранилище (например, cold storage), оставляя в горячей таблице только актуальные записи.
Ключевой trade-off: партиционирование усложняет операции с данными (JOIN по шардам, транзакции, миграции), поэтому сначала стоит измерить, действительно ли проблема в объёме, а не в отсутствии индексов или плохих запросах.
На практике
Для Go-приложения с PostgreSQL:
- Начни с анализа
EXPLAIN ANALYZE- часто проблема не в размере таблицы, а в seq scan. - Если таблица > 100 млн строк и запросы всегда по
student_id- используй native partitioning поstudent_id(hash или range). - В Go-коде не нужно менять логику - драйвер работает с партиционированной таблицей как с обычной.
- Если используешь sharding на уровне приложения - добавь слой репозитория, который вычисляет шард и выбирает нужное подключение. Для этого удобно использовать пул соединений с маршрутизацией.
- Для миграции данных между шардами используй фоновые задачи с батчами и проверкой консистентности.
Пример кода
Пример маршрутизации шардов в Go:
GOtype ShardRouter struct {shards []*sql.DB}func (r *ShardRouter) GetDB(studentID int64) *sql.DB {idx := studentID % int64(len(r.shards))return r.shards[idx]}func (r *ShardRouter) GetStudent(ctx context.Context, studentID int64) (*Student, error) {db := r.GetDB(studentID)row := db.QueryRowContext(ctx, "SELECT id, name FROM students WHERE id = $1", studentID)// ...}
Пример партиционирования в PostgreSQL:
SQLCREATE TABLE students (id bigint NOT NULL,name text,created_at timestamptz) PARTITION BY HASH (id);CREATE TABLE students_0 PARTITION OF students FOR VALUES WITH (MODULUS 4, REMAINDER 0);CREATE TABLE students_1 PARTITION OF students FOR VALUES WITH (MODULUS 4, REMAINDER 1);-- ...
Как отвечать на собеседовании
Начни с уточнения: какой объём данных, какие запросы тормозят, есть ли уже индексы. Это покажет, что ты не предлагаешь решение вслепую. Затем опиши иерархию решений: сначала индексы и оптимизация запросов, потом партиционирование, потом шардирование. Объясни trade-off каждого подхода. Приведи конкретный пример с ключом шардирования и объясни, почему student_id - хороший выбор. Упомяни, что в Go-приложении важно не смешивать логику шардирования с бизнес-логикой - вынеси её в отдельный слой.
Что проверяет интервьюер
- Понимание разницы между партиционированием и шардированием.
- Умение выбирать ключ шардирования с учётом паттернов доступа.
- Знание trade-off: сложность транзакций, JOIN, миграции.
- Практический опыт: как измерить проблему, когда хватает индекса.
- Понимание влияния на Go-код: маршрутизация, пулы соединений, обработка ошибок.
Типичные ошибки
- Предлагать шардирование сразу, без анализа индексов и планов запросов.
- Выбирать ключ шардирования не по паттерну запросов, а по удобству (например, по
idвместоstudent_id). - Игнорировать проблему cross-shard запросов и транзакций.
- Не учитывать перебалансировку при добавлении шардов - hash-модуль ломается при изменении числа шардов.
- Забывать про архивацию и retention policy - часто это дешевле и проще, чем шардирование.
> Похожие задачи по Go
Кто пишет миграции в проекте
Как масштабировать Kafka при высокой нагрузке
Какие джойны и группировки используются для подсчета количества людей по адресам
Какие типы записей существуют в DNS
> Похожие задачи по backend
Кто пишет миграции в проекте
Как масштабировать Kafka при высокой нагрузке
Какие джойны и группировки используются для подсчета количества людей по адресам
Какие типы записей существуют в DNS
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью