先交代一个真实项目里的场景。当时我负责的订单中心,上线还不到一年,订单表已经超过9000万行,磁盘占用接近180GB。每天晚高峰,数据库CPU冲上90%,慢查询一个接一个。MySQL性能优化最基本的索引、SQL改写都做了,撑了几个月又顶不住,最后只能走分库分表和水平扩展。这篇文章就把我这一路踩过的坑、选过的型、落过地的方案,完整写一遍。如果你是后端开发或DBA,正被单表数据量折磨,或者面试前想系统搞懂分库分表,这篇可以直接当操作手册看。
1. 项目背景:单库单表的瓶颈到底卡在哪
1.1 先排查资源瓶颈:CPU、磁盘IO、连接数谁先报警
先说结论:分库分表不是第一步,拆库拆表之前,你得先搞清楚单库到底是被什么资源卡死的。我习惯的做法是盯四个指标:慢查询数量、活跃连接数、磁盘IO使用率、InnoDB锁等待。如果慢查询数量随着数据量线性增长,而且优化完SQL仍然缓解不了,才需要考虑单表规模本身的问题。
以那张订单表为例,起初慢查询集中在两类:一类是按用户查最近订单,另一类是后台按时间范围拉订单。前者加(uid, create_time)联合索引后,大部分查询能走索引,但订单量继续涨到千万级后,辅助索引加上聚簇索引,每次回表都可能有随机IO,数据库的IO队列开始堆积;后者因为要扫全表或大范围数据,加了索引也不治本。这个时候,单表的体量已经成为性能上限了。
1.2 数据量上去之后,索引和锁的问题是怎么放大的
单表数据量过亿后,InnoDB的B+树层级通常在3到4层,单纯看查询理论并不慢。真正变慢的原因是索引缓冲命中率下降、页分裂变频繁、写锁粒度扩大。一个数据页16KB,一行订单数据假设1KB,一个页大约16行,1亿行就是600多万个页。Buffer Pool再大,总有大量页需要从磁盘换进换出,热点数据一散,随机读请求就会把磁盘IO打满。
更麻烦的是锁。比如后台按状态批量更新订单,可能在间隙锁上相互等待;某个商户做活动,大量同一用户或同一商家的写操作涌过来,行锁竞争会把事务响应时间拉长。mysql锁表现在几百上千个,活跃连接全部卡在processlist里。这个时候你再去加索引、改SQL,边际收益已经很小。分库分表解决的是“单机资源上限”和“单表数据规模”两个问题,不是所有慢查询都该用这招。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 分库分表方案选型:不是所有拆分都叫水平扩展
2.1 垂直拆分:优先按业务线拆库,按字段冷热拆表
很多人一上来就问取模还是范围,其实在此之前,垂直拆分应该先做。垂直拆分分两步:一是按业务模块把库拆开,订单库、用户库、商品库分到不同实例或集群;二是把单表中的大字段、不常用字段拆到附属表,比如把订单扩展JSON和核心字段拆开,减少每行数据占用的页空间,让同页能容纳更多核心记录。
垂直拆分见效快,但解决不了核心问题:单表行数还在线性增长。所以它通常作为水平拆分的前置动作。我见过不少团队把订单表按扩展字段拆了,主表还是几亿行,照样慢。记住:垂直拆分是给表瘦身,水平拆分才是真正做扩展。
2.2 水平拆分:分片键怎么选,取模和范围如何取舍
水平拆分最关键的不是怎么分表,而是选分片键。分片键决定每次SQL能不能落到一个可用分片上。拿订单表举
