如何设计可以动态扩容缩容的分库分表方案

大纲

前言

线上系统已经完成了分库分表方案的落地:库表建好、基于分库分表中间件的开发与测试也都顺利完成,数据能够均匀分布到各个库表中,系统也通过双写方案平滑迁移并成功切换到分库分表架构上。然而,随着业务持续增长,现有的库表再次面临瓶颈:单库容量接近上限、单表数据量过大、单库写入并发压力过高等问题,必须进行进一步的扩容。比如,原来是 3 个库,每个库 4 个表,现在需要扩容成 6 个库,每个库 8 个表。问题是:如何在不影响线上业务(即不停机)的情况下,进行动态扩容或缩容呢?

停机扩容

停机扩容与 停机迁移数据 类似,步骤基本相同,唯一的区别是需要使用数据导入工具,将现有库表中的数据抽取并导入到新的库表中。但这种方式并不推荐,原因在于:既然已经采用了分库分表,说明数据量非常庞大,可能达到数亿甚至几十亿条记录,此时再采用停机迁移的方式风险极高。从单库单表迁移到分库分表架构时,数据量尚可接受,单表最多两三千万条,通过开发数据迁移工具,使用多台机器并行处理,1 小时内就能完成导入。但当系统基于分库分表架构运行一段时间后,例如当前有一共 3 个库和 12 个表,数据量达到 1 亿 ~ 2 亿条,仅导入数据就需要数小时。假设 6 点开始导入数据,直到导入完成,后续还要修改数据库连接配置、重新部署系统、业务测试验证,往往到 10 点才能完成,停机时间过长,线上业务无法承受。

动态扩容

一开始就将分库分表架构规划为 32 个库,每个库 32 张表,共 1024 张表。这种设计基本能够满足国内大多数互联网公司的需求,无论是支撑并发能力还是数据容量都没有问题。通过一次性规划足够的库表数量(如 32 个库,每个库 32 张表)+ 固定取模路由规则(2^n)+ DBA 库级迁移工具支持,既能保证早期部署简单,又能支持后期平滑扩容或缩容,避免频繁停机迁移和复杂的数据重新分片工作。

  • 性能预估:
    • 每个库可承载约 1000 TPS(写入并发),则 32 个库总共可支撑 32000 TPS。
    • 如果每个库承载到 1500 TPS,则总共可达 48000 TPS,接近 5 万 /s。
    • 前端再加一个消息队列(MQ)进行削峰:
      • MQ 写入 QPS 可达 8 万 /s,MQ 消费 QPS 可达 5 万 /s,可平滑处理高并发写入。
  • 容量规划:
    • 32 个库,每个库 32 张表。
    • 一共 1024 张表,每表存 500 万条数据,总容量可达 50 亿条数据。
    • 对于国内绝大多数互联网公司,这个规模一般足够支撑多年。
  • 路由规则:
    • 库路由:orderId % 32 → 确定库。
    • 表路由:(orderId / 32) % 32 → 确定表,必须先将 orderId 除以库数量,再对表数量进行取模运算,目的是:
      • 避免表内数据分布不均,导致热点数据集中在某几个表或库;
      • 将数据均匀打散到 32 个库 × 32 张表,每个库内部的 32 张表都能均匀分布数据;
      • 避免表的分布就和库的分布耦合在一起,比如库 0 里永远只会使用到表 0,库 1 永远只会使用到表 1;
    • 这种 基于 2^n 的取模方案,天然支持倍数扩容和缩容,减少扩容时路由规则变更的复杂度。
  • 扩容策略:
    • 首次分库分表就 “一步到位”,建立 32 个库,每个库 32 张表,避免频繁扩容。
    • 早期阶段:
      • 可将多个逻辑库部署在同一台 MySQL 服务器上,例如 4 台 MySQL 服务器,每台 MySQL 服务器创建 8 个库,一共有 32 个库。
    • 后期扩展:
      • 当 MySQL 单机实例压力过大,可以申请多台 MySQL 服务器,进行倍数扩容(如从 4 台扩展至 8 台);
      • 扩容时只需要不断地在旧库和新的 MySQL 服务器之间做数据迁移,然后系统仅需更改库的连接地址,重新发布即可,路由规则无需变更;
      • 最多可扩展至 32 台 MySQL 服务器(每台一个库,每个库 32 张表)。
  • 缩容策略:
    • 如果需要减少 MySQL 服务器的数量,可以进行倍数缩容(如从 8 台扩展至 4 台),即物理合并 MySQL 服务器(保持逻辑库的总数量不变);
    • 比如,原来 32 个库分布在 8 台 MySQL 服务器上,现在迁移到 4 台 MySQL 服务器上,每台 MySQL 服务器上有 8 个库;
    • 只需要将部分库的数据迁移到其他 MySQL 服务器,然后系统仅需更改库的连接地址,重新发布即可,路由规则无需变更。
  • 优势总结:
    • DBA 通过成熟的工具迁移旧库至新的 MySQL 服务器即可完成扩缩容,业务代码无需处理复杂的数据迁移逻辑。
    • 这种分库分表规划减少了后期频繁改造的成本,也能较好应对未来业务增长。