连载中 13/20

扩容:成倍扩容与预分片

2026-07-24 · 3565 阅读 · 0 评论 · 0 赞

取模的账,迟早要还

第 4 篇选 Hash 刀法时埋了个伏笔:取模路由均匀,但扩容是它的死穴。数据按 user_id % 32 分了 32 张表,两年后每张表又长到一亿行,扩到 64 张——模数一改,新的归属和旧的归属对不上号:原本落在表 3 的数据,新规则下大部分要去别的表。「扩容」实际等于「把几乎全部数据重新分配一遍」,生产系统上这就是一次大迁移工程。怎么还这笔账,两条主流路线:翻倍扩容法和预分片法。

路线一:翻倍扩容——数学的温柔

翻倍扩容利用了取模的一个数学性质:模数从 32 翻到 64 时,x % 64 的结果要么等于 x % 32,要么等于它加 32——每条数据只有两个去处:留在原地,或搬进「原表号加 32」的新表。也就是搬家率恰好 50%,而且目标位置确定,无需任何计算协商:

旧:slot = x % 32   →  表 3  (x%32 == 3)
新:slot = x % 64   →  表 3  (x%64 == 3,原地不动)
                    →  表 35 (x%64 == 35,即 3+32,迁移)
判定:x % 64 >= 32 ? 搬到 表(x%32)+32 : 原地不动

操作节奏配双写:新表上线后进入双写期(老表为主、新表同步),后台任务按判定规则分批迁移存量(第 14-15 篇的迁移框架原样复用),迁完校验、切读、切写、下线老表。翻倍法的约束是必须成对扩——32 到 64 到 128,不能 32 跳 96,规划容量时按 2 的幂走。

路线二:预分片——把映射固定住

更一劳永逸的思路是把「数据到槽」的分配和「槽到库」的部署彻底分开:第一天就按一个足够大的模数(如 1024)算槽,1024 个槽的归属永不改变;每个槽映射到当前部署的某个库某张表,这张「槽位映射表」是唯一可变的东西:

slot = hash(user_id) % 1024          // 永不变:数据到槽
slot → (db, table)                    // 可变:槽到物理库的映射
扩容 8 库 → 16 库:把映射表里 4 个库的槽划给 4 个新库,批量搬

扩容从「改路由规则」降级成「改映射配置加搬部分槽」——应用代码与路由算法全程不动。这个思想的同款你在别处见过:Redis Cluster 的 16384 个哈希槽、一致性哈希的虚拟节点,都是用一层固定的大粒度中间层,把数据归属和物理部署解耦。预分片的工程代价:槽位映射本身要高可用存储、要缓存进路由层,代码里多一层间接——买的是未来每一次扩容的从容。

扩容日的执行清单

无论哪条路线,扩容日的动作都收敛成同一套:限速搬迁(迁移任务的 QPS 压在源库安全水位以下)、双写保一致(迁移期间新写双发,杜绝边界数据丢失)、校验后切流(行数加 checksum 通过才放流量)、可回滚(切流后老库保留一个观察期)。这套流程正是接下来两篇「数据迁移」的完整展开——扩容只是迁移最常见的一种触发场景,把它吃透,换机房、换架构、换存储都只是换个搬家的理由。

503

10 年全栈工程师 · 503咖啡馆主理人

#扩容#翻倍扩容#预分片#槽位映射#一致性哈希

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 5 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞