← Назад в словарь

Шардинг

Шардинг (Sharding) — разбиение на фрагменты, осколки

TL;DR

Шардинг — горизонтальное разбиение данных на независимые части (шарды), распределённые по разным узлам базы данных. Позволяет масштабироваться горизонтально, но усложняет запросы и транзакции между шардами.

Краткое определение

Шардинг — метод горизонтального масштабирования базы данных, при котором данные разделяются на независимые части (шарды) по определённому ключу, и каждый шард размещается на отдельном сервере.

Оригинал и перевод

  • Язык: английский
  • Оригинал: Sharding (от shard — осколок, фрагмент)
  • Буквальный перевод: разбиение на фрагменты, осколки
  • Русские аналоги: шардирование, горизонтальное разбиение, секционирование

Синонимы и варианты написания

  • Шардирование
  • Горизонтальное партиционирование
  • Database sharding
  • Horizontal partitioning

Где используется

  • Крупные БД: события, логи, пользовательские данные (миллиарды записей)
  • Высоконагруженные OLTP-системы: социальные сети, мессенджеры, e-commerce
  • Распределённые СУБД: MongoDB, Cassandra, CockroachDB
  • SaaS платформы: данные клиентов по шардам (tenant-per-shard)

Когда это важно

Шардинг критичен при следующих условиях:

  • Объём данных: таблица не помещается на один сервер (100GB+)
  • Нагрузка на запись: один сервер не справляется с количеством INSERT/UPDATE
  • Географическое распределение: данные пользователей в разных регионах
  • Мультитенантность: изоляция данных клиентов по шардам

Как работает шардинг

Принцип

Данные → Выбор шард-ключа → Маршрутизация → Шард 1, 2, 3...

Пример:
user_id = 123 → hash(123) % 3 = 0 → Шард 1
user_id = 456 → hash(456) % 3 = 1 → Шард 2
user_id = 789 → hash(789) % 3 = 2 → Шард 3

Стратегии шардирования

1. Шардирование по диапазону (Range-based)

Данные распределяются по диапазонам ключа:

Шард 1: user_id 1-100000
Шард 2: user_id 100001-200000
Шард 3: user_id 200001-300000

Плюсы:

  • Простая маршрутизация
  • Легко добавить новый шард

Минусы:

  • Неравномерное распределение (hot spots)
  • Сложно изменить диапазоны

2. Хэш-шардирование (Hash-based)

Ключ хэшируется и распределяется по шардам:

shard_id = hash(user_id) % N

hash(123) = 456789 → 456789 % 3 = 0 → Шард 1
hash(456) = 789012 → 789012 % 3 = 1 → Шард 2

Плюсы:

  • Равномерное распределение
  • Нет hot spots

Минусы:

  • Сложно изменить количество шардов (пересчёт всех ключей)
  • Нельзя делать range-запросы между шардами

3. Шардирование по списку (List-based)

Явное указание, какие значения к какому шарду относятся:

Шард 1: Россия, Беларусь, Казахстан
Шард 2: США, Канада
Шард 3: Европа

Плюсы:

  • Гибкое управление
  • Легко добавить новый шард

Минусы:

  • Ручное управление маппингом
  • Неравномерное распределение

4. Гео-шардирование (Geo-based)

Данные распределяются по географическому признаку:

Шард 1 (Москва): пользователи из RU
Шард 2 (Франкфурт): пользователи из EU
Шард 3 (Нью-Йорк): пользователи из US

Плюсы:

  • Данные ближе к пользователям (меньше задержка)
  • Соответствие регуляториям (GDPR, 152-ФЗ)

Минусы:

  • Сложная маршрутизация
  • Глобальные запросы между шардами

Выбор shard-ключа

Хороший shard-ключ

  • Высокая кардинальность: много уникальных значений
  • Равномерное распределение: нет перекоса по шардам
  • Частое использование в WHERE: запросы идут сразу к нужному шарду
  • Стабильность: ключ не меняется со временем

Примеры:

  • user_id — для пользовательских данных
  • tenant_id — для SaaS (мультитенантность)
  • country_code — для гео-шардирования
  • created_at (по диапазону дат) — для временных рядов

Плохой shard-ключ

  • Низкая кардинальность: is_active, gender
  • Неравномерное распределение: created_at (все новые записи в один шард)
  • Редко используется в запросах: данные придётся искать по всем шардам

Проблемы шардирования

1. Кросс-шардовые запросы

JOIN между шардами невозможен или очень дорог:

-- ❌ Нельзя выполнить напрямую
SELECT u.*, o.*
FROM users u  -- Шард 1
JOIN orders o -- Шард 2
  ON u.id = o.user_id;

-- ✅ Решение: делать в приложении
$users = $shard1->query("SELECT * FROM users WHERE ...");
$userIds = array_column($users, 'id');
$orders = $shard2->query("SELECT * FROM orders WHERE user_id IN (...)");

2. Транзакции между шардами

ACID транзакции невозможны без 2PC (two-phase commit):

-- ❌ Нельзя атомарно выполнить
BEGIN;
UPDATE shard1.users SET balance = balance - 100 WHERE id = 1;
UPDATE shard2.users SET balance = balance + 100 WHERE id = 2;
COMMIT;

-- ✅ Решение: eventual consistency, сага-паттерн

3. Ребалансировка

При изменении количества шардов нужно перераспределить данные:

Было: 3 шарда → Стало: 4 шарда
Перемещение 25% данных на новый шард

Решение:

  • Consistent hashing (минимизирует перемещение)
  • Виртуальные шарды (легко перераспределять между физическими узлами)

4. Глобальные данные

Некоторые данные нужны на всех шардах:

Справочники: страны, валюты, настройки
Пользователи: некоторые пользователи нужны на всех шардах

Решение:

  • Репликация глобальных данных на все шарды
  • Отдельный шард для глобальных данных

Шардинг vs Партиционирование

ПараметрПартиционированиеШардинг
УровеньВнутри одного инстанса БДРаспределение по разным узлам
МасштабированиеВертикальное (больше ресурсов)Горизонтальное (больше серверов)
СложностьПроще (встроенная функция БД)Сложнее (приложение управляет)
ПримерPostgreSQL table partitioningMongoDB sharded cluster

Реализация шардирования

MongoDB (встроенный шардинг)

// Включение шардирования для БД
sh.enableSharding("mydb");

// Шардирование коллекции по ключу
sh.shardCollection("mydb.users", { user_id: "hashed" });

// Добавление шарда
sh.addShard("rs1/10.0.0.1:27017");

PostgreSQL (ручное шардирование)

-- Партиционирование таблицы (внутри одного инстанса)
CREATE TABLE users (
    id INT,
    name TEXT
) PARTITION BY HASH (id);

CREATE TABLE users_0 PARTITION OF users
    FOR VALUES WITH (modulus 3, remainder 0);
CREATE TABLE users_1 PARTITION OF users
    FOR VALUES WITH (modulus 3, remainder 1);
CREATE TABLE users_2 PARTITION OF users
    FOR VALUES WITH (modulus 3, remainder 2);

Приложение (PHP + Redis для маршрутизации)

class ShardRouter {
    private array $shards = [
        0 => 'mysql://shard1.example.com/mydb',
        1 => 'mysql://shard2.example.com/mydb',
        2 => 'mysql://shard3.example.com/mydb',
    ];
    
    public function getShard(int $userId): PDO {
        $shardId = $userId % 3;  // hash-based
        $dsn = $this->shards[$shardId];
        
        return new PDO($dsn);
    }
}

// Использование
$router = new ShardRouter();
$shard = $router->getShard(123);  // Шард 1
$user = $shard->query("SELECT * FROM users WHERE id = 123");

Аналоги и связанные термины

  • Partitioning — партиционирование (внутри одного инстанса)
  • Consistent hashing — хэширование для минимизации перемещения данных
  • Distributed SQL — распределённые СУБД (CockroachDB, Spanner)
  • Rebalancing — перераспределение данных между шардами
  • Replication — репликация (вертикальное масштабирование чтения)

Смотри также (статьи на сайте)

Смотри также (сниппеты)

Смотри также (термины)

  • Replication — репликация данных
  • ACID — атомарность транзакций
  • Partitioning — партиционирование
  • Consistent hashing — хэширование для шардирования

Мини-FAQ

Шардинг и партиционирование — одно и то же?

Ответ: Нет. Партиционирование — внутри одного инстанса БД (таблица делится на части). Шардинг — распределение по разным серверам. Шардинг сложнее, но масштабируется лучше.

Когда начинать шардирование?

Ответ: Когда исчерпаны другие методы:

  1. ✅ Индексы и оптимизация запросов
  2. ✅ Репликация для чтения
  3. ✅ Партиционирование таблиц
  4. ✅ Вертикальное масштабирование (больше CPU/RAM)
  5. ⚠️ Шардинг — когда всё выше не помогает

Сколько шардов делать?

Ответ: Начните с 3-5 шардов. Лучше иметь запас, чем добавлять шарды слишком часто. Но не делайте 100+ шардов — усложнится управление.

Можно ли откатить шардирование?

Ответ: Технически да, но сложно и дорого. Шардирование — это архитектурное решение, которое принимается надолго. Тщательно тестируйте перед production.