是什么
分片(sharding)把一个大集合按分片键切成若干段(chunk),分散到多个 shard 上。集群由三类角色组成:mongos 负责接收客户端请求、按分片键路由;config server 保存集群元数据(分片键、chunk 分布、均衡状态);shard 承载真实数据,每个 shard 本身通常是一个副本集。分片键在集合创建后不能随意改,因此它是设计阶段的一次性决策。
基本信息
| 集群角色 | mongos 路由、config server 元数据、shard 数据节点(副本集) |
|---|---|
| 开启分片 | sh.enableSharding("app") |
| 分片集合 | sh.shardCollection("app.orders", { customerId: "hashed" }) |
| 查看状态 | sh.status()、db.orders.getShardDistribution()、sh.isBalancerRunning() |
| 均衡器 | sh.startBalancer() / sh.stopBalancer() / sh.setBalancerState(true) |
典型使用场景
- 单机容量上限:数据量或写入量超过单个副本集能承载的范围时按分片键水平切分
- 热点写入分散:单调递增的分片键会把写压力集中在最后一段,改用哈希分片可打散
- 数据本地化:用 zone 把特定范围的数据固定在某个机房或高配机型上
- 读写隔离:把冷数据迁到低配分片,热数据留在高配分片
- 并行聚合:无分片键的统计查询会广播到所有分片各自计算,再在 mongos 汇总
启用分片与观察
启用分片与观察
sh.enableSharding("app") # 对数据库开启分片
sh.shardCollection("app.orders", { customerId: "hashed" }) # 哈希分片,写入更均匀
sh.shardCollection("app.logs", { tenantId: 1, ts: 1 }) # 复合范围分片键
sh.status() # 查看集合分片状态与 chunk 分布
db.orders.getShardDistribution() # 逐分片看数据量与 chunk 数
sh.isBalancerRunning() # 均衡器是否在工作
sh.stopBalancer() # 大变更期间可临时停均衡,减少抖动查询路由行为
| 查询条件 | 路由结果 | 代价 |
|---|---|---|
| 分片键等值 | 定向到单个分片 | 最低 |
| 分片键范围 | 命中少量分片 | 较低 |
| 不含分片键 | 广播到全部分片后归并 | 随分片数线性上升 |
| 不含分片键的排序 | 各分片排序后在 mongos 归并 | 内存与网络压力明显 |
| 跨分片聚合 | 各分片局部聚合后汇总 | 受最慢分片影响 |
分片键基数太低(如状态字段)会导致 chunk 无法继续拆分、形成超大 chunk;单调递增的分片键(自增 ID、时间戳)会把写入集中到最后一段。优先选择高基数、写入分散、且常用在查询条件里的字段。
分片键是查询路由的前提,也是文档必须携带的字段:设计时要一起看写入分布与高频查询条件,两边的诉求常需要复合分片键来平衡;MongoDB 5.0+ 提供了 reshardCollection 用于变更分片键,但成本高于一次选对。