分片与一致性哈希
Lesson 0004 的读写分离解决读扩展;但写最终都汇聚到主库——一条路走不通,就得把数据切开放。一致性哈希则是分布式系统的通用地基:Redis 集群、Dynamo、Cassandra、CDN、P2P 网络全都用它[1]。面试中「数据怎么分」的追问几乎必到。
§1 · 分片的三种切法
分片(sharding):把数据按某个键水平切开,每台机器负责一个分片。怎么决定「某条数据归谁」?三种策略:
| 策略 | 机制 | 主要代价 |
|---|---|---|
| 范围分片 Range-based | ID 1–1000 万在 A,1000–2000 万在 B;对范围查询友好 | 热点:新数据都写最新段,最后一片被打爆 |
| 哈希分片 Hash-based | hash(user_id) % N;分布均匀 | 扩容时 N 变了 → 几乎全部数据要搬家(见 §2) |
| 查表分片 Directory | 独立路由服务记录「哪条数据在哪」;最灵活 | 查表服务本身是瓶颈和单点 |
§2 · 核心难题为什么需要一致性哈希
取模分片的天生缺陷:hash % 3 变成 hash % 4,原来所有 key 的归属几乎全部改变——扩容一台机器 = 搬家全部数据,期间缓存全部失效(雪崩,Lesson 0005)。
一致性哈希(consistent hashing)的解法:把节点和 key 都放到一个 0 ~ 2³² 的环上,key 顺时针找最近的节点。增删节点只影响相邻一段的数据,迁移量从「几乎全部」降到「约 K/N」[1]。
别只看图——亲手玩一遍。下面的可视化里,黑边的小点就是这次变更中需要迁移的 key:
① 点「添加节点」:观察迁移比例 ≈ 1/N(新节点只接管一小段);
② 点「移除节点」:同理,它的数据顺时针交给下家;
③ 看图例:不开虚拟节点时,三个节点的 key 占比可能差很远(随机位置的运气);
④ 打开「虚拟节点」:占比立刻变均匀——这就是 Dynamo/Cassandra 的工程答案。
虚拟节点 Virtual Nodes
物理节点少时,它们在环上的随机位置会撞出不均匀的分布(方差大)。解法:每个物理节点在环上放置 N 个「影子」(如 100 个),统计意义上分布立刻均匀;还能按机器算力分配不同数量的影子——新机器多放几个,老机器少放几个[1]。
§3 · 分片键的选择比算法更重要
分片键(sharding key)一旦选定极难更改,是数据层最重要的决策。三条标准:
- 高基数(high cardinality):取值足够多,否则切不出均匀的片(用「性别」当分片键?只有两片);
- 查询对齐:最常用的查询必须带上分片键,否则每次查询要广播到所有分片再合并(scatter-gather),慢且贵;
- 抗倾斜:大 V、爆款、企业大客户——单个 key 的数据量或流量远超均值就是数据倾斜(data skew)。必要时对超热 key 做二次拆分或 Lesson 0005 的热点手段。
分片的代价清单要主动说:跨片 JOIN 基本告别、跨片事务要分布式协议(Lesson 0009 预告)、二级索引只覆盖本片。「什么时候才需要分片」本身就是权衡——很多系统在数据量到 TB 级之前,读写分离 + 缓存就够了。
§4 · 随堂检测
§5 · 检索练习
凭记忆回答:① 取模分片扩容时会发生什么?一致性哈希把代价从多少降到多少?② 虚拟节点解决什么问题?③ 分片键的三条标准?
核对参考答案
① %N 变 %N+1 → 几乎全部 key 归属改变(全量搬家 + 缓存全失效);一致性哈希把它降到约 K/N(加一台只迁 1/N)。② 物理节点少时环上位置随机导致分布不均;每节点放 N 个影子位置,统计均匀 + 按算力加权。③ 高基数、查询对齐(带分片键查)、抗倾斜。
§6 · 本周行动
① 找到你公司数据层(或 Redis 集群)的分片方式:取模?一致性哈希?分片键是什么?
② 用三条标准审一遍分片键:基数够吗?最频繁的查询带分片键吗?有倾斜风险吗?
③ 没有分片也不打算分?那就回答「数据量到多少时会需要」——给出数字和依据。
§7 · 延伸资源
- 首选精读:Amazon Dynamo 论文 §4.2(一致性哈希与虚拟节点的原始出处)[1]
- DDIA「Partitioning」章——分片策略与倾斜的系统论述[2]
- 本课组件:
assets/hash-ring.js——把节点数拉到 9 个、开关虚拟节点各玩一遍 - 下一课 → Lesson 0007 消息队列与异步化