分库分表不直接提升性能,仅解决单机容量瓶颈;需满足数据量大、写入高、有合适分片键等条件才适用,盲目拆分反致路由失效、全局聚合等新问题。

分库分表不等于性能提升,盲目拆分反而拖慢查询
MySQL 单实例在合理索引、读写分离、连接池优化后,轻松支撑千万级单表。分库分表是为了解决单机容量瓶颈(如磁盘 IO、连接数、主从延迟加剧),不是“一拆就快”。常见误判:看到 SELECT COUNT(*) FROM orders 变慢,立刻想水平拆分——其实加覆盖索引或改用 approximate_row_count(8.0.23+)更有效。
真正该拆的场景:单表超 2000 万行且高频写入 + 范围扫描
满足以下条件再考虑水平拆分:
- 单表数据量持续增长,
ALTER TABLE已需数小时,且innodb_buffer_pool_size无法再扩大 - 写入 QPS 超过 3000,主库 CPU 长期 >75%,且已排除慢查询和锁竞争
- 业务存在固定分片键(如
user_id、tenant_id),能保证 90% 以上查询落在单分片 - 能接受跨分片
JOIN、全局唯一 ID、分布式事务等复杂度上升
例如:SaaS 系统按 tenant_id 分库,每个库内按 order_id % 16 分表,避免租户间数据混杂和热点倾斜。
sharding-jdbc 或 MyCat 不是银弹,路由失效很常见
中间件依赖 SQL 解析做分片路由,但很多写法会导致路由失败,退化为全库广播:
-
WHERE order_id IN (1,2,3,4)→ 正常路由(前提是order_id是分片键) -
WHERE DATE(created_at) = '2024-01-01'→ 全库扫描(函数破坏索引,也破坏分片推导) -
WHERE user_id = ? ORDER BY created_at LIMIT 10 OFFSET 1000→ 单分片执行,但深分页仍慢 -
SELECT * FROM order WHERE status = 'paid' GROUP BY province→ 全局聚合,中间件拉取所有分片结果再合并
真实压测中,约 35% 的慢查询源于路由失效而非分片本身。建议上线前用 sharding-jdbc 的 sql.show=true 日志确认每条 SQL 是否精准命中单分片。
自增主键必须放弃,snowflake 或 号段模式 才可靠
分库分表后,不能再依赖 AUTO_INCREMENT,否则各分片 ID 冲突。两种主流方案:
-
snowflake:毫秒时间戳 + 机器 ID + 序列号,时钟回拨会出错,适合 Java 生态,需维护 worker ID 分配 -
号段模式(如美团Leaf):DB 表存当前号段范围(max_id=1000000),每次取 1000 个 ID 缓存,降低 DB 压力;但宕机可能丢 ID,需容忍“不连续”
别用 UUID——字符串长度大、无序插入导致 InnoDB 页分裂严重,实测写入吞吐下降 40% 以上。也别用 REPLACE INTO 模拟唯一 ID,高并发下死锁率陡增。
最易被忽略的一点:分库分表后,EXPLAIN 失效,SHOW PROFILE 只显示中间件耗时,真正的慢在分片内执行阶段。必须在每个物理库上单独抓慢日志,并开启 log_slow_slave_statements 查看从库实际执行情况。



















