MongoDB 分片(Sharding)全面指南
MongoDB 分片(Sharding) 是实现 水平扩展(Horizontal Scaling) 的核心机制,将数据分布到多个 分片(Shard) 上,每个分片是一个 副本集(Replica Set),通过 路由进程(mongos) 实现透明访问。
一、核心组件
| 组件 | 职责 | 是否高可用 |
|---|---|---|
| mongos | 查询路由、数据合并、客户端入口 | 可多实例 |
| config server | 存储元数据(chunk 位置、配置) | 必须是 3 节点副本集 |
| shard | 实际存储数据的副本集 | 每个 shard 建议 3 节点副本集 |
客户端 → mongos → config servers + shards
二、架构图示例
┌─────────────────┐
│ Client │
└─────────┬─────────┘
▼
┌─────────────────┐
│ mongos │
└───────┬───┬─────┘
▼ ▼
┌──────────────────────────┐
│ Config Servers (RS) │
│ [csrs: 3 nodes] │
└───────┬──────────────────┘
▼
┌──────────────┴───────────────┐
▼ ▼
┌───────────────┐ ┌───────────────┐
│ Shard 1 │ │ Shard 2 │
│ (replset rs1) │ │ (replset rs2) │
│ [p,s,s] │ │ [p,s,s] │
└───────────────┘ └───────────────┘
三、分片键(Shard Key)选择原则
| 原则 | 说明 |
|---|---|
| 高基数(Cardinality) | 值分布均匀,避免热点(如 user_id 优于 status) |
| 低频率变更 | 避免 update 触发 chunk 迁移 |
| 支持查询模式 | 常用过滤字段(如 user_id, createdAt) |
| 避免单调递增 | 如 ObjectId 导致写入集中到最后一个 chunk |
推荐分片键
| 场景 | 推荐键 |
|---|---|
| 用户数据 | user_id |
| 时间序列 | { user_id: 1, createdAt: 1 } |
| 多租户 | tenant_id |
| 地理位置 | location(hashed) |
四、搭建分片集群(完整实战)
1. 目录结构
mkdir -p shard/{config,shard1,shard2}/{1,2,3} mongos
2. 启动 Config Server(副本集 csrs)
# config1.conf
mongod --configsvr --replSet csrs --bind_ip_all --port 27019 --dbpath shard/config/1
# config2.conf / config3.conf 类似,端口 27020, 27021
// 初始化 csrs
rs.initiate({
_id: "csrs",
configsvr: true,
members: [
{ _id: 0, host: "localhost:27019" },
{ _id: 1, host: "localhost:27020" },
{ _id: 2, host: "localhost:27021" }
]
})
3. 启动 Shard 1 和 Shard 2(副本集)
# shard1-1.conf
mongod --shardsvr --replSet shard1 --port 27018 --dbpath shard/shard1/1
# 类似启动 shard1-2(27028), shard1-3(27038)
# shard2: 27048, 27058, 27068
// 初始化 shard1
rs.initiate({
_id: "shard1",
members: [
{ _id: 0, host: "localhost:27018" },
{ _id: 1, host: "localhost:27028" },
{ _id: 2, host: "localhost:27038" }
]
})
4. 启动 mongos
mongos --configdb csrs/localhost:27019,localhost:27020,localhost:27021 --port 27017
5. 添加分片
// 连接 mongos
mongosh --port 27017
// 添加 shard
sh.addShard("shard1/localhost:27018,localhost:27028,localhost:27038")
sh.addShard("shard2/localhost:27048,localhost:27058,localhost:27068")
6. 启用数据库分片
sh.enableSharding("mydb") // 启用数据库分片
// 创建集合并指定分片键
sh.shardCollection("mydb.users", { user_id: 1 })
// 或 hashed 分片键(均匀分布)
sh.shardCollection("mydb.events", { device_id: "hashed" })
五、Chunk 与 Balancer
- 数据按 chunk(默认 64MB)切分
- Balancer 自动迁移 chunk 保持均衡
sh.status() // 查看分片状态
sh.getBalancerState() // 是否运行
sh.disableBalancing("mydb.users")
sh.enableBalancing("mydb.users")
// 手动迁移 chunk
sh.moveChunk("mydb.logs", { timestamp: ISODate("2025-01-01") }, "shard2")
六、查询路由机制
| 查询类型 | 路由方式 |
|---|---|
| Targeted | 包含分片键 → 直达目标 shard |
| Scatter-Gather | 无分片键 → 广播所有 shard |
| Hashed Key | 计算 hash → 定位 |
// Targeted(高效)
db.users.find({ user_id: 12345 })
// Scatter-Gather(低效)
db.users.find({ email: "a@x.com" }) // 无索引 + 无分片键
七、高级分片策略
1. 复合分片键(Compound Shard Key)
sh.shardCollection("mydb.orders", {
customer_id: 1,
order_date: 1
})
支持
{ customer_id: X }和{ customer_id: X, order_date: { $gte: ... } }
2. Hashed 分片键
sh.shardCollection("mydb.sessions", { session_id: "hashed" })
适合高并发写入,避免热点
3. 标签感知分片(Tag-Aware Sharding)
// 标记 shard 地域
sh.addTagRange("mydb.users",
{ region: "US", user_id: MinKey },
{ region: "US", user_id: MaxKey },
"US-shard"
)
sh.addShardTag("shard1", "US")
实现 数据本地化
八、性能监控与优化
关键命令
sh.status() // 整体概览
db.collection.getShardDistribution() // 数据分布
sh.balancerCollectionStatus("mydb.users")
// 查看当前 balancer 状态
sh.isBalancerRunning()
关键指标
| 指标 | 说明 |
|---|---|
chunks per shard | 应接近平均 |
jumbo chunks | 超过 64MB,需拆分 |
balancer rounds | 迁移频率 |
mongos latency | 路由延迟 |
九、常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 写入热点 | 单调递增分片键 | 改用 hashed |
| 查询慢 | Scatter-Gather | 添加分片键索引 |
| Balancer 不工作 | 窗口关闭 | sh.setBalancerState(true) |
| Chunk 无法拆分 | 文档太大 | 预分片 / 调整设计 |
| 元数据不一致 | config server 异常 | 重启 csrs |
十、生产级最佳实践
| 建议 | 说明 |
|---|---|
| 每个 shard 是 3 节点副本集 | 高可用 |
| config server 单独部署 | 避免竞争 |
| mongos 与应用同机房 | 降低延迟 |
| 预分片(Pre-splitting) | 大批量导入前 |
| 定期备份 config DB | mongodump -d config |
十一、Docker Compose 一键部署(生产可用模板)
version: '3.8'
services:
config1: &config
image: mongo:7
command: --configsvr --replSet csrs --bind_ip_all
volumes: [./data/config1:/data/db]
config2: *config
config3: *config
shard1-1: &shard
image: mongo:7
command: --shardsvr --replSet shard1 --bind_ip_all
volumes: [./data/shard1-1:/data/db]
shard1-2: *shard
shard1-3: *shard
shard2-1: *shard
shard2-2: *shard
shard2-3: *shard
mongos:
image: mongo:7
command: mongos --configdb csrs/config1:27019,config2:27020,config3:27021 --bind_ip_all
ports: ["27017:27017"]
depends_on: [config1, config2, config3]
初始化脚本略,可参考上文
十二、学习资源
- 官方文档:Sharding
- 分片管理:Shard Administration
- MongoDB University:M201: Sharding
你想实现什么?
- 大规模日志系统?
- 多租户 SaaS?
- 全球分布式部署?
- 10 亿+ 数据迁移?
欢迎贴出你的:
- 数据量(文档数、增长率)
- 查询模式(QPS、过滤字段)
- 高可用要求
我可以为你设计 完整分片方案 + 分片键 + 自动化脚本!