Самая главная проблема шардирования — как приложению понять, на каком именно сервере лежит нужный пользователь? Для этого используют ключ шардирования (Shard Key) и один из трех алгоритмов.
1. Range-based Sharding (По диапазонам)
Данные разбиваются по логическим диапазонам значений ключа. Например, пользователи с ID от 1 до 1М идут на первый шард, от 1М до 2М — на второй.
- Плюсы: Отлично подходит для range-запросов (например, «достать все заказы за апрель», если ключ — это дата). Легко добавлять новые шарды для новых диапазонов.
- Минусы: Огромный риск появления горячих шардов. Если мы шардируем по дате, то шард «Текущий месяц» будет перегружен, а старые шарды будут простаивать.
2. Hash-based Sharding (Хеширование)
Мы применяем хеш-функцию к ключу (например, UserID) и делим по модулю на количество серверов. .shard_number = hash(user_id) % total_shards
- Плюсы: Идеально равномерное распределение данных. Нет перегруженных серверов, так как хеш размазывает нагрузку.
- Минусы: Нельзя делать range-запросы. Но самая главная боль — добавление нового сервера. Если
меняется с 3 на 4, хеш-функция выдаст новые результаты для всех пользователей. Придется переносить 90% базы данных. (Эту проблему решает алгоритм Consistent Hashing).total_shards
Подробнее про консистентное хеширование можешь почитать в этой статье
3. Directory-based Sharding (Lookup Table)
Используется отдельная маленькая база данных (таблица маршрутизации), в которой жестко прописано сопоставление: «User A живет на сервере 3».
- Плюсы: Максимальная гибкость. Можно переносить конкретных тяжелых юзеров на выделенные сервера без изменения общей логики приложения.
- Минусы: Сама таблица маршрутизации (Lookup Table) становится узким местом и точкой отказа (Single Point of Failure). Приложению приходится делать два сетевых прыжка вместо одного.
Индустриальный стандарт: Virtual Buckets.
В современном BigTech данные почти никогда не мапят напрямую на физический сервер. Сначала данные хешируют в большое количество логических шардов (buckets) (например, 4096 бакетов). А уже эти бакеты таблицей маршрутизации распределяют по физическим серверам. Это позволяет легко перекидывать бакеты между серверами при масштабировании.