内功最后一节回到 1.2 节立下的承诺:水平扩展让容量成为加法。把承诺兑现的机制就是分片(Sharding)——按什么规则把数据切到不同节点、切错了怎么办、集群怎么再平衡。本节是通用内功的收官,第 4.6 节 MongoDB 分片与第 5.8 节 Redis 集群都是它的具体实现。
阅读完本节,你应当能够:
范围分片(Range):按分片键的值域切分——ID 1 到 100 万在节点 A,100 万到 200 万在节点 B。优点是范围查询友好("某时间段数据"落在连续区间);缺点是天然热点:按自增 ID 分片,所有新写入永远砸向最后一个节点。
哈希分片(Hash):对分片键做哈希后按哈希值域切分。优点是写入均匀打散,热点消失;缺点是范围查询变成全分片广播——原本"读一个用户的所有订单"变成"问遍所有节点"。
一致性哈希(Consistent Hashing):把哈希空间排成一个环,节点与数据都映射到环上,数据归属顺时针遇到的第一个节点。它的杀手锏在扩缩容:增删一个节点只迁移相邻区间的数据(普通哈希分片要迁移几乎全部数据)。Dynamo 系产品与多数分布式缓存采用此法。

无论哪种策略,真正的难点是选分片键,因为多数系统(MongoDB、Cassandra)的分片键建表后不可更换。选择标准是两头的平衡:
三个标准常常打架——用户 ID 均匀但按时间查询就广播,时间键范围友好但尾部写入热点。解法通常是复合分片键(如"用户 ID + 月份"):主维度保离散,次维度控分区大小。
背景:私信表预计三年内 50 亿行,查询模式两种——会话视图(某两人之间的消息,按时间倒序)与未读提醒(按接收者聚合未读数)。
操作:第一种候选,按消息 ID 哈希分片:写入均匀,但会话视图需要遍历所有分片,否决。第二种候选,按发送者 ID 分片:会话视图里接收方消息在另一个分片,一次会话要读两个分片再合并,勉强可用但路由复杂。最终方案:分片键取"会话 ID"(由双方用户 ID 排序拼接生成,保证同一会话双向消息同键),会话视图一次定位;未读提醒用独立的按接收者分片的统计表,发消息时同步更新(写入双份)。
结果:会话视图毫秒级;统计表承担高频未读检查;两个表各自服务一种查询,符合 3.5 节"查询清单驱动建模"的思路。
解读:注意最终方案里分片键设计解决的是路由问题,冗余表解决的是第二查询模式问题,两者分工明确。还要预判一个问题:以"会话"为分片单位,重度用户的会话更多,但单会话数据量可控(消息按时间归档),分区不会无限膨胀——分片键的粒度选择同样重要。
变式:如果三年后数据远超预期,要加节点怎么办?范围分片需要迁移区间并更新路由表(协调服务承担);一致性哈希只搬相邻区间;MongoDB 的均衡器会在后台自动搬迁块(第 4.6 节)。无论哪种,再平衡期间的性能抖动与双写窗口都要提前演练——分片方案上线前,先在测试环境模拟一次加节点。
第一个错误是过早分片:单机明明还能扛两年,团队急着上分片架构,运维复杂度先涨、收益为零;常见判断是单机写入持续超过阈值、或数据量已让备份与迁移窗口不可接受。第二个是自增键直接分片:尾部热点让集群退化为单机性能,哈希化或复合键是必修课。第三个是跨分片事务依赖:设计时放任业务做跨分片的多步写入,后期被 3.4 节的种种限制反噬;分片设计应尽量让强相关数据同片(co-location)。
水平扩展的动作是"把数据切成多份放到多个节点",怎么切决定了后续所有的运维特性。
| 策略 | 怎么切 | 优点 | 缺点 | 适用 |
|---|---|---|---|---|
| 范围分片 | 按分片键的区间(如时间、ID 段) | 范围查询高效;扩容只加新片 | 热点集中(最新区间最热) | 日志、时序、按时间查询为主 |
| 哈希分片 | 对分片键取哈希后取模 | 数据分布均匀 | 范围查询变广播;扩容需大量搬迁 | 用户数据、订单、KV 缓存 |
| 一致性哈希 / 虚拟节点 | 哈希环 + 虚拟节点 | 扩容只搬少量数据 | 实现复杂;仍不擅长范围查询 | 分布式缓存、对象存储 |
还有一种常被忽略的维度:地理或业务分区。按地区或租户把数据固定到特定分片(如欧盟用户的数据留在欧盟节点),这不是性能优化,而是合规与数据主权要求。这类约束会直接否决掉某些分片策略,选型时必须先确认合规要求。
假设 4 个节点存 1024 个逻辑槽位,现在要加到 5 个节点。
过程中最容易出问题的两处:一是迁移期间客户端路由信息过期,请求仍打到旧节点——需要节点返回重定向响应、客户端重试,这要求客户端协议支持;二是迁移中断(网络抖动、节点重启)后状态不一致,需要迁移任务可重入、可回滚。
水平扩展不是无限的,三类负载很难靠加机器解决:跨分片的强一致事务(协调成本随节点数增长)、不带分片键的全局查询(广播代价随节点数线性增长)、以及单点写入的全局计数器(热点无法分散)。遇到这三类需求,正确做法不是继续加机器,而是改设计——加缓存层、加异构索引、或者干脆把这类负载挪到专用引擎。
通用内功修炼完毕。下一章把镜头推近,深访文档门派的代表 MongoDB——你会看到本章所有法则的产品化长相。