这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 分片用于将数据水平分布到多个副本集。它解决单机容量、写入吞吐和大数据集工作集限制,但也会引入路由、分布式事务、均衡和运维复杂度。是否分片,应从数据规模和增长模型出发。
16.1 组件
Client
-> mongos
-> Shard 1 Replica Set
-> Shard 2 Replica Set
-> Shard 3 Replica Set
Config Server Replica Set
| 组件 | 职责 |
|---|---|
| mongos | 路由请求、合并结果 |
| Shard | 保存数据子集 |
| Config Server | 保存元数据和 chunk 分布 |
| Balancer | 迁移 chunk 平衡集群 |
16.2 什么时候分片
分片前先确认瓶颈:
| 瓶颈 | 分片是否有帮助 |
|---|---|
| 数据量超过单机容量 | 有 |
| 写入吞吐超过单机 | 有 |
| 工作集超过内存 | 有 |
| 单热点文档 | 无 |
| 慢查询或无索引 | 先优化 |
| 连接数过多 | 先治理应用 |
| 磁盘故障 | 恢复或升配 |
过早分片会让查询和事务复杂化,收益可能不明显。
16.3 分片键
分片键决定文档分布:
sh.shardCollection("shop.orders", { user_id: "hashed" })
范围分片:
sh.shardCollection("shop.orders", { user_id: 1, created_at: 1 })
| 类型 | 特点 |
|---|---|
| 范围分片 | 支持范围查询,易热点 |
| 哈希分片 | 分布均匀,范围查询需广播 |
| 复合分片 | 平衡查询与分布 |
分片键一旦选择,后续调整代价很高,必须在设计早期评估。
16.4 Chunk
MongoDB 将分片键空间划分为 chunk:
shard 1: [minKey, user_1000)
shard 2: [user_1000, user_2000)
shard 3: [user_2000, maxKey]
查看分布:
sh.status()
查看 chunk:
use config
db.chunks.find({ ns: "shop.orders" }).limit(5)
16.5 查询路由
带分片键:
db.orders.find({ user_id: "u_1001" })
路由到目标 shard。
不带分片键:
db.orders.find({ status: "PAID" })
mongos 需要广播到多个 shard,再合并结果。高频查询应包含分片键或支持定向路由。
16.6 写入路由
单文档写:
document shard key
-> target shard
-> shard replica set primary
不带分片键的插入在一些模式下无法路由,必须提供分片键。更新和删除如果不带分片键,也会影响路由范围。
16.7 Config Server
Config Server 保存:
- 集群成员;
- 数据库和集合分片配置;
- chunk 区间;
- balancer 状态;
- 分片拓扑。
要求:
- 部署为副本集;
- 使用低延迟可靠存储;
- 严格控制权限;
- 纳入备份;
- 不承载业务读负载。
Config Server 不可用会影响元数据操作和路由发现。
16.8 mongos
建议:
- 多实例部署;
- 应用配置多个 mongos;
- 与应用网络就近;
- 监控连接和请求延迟;
- 控制连接池;
- 优雅发布。
mongos 无状态,扩容相对容易,但结果合并和广播查询仍会带来 CPU 与网络成本。
16.9 目标分片策略
设计流程:
find top queries
-> choose shard key
-> verify cardinality
-> verify write distribution
-> verify target queries
-> test split and balance
目标:
- 写入均匀;
- 高频查询带分片键;
- 分片键基数足够;
- 单 chunk 不无限增长;
- 热点 key 可拆分;
- 事务跨 shard 可控。
16.10 分片治理
常用命令:
sh.enableSharding("shop")
sh.shardCollection("shop.orders", { user_id: "hashed" })
sh.status()
sh.isBalancerRunning()
sh.startBalancer()
sh.stopBalancer()
生产注意:
- 初始导入先规划分片;
- 索引在所有 shard 存在;
- 监控 chunk 数和分布;
- 控制均衡窗口;
- 分片集合查询必须评估路由;
- 事务和
$lookup成本更高。
本章小结
分片通过 mongos、Config Server、shard 和 balancer 将数据水平分布。它解决容量与吞吐扩展问题,但查询必须尽量带分片键,运维复杂度也明显上升。分片键设计是整个架构的核心决策。
思考题
- 什么信号说明必须考虑分片?
- 哈希分片和范围分片的核心差异是什么?
- 为什么高频查询应包含分片键?
- Config Server 保存哪些信息?
- 单热点文档为什么分片无法解决?