分库分表这事儿,我一直觉得是MySQL性能优化里最“重”的一招。很多人一听到性能瓶颈,脑子里第一反应就是分库分表,但真正动手的时候才发现,这玩意儿不是加几个表那么简单。选分片键、定分片算法、扩容迁移、跨分片查询,每一步都在挑战你对数据分布的理解。这篇文章我把这几年在项目里做分库分表踩过的坑、沉淀下来的方案、还有实际配置参数完整拆开来讲,希望能让你少走几步弯路。
1. 什么时候才需要分库分表:先搞清楚瓶颈在哪里
1.1 用数据说话:从QPS和存储量两个维度做预判
MySQL的瓶颈通常分成两种:一种是写并发太高,单库的连接数和锁竞争让你撑不住;另一种是数据量太大,单表几千万上亿行之后,即使走索引,B+树的层数变高,随机IO也明显变慢。很多人在表只有几百万行的时候就开始喊着要分库分表,这就属于过度设计。
我一般用两个数字做判断:
- 日增数据量 × 预计留存周期,如果单表行数超过5000万~1亿,就需要认真考虑水平拆分了。这个数字不是拍脑袋拍的,InnoDB聚簇索引的B+树在千万级以下基本还能保持三层以内,超过这个量级,索引维护代价和查询IO都会非线性恶化。
- 峰值写QPS超过5000~10000,单主库的binlog落盘、刷脏页、锁竞争都会报警,这时候需要的是分库,把写压力分散到多个实例上。
这个判断标准不是绝对的,但你至少要先拿监控数据看一眼,别凭感觉。如果单表才几百万行、QPS才几百,那要做的不是分库分表,而是先把慢查询日志拉出来看,是不是少建了索引、是不是有全表扫描、是不是没用上覆盖索引。真正合适的顺序是:索引优化 -> 缓存(Redis) -> 读写分离 -> 分库分表。分库分表是最后的手段,因为它的运维复杂度和业务侵入度都是最高的。
1.2 被忽视的前置优化:分库分表是最后的手段
我在很多项目里看到一个现象:表确实大了,但大部分慢查询是SQL本身写得有问题。比如查询条件里对索引列用了函数运算、在LIKE前面加了通配符、OR条件没有用UNION拆开、分页深翻页用了LIMIT 100000, 20这种写法。这些场景下,哪怕你把表分成128片,该慢还是慢。
所以我给团队定的规矩是:**先做一轮完整的SQL走查和索引优化,再看是否真的需要分库分表。**具体来说:
- 打开慢查询日志(
slow_query_log = ON,long_query_time = 1),采集一周的慢SQL。 - 用
EXPLAIN逐个分析执行计划,重点看type是否为ALL(全表扫描)、key是否使用了索引、rows扫描了多少行。 - 对高频查询做覆盖索引优化,尽量让索引包含所有SELECT的列,减少回表。
- 把分页改造成基于游标的方式,用
WHERE id > ? ORDER BY id LIMIT ?代替LIMIT offset。
这一套做下来,很多原本以为要分库分表的系统,硬生生把压力降了一半以上。只有当这些手段全部用尽、瓶颈依然存在时,才进入分库分表的方案评估阶段。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 分库分表方案怎么选:垂直拆、水平拆、中间件选型
2.1 垂直拆分:把大表按业务边界拆开
垂直拆分有两种理解。一种是按字段拆:一张表字段太多,比如有60个字段,其中只有20个是高频查询字段,另外40个是很少用到的大字段,那就把高频字段和低频字段拆成两张表,通过主键关联。这种做法的核心收益是让单行的存储变小,一个数据页能容纳更多行,查询时扫描的页更少,IO成本下降。
另一种是按业务库拆:原本所有表都在一个库里,现在按业务边界拆成用户库、订单库、商品库。这样做的好处是可以针对不同业务的负载特征做差异化配置,比如把订单库放在高性能SSD上,把日志库放到普通机械盘上;坏处是跨库的JOIN彻底没戏了,你需要在应用层做数据聚合,或者在冗余字段上做文章。
垂直拆分相对容易落地,因为它不会改变单表的数据分布逻辑,只是把表从一个库挪到另一个库。它解决的是**“单库压力大”**的问题,不是“单表数据量大”的问题。如果你的瓶颈是单库连接数满了但单表数据量不大,垂直拆分(也就是拆库)往往就够用了。
2.2 水平拆分:让核心表的数据分散出去
水平拆分才是真正意义上的“分表”。比如订单表从逻辑上还是一个 orders,物理上却变成 orders_0、orders_1、...、orders_15,一共16张表,分布在4个库上。每条订单根据分片键(通常是订单ID或用户ID)计算出一个哈希值,再取模落到对应的表里。
水平拆分的核心优势是:
- 单表数据量降下来了,索引层数变低,查询性能恢复。
- 写入并发分散到多库多表,锁竞争和磁盘竞争都降下来了。
- 可以线性扩展,当压力上来后,可以再增加分片数。
代价同样明显:**所有操
