mongodb 7.0分片成功需满足三前提:必须启用sharding且只经mongos访问;片键须组合设计(如{"vehicle_id":1,"ts":1})避免热点;balancer需人工干预防卡死,聚合须适配分片规则。

MongoDB 7.0 的分片架构是支撑 PB 级车联网数据(如长城汽车近 700 万辆车接入、PB 级存储)的底层基石,但直接堆机器不等于自动扩容——分片失效、查询变慢、balancer 卡死才是常见现实。
分片前必须确认 sharding 已启用且 mongos 是唯一入口
很多团队在副本集上误加 sh.addShard() 就以为完成分片,结果应用直连 mongod 节点,绕过路由层,导致数据不分布、查询不路由、聚合失效。MongoDB 7.0 严格要求所有读写必须经由 mongos 实例,否则分片逻辑完全不生效。
-
mongos不存数据,只做路由和合并,部署数量应按读写压力横向扩展,而非固定1个 - 检查是否启用分片:连接
mongos后执行sh.status(),若报错sharding not enabled,需先对 config server 执行sh.enableSharding("db_name") - 应用连接字符串必须指向
mongos地址(如mongodb://mongos1:27017,mongos2:27017/?replicaSet=rs0),绝不能混用mongod地址
sh.shardCollection() 的片键选择直接决定水平扩展上限
片键不是越“唯一”越好,也不是越“均匀”越稳。在 PB 级车联网场景中,用 _id 或时间戳做片键极易引发“热点写入”——所有新车辆上报数据集中写入单一分片,导致该 shard 节点 CPU 和磁盘 I/O 持续 95%+,而其他 shard 空转。
- 推荐组合片键:如
{ "vehicle_id": 1, "ts": 1 },既保证单辆车数据局部性(便于按车查轨迹),又通过ts分散写入压力 - 避免使用单调递增字段(如纯
ts)或低基数字段(如status: "online"|"offline"),前者造成写倾斜,后者导致 chunk 无法分裂 - 创建时必须带非空
numInitialChunks参数,例如sh.shardCollection("fleet.telemetry", { "vehicle_id": 1, "ts": 1 }, { numInitialChunks: 256 }),否则 MongoDB 7.0 默认只建 2 个初始 chunk,后续分裂压力全压在 balancer 上
balancer 不是“开箱即用”,moveChunk 失败和 chunk 僵尸化是 PB 级集群常态
MongoDB 7.0 的 balancer 默认开启,但面对日增千万级文档、单集合超百亿文档时,它常卡在“尝试迁移但反复失败”,表现为 sh.status() 中某些 shard 的 chunk 数长期远高于其他节点,且 moveChunk 日志频繁出现 failed with error: NotMaster 或 interrupted at shutdown。
- 根本原因常是 config server 响应延迟或 primary 切换期间 balancer 未暂停:在运维窗口手动执行
sh.stopBalancer(),等集群稳定后再sh.startBalancer() - 禁止在业务高峰时段触发手动迁移:
sh.moveChunk("fleet.telemetry", { vehicle_id: "V123456" }, "shard01")这类操作会阻塞该 chunk 所有读写,持续数秒至分钟级 - 定期检查僵尸 chunk:
db.adminCommand({ listShards: 1 }).shards.forEach(s => print(s._id + ": " + s.state + " " + s.host)),若某 shard 显示"state" : 0或 host 不可达,需人工清理其残留元数据,否则 balancer 拒绝调度
分片后聚合性能断崖下跌?$lookup 和 $facet 必须重写
在单机 MongoDB 上跑得飞快的聚合管道,一上分片集群就超时或返回空结果,尤其涉及跨集合 $lookup 或多阶段 $facet。MongoDB 7.0 仍不支持跨分片的分布式事务式聚合,所有 $lookup 目标集合也必须分片(且片键能与主集合对齐),否则直接报错 unsharded collection cannot be looked up。
- 跨集合关联必须确保被查集合(
from)已分片,且片键包含localField对应字段,例如主集合用{ vehicle_id: 1, ts: 1 },则 lookup 表也需以vehicle_id为片键前缀 -
$facet的每个子管道独立路由,若各 facet 阶段引用不同分片数据,性能会指数级下降;PB 级场景建议拆成多次单分片聚合 + 应用层合并 - 开启
allowDiskUse: true是必须项,否则内存不足时整个聚合直接失败,错误信息为Sort exceeded memory limit
真正卡住 PB 级分片落地的,从来不是“能不能分”,而是片键设计是否匹配业务写入模式、balancer 是否在后台安静干活、以及聚合语句有没有被分片规则悄悄改写语义——这些细节在 sh.status() 的一行输出里,在 moveChunk 的一条日志里,在聚合超时前最后 0.3 秒的慢查询日志里。











