直接拆库需先确认必要性,满足单库导出超2小时、单表行数超5000万、日写入超100万等硬指标;否则优先优化索引与归档。垂直拆分应按访问路径而非表名,双写+校验迁移,彻底清理存储过程、触发器与外键。

直接拆库不是“导出再导入”就能完事的,数据一致性、业务连续性、回滚能力这三点没保障,上线当天就可能丢订单或用户信息。
先确认要不要真拆库
很多团队一上来就喊“必须分库”,结果发现只是索引没建好、慢查询没优化、连接池配置不合理。真正需要拆库的信号很具体:
-
mysqldump导出单库耗时超过 2 小时,且SHOW PROCESSLIST经常卡住 - 单表行数 > 5000 万,
SELECT COUNT(*)已不可接受 - 日均写入量持续 > 100 万条,主库
Seconds_Behind_Master经常 > 60 - 不同业务模块(如订单、用户、库存)的读写流量峰值时间错开,但共用同一套事务隔离级别和备份策略
如果以上都不满足,优先做 pt-archiver 归档 + ALTER TABLE ... ALGORITHM=INPLACE 重建索引,比拆库便宜得多。
垂直拆分:按业务边界切库,不是按表名猜
别看到“user_”开头的表就全扔进 user_db,关键看数据访问路径。比如 user_profile 表虽然带 user,但被订单服务高频 JOIN,硬塞进用户库会导致跨库 JOIN 或冗余同步。
实操建议:
- 用
pt-query-digest抓一周慢查,统计每张表被哪些应用模块的 SQL 访问,生成访问热力图 - 把高频联合查询的表(哪怕跨业务)先保留在同一库,后续再用
FEDERATED或视图过渡 -
information_schema.TABLES里查DATA_LENGTH + INDEX_LENGTH,优先从 > 2GB 的库开始切 - 拆完后,每个库的
my.cnf单独配max_connections和innodb_buffer_pool_size,别沿用原单库配置
迁移过程必须支持双写+校验,不能只靠 mysqldump
mysqldump 适合停机迁移,但微服务改造期通常不允许停业务。真实可行的方案是双写 + 增量追平:
- 在应用层加一层路由逻辑:老库写完,立刻发 MQ 消息到新库写入(用
binlog解析更稳,但开发成本高) - 用
pt-table-checksum每小时校验一次分片间数据差异,输出不一致的PRIMARY KEY列值 - 切流前 48 小时开启只读模式,用
SELECT ... FOR UPDATE锁住关键业务表,跑一次最终全量比对 - 所有迁移脚本必须带
--where="id BETWEEN 100000 AND 200000"分段参数,避免长事务锁表
最容易被忽略的坑:存储过程、触发器和外键约束
单体库里藏了 200 多个存储过程?它们大概率用了跨库表名(old_db.user_info),拆库后直接报 ERROR 1146 (42S02): Table 'old_db.user_info' doesn't exist。
处理原则很硬核:
- 触发器全部删掉,改用应用层事件驱动(比如用户注册成功后发
user_created事件) - 存储过程逐个重写为微服务接口,禁止新写任何跨库逻辑
- 外键约束在拆库后物理上失效,必须靠应用层校验 + 最终一致性补偿任务(比如定时扫
order表里不存在的user_id) - 连
EVENT调度器里的定时任务都要检查,很多清理逻辑写死库名,一拆就停摆
最后提醒一句:别信“一次性迁移完成”的承诺。真实项目里,70% 的线上数据问题都出在归档表、历史流水、审计日志这些冷数据上——它们往往没进测试用例,却在某个财务对账场景里突然炸出来。











