Вопрос проверяет умение проектировать распределение данных по шардам так, чтобы избежать перекосов нагрузки и упростить масштабирование.
Ключ шардирования выбирают так, чтобы данные распределялись равномерно и запросы чаще попадали в один шард. Обычно берут поле с высокой уникальностью (например, user_id) и стабильной семантикой. Важно избегать ключей, которые создают “горячие” шарды, например дату или монотонно растущий идентификатор без хеширования. Также учитывают, как будут выполняться JOIN и выборки по основным фильтрам.
Шардирование — это разбиение данных на несколько независимых частей (shards), чтобы масштабировать хранение и нагрузку. Ключ шардирования определяет, в какой шард попадет запись и куда пойдет запрос.
Сначала формулируют требования к ключу, иначе шардирование быстро превращается в постоянную “борьбу с перекосами”.
Ключ должен давать близкий к равномерному распределению:
высокая кардинальность (много разных значений)
отсутствие сильной корреляции с временем/нагрузкой
отсутствие “топовых” значений, на которые приходится большинство операций
Пример плохого ключа:
created_at (все новые записи идут в один “хвост”)
country (несколько больших стран перегружают отдельные шарды)
Идеально, когда большинство запросов можно обслужить одним шардом:
запросы по user_id → шардирование по user_id
запросы по tenant_id (multi-tenant) → шардирование по tenant_id
Если запросы регулярно требуют данных “со всех шардов”, вы получите fan-out:
растет латентность
растут расходы на сеть
усложняется отказоустойчивость
Hotspot — это шард, который постоянно “горячий” из-за перекоса в данных или трафике.
Типовые причины:
монотонно растущий ключ без хеширования
небольшое множество популярных значений
“топовые” пользователи/тенанты, генерирующие основной трафик
Частая техника для борьбы:
использовать hash(user_id) вместо “сырого” user_id
или консистентное хеширование на уровне роутинга
Ключ должен редко меняться. Если значение ключа меняется — запись нужно “перевезти” в другой шард, а это:
дорогая операция
требует согласованности
усложняет транзакции и ссылки
Поэтому, например, email как ключ часто плох, а user_id — хорош.
Шардирование меняет жизнь JOIN и транзакций.
Если у вас есть частые связи:
orders ↔ users
payments ↔ orders
то разумно выбирать ключ так, чтобы связанные сущности попадали в один шард. Например:
orders шардинг по user_id
payments шардинг по тому же user_id или по order_id, если order_id однозначно привязан к user_id и роутинг это знает
Подходит, если:
большинство запросов “вокруг пользователя”
нужен хороший баланс
удобна локальность
Пример роутинга:
shard = hash(user_id) % N
Подходит, если:
система multi-tenant
важна изоляция крупных клиентов
возможны отдельные политики на тенанта
Минус: “крупный тенант” может перегреть один шард, тогда приходится делать “тенант внутри тенанта” или отдельные шарды для крупных.
Подходит для логов/метрик, но почти всегда требует:
партиционирования внутри шарда
аккуратной стратегии на “горячий хвост”
возможного split горячих диапазонов
Допустим, основной паттерн: “показать профиль и последние заказы пользователя”.
Тогда логично:
users шардировать по user_id
orders шардировать по user_id
читать данные без fan-out
Пример (упрощенно):
GET /users/42
GET /users/42/orders?limit=20
Оба запроса идут в один и тот же шард.
Ключ шардирования выбирают так, чтобы обеспечить баланс (без hotspot), локальность большинства запросов (минимум fan-out), стабильность (редко меняется) и разумную модель для связей/транзакций. На практике чаще всего выигрывает user_id или tenant_id с хешированием.