Пост написан по мотивам разбора Database Sharding Explained (architecturenotes.co). Шардирование — классическая тема, поэтому текст и диаграммы здесь мои собственные; за развёрнутым первоисточником и иллюстрациями — по ссылке.
Рано или поздно одна база данных упирается в потолок: данных больше, чем влезает на диск одного сервера; запись идёт быстрее, чем один узел успевает переваривать; коннекты и блокировки начинают мешать друг другу. Первое, что делают (и правильно) — добавляют индексы, реплики для чтения, кэш, вертикально наращивают железо. Но всё это не решает одну проблему: запись и объём всё ещё упираются в один узел. Вот тут появляется шардирование.
Что такое шардирование
Шардирование (sharding) — это горизонтальное разбиение одного логического набора данных на несколько независимых баз (шардов), каждая из которых живёт на своём узле и хранит свою часть данных.
Ключевое слово — «свою часть». Не копию, а именно кусок. Пользователи с 1 по 1 000 000 — на первом шарде, с 1 000 001 по 2 000 000 — на втором, и так далее. Каждый шард — полноценная БД, которая ничего не знает про остальные.
Чем это НЕ является
Три вещи часто путают:
- Репликация — это копии одних и тех же данных на нескольких узлах (для отказоустойчивости и масштабирования чтения). Шардирование — это разные данные на разных узлах (для масштабирования записи и объёма). Они не взаимоисключающие: обычно каждый шард ещё и реплицируется.
- Вертикальное партиционирование — разбиение по столбцам (редко используемые поля выносят в отдельную таблицу). Шардирование — это горизонтальное разбиение по строкам.
- Партиционирование внутри одной БД (например,
PARTITION BYв PostgreSQL) — данные бьются на куски, но живут в одном инстансе. Шардирование выносит куски на разные серверы.
На практике шардирование и репликация работают вместе: данные бьются на шарды, а каждый шард ещё и реплицируется.
Shard key — сердце всей затеи
Shard key (ключ шардирования) — это поле (или набор полей), по которому принимается решение, на каком шарде живёт запись. От выбора shard key зависит вообще всё: равномерность нагрузки, скорость запросов, боль будущего решардинга.
Хороший shard key:
- высококардинальный — много разных значений, чтобы данные дробились мелко (user_id хорошо, «страна» плохо);
- равномерно распределённый — чтобы ни один шард не стал «горячим»;
- присутствует в большинстве запросов — иначе придётся опрашивать все шарды сразу.
Стратегии распределения
Range-based (по диапазону)
Данные делятся по диапазонам значений ключа: A–F на первый шард, G–M на второй и т.д.
- ➕ Простой и понятный; диапазонные запросы (
WHERE id BETWEEN …) бьют в один шард. - ➖ Легко получить перекос: если новые пользователи всё время попадают в «последний» диапазон, этот шард раскаляется, а остальные простаивают.
Hash-based (по хешу)
К ключу применяют хеш-функцию, и остаток от деления определяет шард: hash(user_id) % N.
- ➕ Отличная равномерность — данные размазываются ровным слоем.
- ➖ Диапазонные запросы становятся дорогими (данные разбросаны по всем шардам). И главная боль — при изменении числа шардов
% Nпереназначает почти всё, то есть при добавлении узла нужно перетасовать почти все данные.
Directory-based (справочник)
Отдельный сервис-справочник хранит явную таблицу «ключ → шард».
- ➕ Максимальная гибкость: можно перемещать данные и менять раскладку, не трогая формулу.
- ➖ Справочник становится единой точкой отказа и лишним хопом на каждом запросе; его самого надо реплицировать и кэшировать.
Реальные проблемы (то, о чём молчат в туториалах)
Шардирование не бесплатно. За масштабируемость платят сложностью:
- Hotspots (горячие шарды). Неудачный shard key → один узел получает непропорциональную нагрузку. Классика — шардировать по времени: «сегодняшний» шард всегда горит.
- Решардинг. Добавить узел в шардированную систему — это не «докинуть реплику», а перераспределить данные вживую, не роняя прод. С наивным
% Nэто переезд почти всего датасета. - Кросс-шардовые запросы и JOIN. Запрос, которому нужны данные с нескольких шардов, приходится разбивать, рассылать веером (scatter-gather) и агрегировать в приложении. JOIN между шардами по сути невозможен на уровне БД.
- Распределённые транзакции. ACID-транзакция в пределах одного шарда — легко. Транзакция через несколько шардов — это уже двухфазный коммит или сага, со всей их сложностью.
- Уникальные идентификаторы. Автоинкремент на каждом шарде выдаст пересекающиеся ID. Нужны глобально уникальные ID (UUID, Snowflake-подобные схемы).
- Операционная сложность. Бэкапы, миграции схемы, мониторинг, восстановление — всё умножается на число шардов.
Как смягчают
- Consistent hashing вместо
% N— при добавлении/удалении узла переезжает лишь малая доля ключей, а не всё. - Виртуальные шарды (buckets). Данные раскладывают в много (например, 1024) логических корзин, а корзины уже мапят на физические узлы. Масштабирование = перенос корзин, а не пересчёт ключей.
- Глобальная генерация ID (Snowflake: время + id узла + счётчик) — уникальность без общего автоинкремента.
Когда шардировать НЕ надо
Честно: шардирование — это то, что откладывают до последнего, потому что оно необратимо усложняет систему. Сначала стоит выжать всё из более дешёвых средств:
- Индексы и оптимизация запросов — часто «медленная БД» лечится одним индексом.
- Реплики для чтения — если упор в чтение, а не в запись.
- Кэш (Redis/Valkey) перед БД — снимает основную массу повторных чтений.
- Вертикальное масштабирование — больше CPU/RAM/NVMe; одна мощная нода тянет очень много.
- Партиционирование в пределах одной БД — если проблема в размере таблиц, а не всего инстанса.
Шардирование оправдано, когда запись или объём реально не помещаются в один узел и всё вышеперечисленное исчерпано. Если можете не шардировать — не шардируйте.
Итог
Шардирование решает ровно одну задачу — масштабирование записи и объёма за пределы одного сервера — и делает это ценой серьёзной сложности: выбор shard key, кросс-шардовые запросы, решардинг, распределённые транзакции. Правильный порядок действий: индексы → реплики → кэш → вертикаль → партиционирование → и только потом шарды. А когда дошло до шардов — 80% успеха закладывается на этапе выбора shard key, так что это решение стоит вашего самого внимательного часа.