MongoDBNotes

第 16 章:分片架构

zjc 于 2026-01-16 发布

这是《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 保存:

  1. 集群成员;
  2. 数据库和集合分片配置;
  3. chunk 区间;
  4. balancer 状态;
  5. 分片拓扑。

要求:

  1. 部署为副本集;
  2. 使用低延迟可靠存储;
  3. 严格控制权限;
  4. 纳入备份;
  5. 不承载业务读负载。

Config Server 不可用会影响元数据操作和路由发现。

16.8 mongos

建议:

  1. 多实例部署;
  2. 应用配置多个 mongos;
  3. 与应用网络就近;
  4. 监控连接和请求延迟;
  5. 控制连接池;
  6. 优雅发布。

mongos 无状态,扩容相对容易,但结果合并和广播查询仍会带来 CPU 与网络成本。

16.9 目标分片策略

设计流程:

find top queries
  -> choose shard key
  -> verify cardinality
  -> verify write distribution
  -> verify target queries
  -> test split and balance

目标:

  1. 写入均匀;
  2. 高频查询带分片键;
  3. 分片键基数足够;
  4. 单 chunk 不无限增长;
  5. 热点 key 可拆分;
  6. 事务跨 shard 可控。

16.10 分片治理

常用命令:

sh.enableSharding("shop")
sh.shardCollection("shop.orders", { user_id: "hashed" })
sh.status()
sh.isBalancerRunning()
sh.startBalancer()
sh.stopBalancer()

生产注意:

  1. 初始导入先规划分片;
  2. 索引在所有 shard 存在;
  3. 监控 chunk 数和分布;
  4. 控制均衡窗口;
  5. 分片集合查询必须评估路由;
  6. 事务和 $lookup 成本更高。

本章小结

分片通过 mongos、Config Server、shard 和 balancer 将数据水平分布。它解决容量与吞吐扩展问题,但查询必须尽量带分片键,运维复杂度也明显上升。分片键设计是整个架构的核心决策。

思考题

  1. 什么信号说明必须考虑分片?
  2. 哈希分片和范围分片的核心差异是什么?
  3. 为什么高频查询应包含分片键?
  4. Config Server 保存哪些信息?
  5. 单热点文档为什么分片无法解决?