单表数据量过亿、单库连接数打满,是MySQL数据库分库分表最典型的触发信号。分库分表解决的是容量和并发问题,但引入的复杂度不小。本文从拆分时机、分片键选择、中间件选型到存量数据迁移,给出完整落地路径。
什么时候该分库分表
先别急着拆,按两个指标判断:
容量维度:单表数据超过500万-1000万,或表容量逼近2TB,B+树层级变深,写入和查询明显变慢。
并发维度:单库连接数长期接近上限,或热点表写入锁竞争严重。
如果只是读压力大,优先上缓存(Redis)和读写分离,分库分表放在最后考虑。拆完的数据多副本、跨库查询、分布式ID、事务问题都会冒出来,复杂度是几何级上升的。
垂直拆分与水平拆分
垂直拆分按业务域拆表:把用户表、订单表、商品表分到不同库,顺带把字段拆成主表+扩展表,解决行宽问题。水平拆分把同一张表的数据按规则分到多库多表,容量翻倍。常见做法:
# 示例:订单表按 user_id 取模分16库,每库16表(共256表)
分库: order_db_{0..15} 路由键 user_id % 16
分表: order_{0..15} 路由键 user_id / 16 % 16
分片键(sharding key)必须从业务访问模式里选,通常用最频繁的等值查询字段:用户体系用user_id,订单体系用order_id。选错分片键会导致大量跨库查询,性能反而更差。
分库分表中间件选型
ShardingSphere-JDBC:应用内嵌入式,性能最好,适合新项目直接集成。
ShardingSphere-Proxy:独立代理,兼容性强,适合已有系统平滑改造。
MyCat:老牌代理,支持多语言客户端,功能偏传统。
新系统建议ShardingSphere-JDBC(代码级控制),存量系统改造用Proxy降低侵入。分片配置示例(Java):
# sharding.yaml 订单库分片配置
rules:
- !SHARDING
tables:
order:
actualDataNodes: order_db_${0..15}.order_${0..15}
tableStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: mod16
defaultDatabaseStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: mod16
分页与跨库查询的处理
分库分表后,不带分片键的查询需要广播到全部分片再聚合,业务要设计降级路径:
1. 强制业务查询带分片键(user_id),走单分片。
2. 不带分片键的列表页走ES(Elasticsearch)或宽表副本。
3. 跨分片排序,中间件合并后内存排序,数据量大时提前limit。
最忌讳的就是”全表扫分片+内存聚合”,并发一上来直接打爆库。
存量数据迁移:双写+影子表
存量数据迁移是分库分表里风险最高的步骤,推荐”全量+增量+双写校验”方案:
1. 目标库建好分片表(影子表)。
2. 用DataX、canal等工具全量同步历史数据。
3. 业务双写:写新库的同时在影子表同步一份,开启binlog订阅校验。
4. 校验通过后切换读流量,再切写流量。
5. 保留旧库一段时间的只读,灰度回滚。
# 示例:canal监听binlog同步增量
canal.instance.master.address=127.0.0.1:3306
canal.instance.filter.regex=orderdb\\.order_.*
迁移期间用影子表对比每一条数据的MD5或版本号,确保两端一致再切流。
分库分表后的数据一致性
跨库跨表的事务,老方案靠分布式事务(XA/Seata),新方案尽量拆事务。维护成本从”拆表”转向”拆业务”,把一次性大事务拆成多个最终一致的小步骤。同时建立分片监控:各分片的数据分布、热点分片、慢SQL,第一时间发现倾斜问题。
分库分表只是手段,不是目的。数据量大先归档冷数据,读压力大先上缓存和读写分离,把这些做干净再评估要不要动库,投入产出比高得多。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/mysql-fen-ku-fen-biao-fang-an-shi-zhan-cong-chai-fen-ce/