复制集(4.5)解决的是"机器挂了怎么办",分片解决的是"机器不够怎么办"——3.6 节分片理论的产品化实现。本节拆分片集群的三大组件、范围与哈希两种片键策略的落地细节,以及块(Chunk)分裂与迁移机制;3.6 的分片键两标准(离散、可路由)会在这里变成具体决策。
分片集群由三类角色协作:
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(默认 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 修改分片键的后缀字段,但前缀字段仍不可变。因此选键这件事,值得在数据量还小的时候多花几天。
扩展链路走通,下一节补上分析能力——聚合框架,文档流水线上的变形与统计。