连载中 13/20

扩容:成倍扩容与预分片

2026-07-24 · 3567 阅读 · 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)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞