MongoDB 分片是什么?Shard Key 应该怎么选?
简化版
MongoDB 分片是把一个集合的数据按 shard key 拆分到多个 shard 上,实现水平扩展。Shard Key 选择非常关键,要兼顾高基数、写入分布均匀、查询可定向路由、避免热点和 jumbo chunk。选错 shard key 会导致数据倾斜、单分片热点、跨分片查询变多,后期调整成本很高。
详细版
分片集群主要组件:
- Shard:实际存储数据的分片;
- Mongos:查询路由;
- Config Server:保存集群元数据;
- Shard Key:决定文档分布和查询路由;
- Chunk:数据迁移和平衡的基本范围。
好的 shard key 通常具备:
- 高基数:不同取值足够多;
- 低频重复:不会大量数据集中到一个值;
- 写入分散:避免所有写入打到一个分片;
- 查询常带:让查询能定向到少数分片;
- 不容易变更:分片键变更成本高。
完整版教学
一、为什么需要分片
当单个副本集无法承载数据量或吞吐时,需要水平扩展。分片把数据拆到多个 shard,每个 shard 负责一部分数据。
可以理解为:
集合 orders
├── shard1: 一部分订单
├── shard2: 一部分订单
└── shard3: 一部分订单
这样存储和读写压力可以分摊。
二、Mongos 负责路由请求
应用通常连接 mongos。Mongos 根据查询条件和 config server 中的元数据判断请求应该发往哪些 shard。
如果查询带 shard key:
db.orders.find({ userId: 100 })
可能只路由到目标 shard。
如果查询不带 shard key,就可能广播到多个 shard,这叫 scatter-gather,成本更高。
三、Shard Key 是分片成败的核心
Shard Key 决定数据怎么分布,也决定查询能否定向。
如果选择递增字段如纯时间戳或自增 ID,写入可能总是落到最后一个范围,形成写热点。
如果选择低基数字段如性别、状态,数据会集中到少数取值,分布不均。
所以 shard key 不是随便选一个查询字段,而是要综合写入分布和查询路由。
四、范围分片和哈希分片怎么取舍
范围分片适合范围查询,例如按时间范围查询。但如果写入键单调递增,容易产生热点。
哈希分片可以让写入更均匀,但范围查询可能要访问多个 shard。
取舍思路:
- 范围查询很重要:考虑范围分片或复合键;
- 写入均匀更重要:考虑哈希分片;
- 既要过滤业务维度又要分散写入:考虑复合 shard key;
- 不要只看当前数据量,要看未来增长。
五、分片不是自动解决所有慢查询
分片能扩展容量和吞吐,但如果查询不能定向,反而可能更慢。
常见问题:
- 查询不带 shard key,广播所有分片;
- shard key 选择导致数据倾斜;
- 热点用户或热点租户集中;
- chunk 无法正常拆分或迁移;
- 跨分片聚合和事务成本高。
分片前要先确认单机索引、查询、建模已经优化过。
六、Chunk 和均衡迁移决定运维成本
MongoDB 分片不是简单把数据平均切成几份,它通过 chunk 管理数据范围,并由 balancer 在 shard 之间迁移 chunk。Shard key 的分布会直接影响 chunk 是否能正常拆分、迁移和均衡。
| Shard Key 问题 | 可能后果 | 例子 |
|---|---|---|
| 低基数 | 少数 chunk 过大 | status 只有 3 个取值 |
| 单调递增 | 写入集中到尾部 chunk | 时间戳、自增 ID |
| 热点实体 | 单 shard 请求过载 | 头部租户、热门商家 |
| 查询不带 shard key | 广播查询 | 全站按状态查订单 |
假设集合有 9 亿文档,分到 3 个 shard,理想状态每个 shard 约 3 亿;但如果 shard key 是 tenantId,某个大租户占 4 亿文档,它所在 shard 就会明显倾斜。分片设计必须同时看数据量分布和请求量分布。
七、常见误区与追问
带 shard key 查询:
mongos -> 定位目标 shard -> 单 shard 查询
不带 shard key 查询:
mongos -> 广播多个 shard -> 汇总结果
记忆钩子:好 shard key 要同时满足三个动作:写得散、查得准、迁得动。
- 误区:分片后所有查询都会变快。 不带 shard key 的查询可能广播到所有 shard,再汇总排序,复杂度反而更高。
- 误区:高基数字段一定适合做 shard key。 高基数只是条件之一,还要看查询是否常带、写入是否均匀、是否存在热点实体。
- 误区:哈希分片没有任何缺点。 哈希能分散写入,但范围查询和按时间归档可能需要访问多个 shard。
- 追问:为什么递增 shard key 容易热点? 新写入总落到最新范围 chunk,导致某个 shard 承担大部分写压力。
- 追问:什么是 scatter-gather? 查询无法定向到单个 shard 时,mongos 向多个 shard 发请求并聚合结果,延迟和资源成本更高。
- 追问:Shard key 选错后好改吗? 成本很高,可能涉及重分片、迁移、索引、停机或长时间双写校验,所以前期评估非常关键。
八、加强记忆
MongoDB 分片的核心是 shard key。好 shard key 要让数据分得开、写入散得开、查询找得到;坏 shard key 会让分片集群变成更复杂的单点瓶颈。分片解决水平扩展,不替代索引和建模优化。