4.6 分片:MongoDB 的水平扩展


4.6 分片:MongoDB 的水平扩展

复制集(4.5)解决的是"机器挂了怎么办",分片解决的是"机器不够怎么办"——3.6 节分片理论的产品化实现。本节拆分片集群的三大组件、范围与哈希两种片键策略的落地细节,以及块(Chunk)分裂与迁移机制;3.6 的分片键两标准(离散、可路由)会在这里变成具体决策。

三大组件:路由、目录、数据

分片集群由三类角色协作:

  • mongos 路由:无状态查询路由器,应用连它而不是直连分片;它解析查询中的片键条件,把请求定向到对应分片(可路由),无片键条件的查询广播全部分片(scatter-gather,慢)。
  • 配置服务器(Config Server):以复制集形式部署,存储集群目录——哪个分片负责哪些数据区间、块的位置元数据。所有路由决策从这里来,它是集群的大脑。
  • 分片(Shard):数据节点的本体,每个分片内部就是一个复制集——高可用与水平扩展在这里叠加:单个分片宕机不丢数据(复制集兜底),数据量增长由增加分片吸收。

片键:范围与哈希的落地

3.6 讲过两种策略的理论,MongoDB 的落地形态是:范围片键把片键值域切成连续区间(如用户 ID 百万一段),区间内数据共处一个分片,范围查询天然高效;哈希片键对片键计算哈希值再按哈希值域切分,写入均匀但范围查询广播。两者不可兼得,按查询模式定:订单按时间范围查询为主则范围片键(时间作片键),写入无范围语义则哈希片键(用户 ID 哈希)。

片键还有一个工程细节:块(Chunk)与均衡器。数据按片键区间切成块(新版本默认按需分裂),均衡器(Balancer)检测分片间块数失衡后,在后台把块从重分片搬到轻分片。三个推论:均衡迁移消耗带宽,可设时间窗口避开业务高峰;单块内数据量过大无法分裂(片键值高度重复,如同一用户占了海量数据)会产生不可分块(Jumbo Chunk),只能人工处理;片键一旦选定不可更换(直到近年版本才提供在线改片键,代价仍高)。

演练:订单集合的分片改造全过程

背景:订单集合 8 亿行、单复制集写入打满(每秒 6 万),决定改造为四分片集群。查询模式:按用户查订单列表(高频)、按时间范围出报表(中频)、按订单号点查(高频)。

操作:第一步评估片键候选。候选一:用户 ID 哈希——写入均匀、按用户查询可路由,但时间报表广播四分片;候选二:时间范围——报表高效但尾部热点(新订单全写入最新分片),写入瓶颈原样保留;候选三:复合片键 {userId: "hashed"}——以用户哈希为主键兼顾均匀与路由,时间报表接受广播(中频可忍)。第二步选候选三,预分裂哈希区间避免上线初期数据全涌进一个分片;第三步双写迁移:新分片集群与旧库并行写入,历史数据批量导入,校验一致后切流;第四步观察均衡器窗口与块分布。

结果:写入吞吐随分片数近线性扩展(四分片约 4 倍);按用户与订单号查询毫秒级;时间报表变慢约 3 倍但中频可接受;两处隐患被预案化解——某大商户(单用户 ID)订单极多产生大块,通过把该商户拆到独立集合解决。

解读:注意改造流程的顺序——先定片键(最难且不可逆)、再预分裂(防初期倾斜)、后双写迁移(可回滚)。大商户问题暴露了哈希片键的隐藏假设:片键取值离散且单值数据量有限,假设不成立就要在建模层拆解(独立集合或复合片键补充维度)。

变式:若报表也升为高频,把报表查询改走专门的分析集合(3.5 变式手法),主分片集群专注交易路径——分片方案不追求覆盖一切查询,覆盖核心查询即可。

易错点

第一个是用自增字段做范围片键:所有写入涌向最后一个分片,集群形同虚设——这是分片设计的第一大坑,3.6 已预警。第二个是无片键查询常态化:应用里大量广播查询把 mongos 变成瓶颈,上线前用查询日志审计片键命中率。第三个是把 mongos 当连接池:mongos 无状态但不是免费的,连接应经过应用侧池化,而非每请求新建。第四个是忘记分片内的复制集健康:分片解决规模,复制集(4.5)才解决可用,两者配置都要到位。

分片键选择对照表

分片集群由三部分组成:分片(存数据)、配置服务器(存元数据)、路由节点(mongos,接收客户端请求)。而决定这套架构跑得好不好的,几乎只有一件事——分片键。

分片键类型 分布 范围查询 热点风险 适用
升序键(时间戳、自增 ID) 差,新数据全落一片 高,写热点集中 少用,除非写入压力小
随机哈希键 好,均匀 差,范围查询变广播 高并发写入、点查为主
复合哈希键(租户 + 哈希) 中等 SaaS 多租户,兼顾隔离与均匀
基于区间的分区(Zone) 可控 取决于分区设计 地域合规、冷热分层

演练:chunk 迁移与一次扩容

数据按分片键的取值区间切成 chunk(默认 64MB 左右为一个),均衡器(balancer)负责在分片之间搬 chunk。

// 查看分片集群概况 sh.status() // 关键输出:各分片持有的 chunk 数量、每个分片键区间的归属 // 手动触发一次迁移(生产上通常由均衡器自动完成) sh.moveChunk("demo_shop.orders", { customer_id: 90001 }, "shard-02") // 查看均衡器状态与正在进行的迁移 sh.isBalancerRunning() sh.getBalancerState()

迁移过程分四步:源分片复制 chunk 数据到目标分片 → 同步迁移期间的增量变更 → 短暂锁定该 chunk 的写入完成切换 → 更新配置服务器上的元数据。整个过程中客户端请求会收到"迁移中"的重定向响应,由驱动自动重试。

三条分片实践要点

第一,分片键要服务最高频的查询。 不带分片键的查询会广播到所有分片再在路由节点归并,分片越多越慢。上线前把核心查询统计一遍,选出覆盖度最高的字段组合。

第二,优先选"基数高、分布均匀、且查询常带"的字段。 基数太低(如性别)会让数据集中在少数 chunk;分布不均(如大客户订单量占比 30%)会造成数据倾斜——后者可以用复合键(tenant + 哈希)缓解。

第三,分片键一旦选定几乎无法更改。 4.4 起支持通过 refineCollectionShardKey 修改分片键的后缀字段,但前缀字段仍不可变。因此选键这件事,值得在数据量还小的时候多花几天。

本节要点回顾

  • 三组件:mongos 路由(可路由或广播)、配置服务器(集群大脑)、分片(内部即复制集)
  • 片键二选一:范围(报表友好、尾部热点)对哈希(写入均匀、范围广播)。
  • 复合片键可兼顾均匀与路由;片键不可轻换,选型即终审。
  • 块分裂 + 均衡器自动再平衡;**大块(Jumbo)**源于片键值重复度高的数据。
  • 改造顺序:定片键 → 预分裂 → 双写迁移 → 校验切流,每步留回滚。

扩展链路走通,下一节补上分析能力——聚合框架,文档流水线上的变形与统计。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U