12. MongoDB: Sharding
Sharding — это метод горизонтального масштабирования, при котором данные распределяются по нескольким серверам (shards). Это позволяет обрабатывать терабайты данных и миллионы операций в секунду.
Архитектура Sharded Cluster
Заголовок раздела «Архитектура Sharded Cluster»graph TD A[Application] --> B[Mongos - Router] B --> C[Config Servers] B --> D[Shard 1] B --> E[Shard 2] B --> F[Shard 3]
C --> C1[Метаданные о chunks] D --> D1[Subset данных] E --> E1[Subset данных] F --> F1[Subset данных]Компоненты:
- Mongos: Роутер запросов, выбирает нужные shard’ы
- Config Servers: Хранят метаданные о распределении данных
- Shards: Хранят актуальные данные (обычно replica sets)
Зачем нужен Sharding?
Заголовок раздела «Зачем нужен Sharding?»✅ Когда использовать:
- Размер данных превышает ресурсы одного сервера (>2TB)
- Пропускная способность (queries/sec) упирается в лимиты сервера
- Рабочий набор данных не помещается в RAM
❌ Когда НЕ нужен:
- Данные
<100GB (достаточно вертикального масштабирования) - Большинство запросов не используют shard key
- Слабая инфраструктура (нужно минимум 3 config + 2 shards + mongos)
Shard Key
Заголовок раздела «Shard Key»Shard key — это поле (или набор полей), по которому данные распределяются между shards.
Выбор Shard Key
Заголовок раздела «Выбор Shard Key»// ❌ Плохой shard key: монотонно растущий _idsh.shardCollection("mydb.users", { _id: 1 })// Проблема: все новые документы попадают в один shard (hotspot)
// ✅ Хороший shard key: hashed _idsh.shardCollection("mydb.users", { _id: "hashed" })// Равномерное распределение
// ✅ Compound shard key: country + userIdsh.shardCollection("mydb.users", { country: 1, userId: 1 })// Можно эффективно запрашивать по countryКритерии хорошего shard key:
- Cardinality (уникальность): Много разных значений
- Write Distribution: Запись равномерно по shards
- Query Isolation: Запросы попадают в минимум shards
- Monotonicity: Не монотонно растущий
Примеры Shard Keys
Заголовок раздела «Примеры Shard Keys»// Для логов: хеш timestamp + userIdsh.shardCollection("logs.events", { timestamp: "hashed", userId: 1})
// Для e-commerce: country + customerIdsh.shardCollection("shop.orders", { country: 1, customerId: 1})
// Для IoT: deviceIdsh.shardCollection("iot.sensors", { deviceId: "hashed"})
// Для multitenancy: tenantId + createdAtsh.shardCollection("saas.data", { tenantId: 1, createdAt: 1})Настройка Sharded Cluster
Заголовок раздела «Настройка Sharded Cluster»1. Запуск Config Servers
Заголовок раздела «1. Запуск Config Servers»# 3 config servers (replica set)mongod --configsvr --replSet configRS --port 27019 --dbpath /data/config1mongod --configsvr --replSet configRS --port 27020 --dbpath /data/config2mongod --configsvr --replSet configRS --port 27021 --dbpath /data/config3
# Инициализацияmongosh --port 27019rs.initiate({ _id: "configRS", configsvr: true, members: [ { _id: 0, host: "localhost:27019" }, { _id: 1, host: "localhost:27020" }, { _id: 2, host: "localhost:27021" } ]})2. Запуск Shards
Заголовок раздела «2. Запуск Shards»# Shard 1 (replica set)mongod --shardsvr --replSet shard1RS --port 27017 --dbpath /data/shard1# ... остальные члены replica set
# Shard 2 (replica set)mongod --shardsvr --replSet shard2RS --port 27018 --dbpath /data/shard2# ... остальные члены replica set3. Запуск Mongos
Заголовок раздела «3. Запуск Mongos»mongos --configdb configRS/localhost:27019,localhost:27020,localhost:27021 --port 270164. Добавление Shards
Заголовок раздела «4. Добавление Shards»// Подключение к mongosmongosh --port 27016
// Добавление shardssh.addShard("shard1RS/localhost:27017")sh.addShard("shard2RS/localhost:27018")
// Проверка статусаsh.status()5. Включение Sharding для БД и коллекции
Заголовок раздела «5. Включение Sharding для БД и коллекции»// Включить sharding для БДsh.enableSharding("mydb")
// Создать индекс на shard keydb.users.createIndex({ userId: "hashed" })
// Shard коллекциюsh.shardCollection("mydb.users", { userId: "hashed" })Chunks и Balancing
Заголовок раздела «Chunks и Balancing»MongoDB разбивает данные на chunks (по умолчанию 64MB). Балансер автоматически перемещает chunks между shards.
// Просмотр chunksuse configdb.chunks.find({ ns: "mydb.users" }).pretty()
// Статистика по shardsdb.users.getShardDistribution()
// Настройки балансировкиsh.setBalancerState(true) // включитьsh.setBalancerState(false) // выключить
// Балансировка только ночьюuse configdb.settings.update( { _id: "balancer" }, { $set: { activeWindow: { start: "23:00", stop: "06:00" } } }, { upsert: true })
// Размер chunk (default 64MB)use configdb.settings.save({ _id: "chunksize", value: 128 }) // 128MBЗапросы в Sharded Cluster
Заголовок раздела «Запросы в Sharded Cluster»Targeted Query (эффективно)
Заголовок раздела «Targeted Query (эффективно)»// Запрос использует shard key → попадает в 1 sharddb.users.find({ userId: 12345 })Broadcast Query (неэффективно)
Заголовок раздела «Broadcast Query (неэффективно)»// Запрос БЕЗ shard key → mongos опрашивает ВСЕ shardsAggregate с Sharding
Заголовок раздела «Aggregate с Sharding»// Pipeline разбивается: часть на shards, часть на mongosdb.orders.aggregate([ { $match: { status: "completed" } }, // на shards { $group: { _id: "$userId", total: { $sum: "$amount" } } }, // на shards { $sort: { total: -1 } }, // на mongos (merge) { $limit: 10 } // на mongos])TypeScript примеры
Заголовок раздела «TypeScript примеры»import { MongoClient } from 'mongodb';
// Подключение к mongosconst client = new MongoClient('mongodb://localhost:27016');
async function shardingExamples() { await client.connect(); const db = client.db('mydb'); const admin = client.db('admin');
// Включить sharding для БД await admin.command({ enableSharding: 'mydb' });
// Создать индекс и shard коллекцию await db.collection('users').createIndex({ userId: 'hashed' }); await admin.command({ shardCollection: 'mydb.users', key: { userId: 'hashed' } });
// Статистика const shardStats = await db.command({ dbStats: 1 }); console.log('Database stats:', shardStats);
// Распределение коллекции по shards const collStats = await db.collection('users').stats(); console.log('Collection stats:', collStats.sharded, collStats.shards);
await client.close();}
// Мониторинг балансировкиasync function monitorBalancing() { await client.connect(); const config = client.db('config');
// Chunks по shards const chunks = await config.collection('chunks') .aggregate([ { $match: { ns: 'mydb.users' } }, { $group: { _id: '$shard', count: { $sum: 1 } } } ]) .toArray();
console.log('Chunks per shard:', chunks);
// История балансировки const balancerHistory = await config.collection('changelog') .find({ what: 'moveChunk.commit' }) .sort({ time: -1 }) .limit(10) .toArray();
console.log('Recent balancing:', balancerHistory);
await client.close();}Zones (Tag-aware Sharding)
Заголовок раздела «Zones (Tag-aware Sharding)»Zones позволяют привязать диапазоны данных к конкретным shards.
// Создать зоны для geo-distributionsh.addShardTag("shard1RS", "US")sh.addShardTag("shard2RS", "EU")sh.addShardTag("shard3RS", "ASIA")
// Назначить диапазоныsh.addTagRange( "mydb.users", { country: "US", userId: MinKey }, { country: "US", userId: MaxKey }, "US")
sh.addTagRange( "mydb.users", { country: "DE", userId: MinKey }, { country: "DE", userId: MaxKey }, "EU")
// Теперь данные пользователей из US останутся в shard1RSРезервное копирование Sharded Cluster
Заголовок раздела «Резервное копирование Sharded Cluster»# Остановить балансировкуmongosh --port 27016sh.stopBalancer()
# Бэкап каждого shardmongodump --host shard1RS/localhost:27017 --out /backup/shard1mongodump --host shard2RS/localhost:27018 --out /backup/shard2
# Бэкап config serversmongodump --host configRS/localhost:27019 --out /backup/config
# Возобновить балансировкуsh.startBalancer()💡 Best Practices
Заголовок раздела «💡 Best Practices»-
Shard Key нельзя изменить после создания коллекции! Выбирайте тщательно.
-
Тестируйте на реальных данных:
- Симулируйте production нагрузку
- Проверяйте распределение chunks
- Мониторьте hotspots
-
Мониторинг:
- Размер chunks и балансировка
- Targeted vs broadcast queries
- Latency между mongos и shards
-
Планируйте миграцию:
- Начинайте с replica set
- Переходите на sharding при >100GB или bottleneck
-
Инфраструктура:
- Config servers: минимум 3 (replica set)
- Shards: минимум 2 (каждый replica set)
- Mongos: минимум 2 (для HA)
Ограничения Sharding
Заголовок раздела «Ограничения Sharding»- Уникальные индексы должны включать shard key
- Нельзя делать multi-document transactions между shards (до MongoDB 4.2)
- Некоторые команды не поддерживаются (group, mapReduce)
- Overhead на мониторинг и управление
⚠️ Частые ошибки
Заголовок раздела «⚠️ Частые ошибки»- Выбор монотонно растущего shard key (_id, timestamp)
- Недостаточная cardinality (например, только 10 разных значений)
- Запросы без shard key (broadcast на все shards)
- Игнорирование мониторинга балансировки
Следующий урок: Replica Sets в MongoDB →