分库分表前必须确认三件事:单库单表是否真成瓶颈、写入qps超3000且主从延迟>5s才考虑分库、单表行数超5000万且查询无法稳定走索引才考虑分表;无全局唯一id不得分表;sharding-key须高覆盖率+高区分度,忌低基数或时间字段滥用;中间件配置须严谨,跨分片聚合需预计算或业务妥协。

分库分表前必须确认的三件事
不加判断直接分库分表,90% 会把问题变得更糟。先问清楚:单库单表是否真成了瓶颈?很多所谓“慢”,其实是缺少索引、未走覆盖索引、或应用层 N+1 查询导致的,和数据量关系不大。
确认真实瓶颈的方法只有两个:EXPLAIN 看执行计划 + slow_query_log 抓真实慢 sql;同时观察数据库的 Threads_running 和 Innodb_row_lock_waits 是否持续偏高。如果锁等待多、连接数满、但磁盘 I/O 和 CPU 都没打满,大概率是设计或查询问题,不是分片问题。
- 业务写入 QPS 超过 3000 且持续增长,单实例主从同步延迟 > 5s —— 才值得考虑分库
- 单表行数 > 5000 万,且
WHERE条件无法稳定命中索引(比如总带OR或函数)—— 才值得考虑分表 - 没有全局唯一 ID 生成机制(如
UUID_SHORT()、Twitter Snowflake或自建号段服务),别碰分表
sharding-key 选错等于白干
分片键(sharding key)不是“哪个字段用得最多就选哪个”,而是“哪个字段能保证 90% 以上查询都带它、且分布足够离散”。常见错误是选 user_id 却忽略“大 V 用户”导致热点;或者选 create_time 导致新数据全挤在少数节点上。
真正靠谱的策略是:先统计高频查询的 WHERE 条件组合,再看哪些字段具备高区分度 + 高覆盖率。比如电商订单表,order_id 全局唯一但业务查询极少用它查;而 buyer_id 查询频次高,但存在头部用户占 20% 订单的情况 —— 这时就得加一层哈希(如 MOD(buyer_id, 1024))再分片,避免倾斜。
- 绝对不要用
status、type这类低基数字段做分片键 - 时间类字段慎用范围分片(range sharding),除非你明确只查最近 N 天,且能接受跨分片归并
- 如果业务强依赖
JOIN或跨用户查(如后台报表),说明分片键设计失败,得退回去重想
MyCat / ShardingSphere 的配置陷阱
这两个中间件看着配置简单,但几个关键参数一设错,上线就出事。sql.show 必须开,否则连路由日志都看不到;props.sql-show 在 ShardingSphere 5.x 后改名了,设错就等于盲操作。
最常踩的坑是 default-database-strategy 和 default-table-strategy 没配一致,导致有的 SQL 路由到 A 库 A 表,有的却去了 A 库 B 表;还有就是 master-slave 配置里漏掉 load-balance-algorithm-type: round_robin,结果所有读请求全压在第一个从库上。
-
sharding-column名称必须和 SQL 中写的完全一致(大小写敏感),别写成user_id却在 SQL 里用userId - ShardingSphere 的
spring.shardingsphere.props.sql-show=true只在开发环境开,生产务必关,否则日志刷爆磁盘 - MyCat 的
scheme.xml里dataNode和dataHost必须一一对应,IP 写错一个,整个节点不可用
跨分片 count / ORDER BY 的现实解法
别信文档里“自动合并”的说法。真实场景下,select COUNT(*) FROM order WHERE status = 1 会发到所有分片,再在中间件里累加 —— 数据量大时,网络传输和内存聚合本身就成了瓶颈。更麻烦的是 ORDER BY create_time LIMIT 20,10:每个分片返回 30 条,中间件合并后取第 20–30 条,结果错得离谱。
可行方案只有两个:一是用异步任务定期预计算(比如每小时跑一次 INSERT INTO stat_order_count SELECT status, COUNT(*) ... GROUP BY status 到汇总库);二是业务妥协,比如把“最新 100 条订单”改成“最近 1 小时内最新 100 条”,用时间分片兜底。
- 禁止在分片环境下用
DISTINCT+ 多字段组合,去重逻辑中间件扛不住 -
union ALL可以用,但必须确保每个子查询都带上分片键条件,否则变成广播查询 - 如果业务真要精确分页,老老实实加
create_time + id复合索引,用游标分页(WHERE create_time )
分库分表不是加个中间件就完事。最难的部分永远不在怎么切,而在怎么让业务接受“不能随便 JOIN、不能随便 COUNT、不能随便跨天查”。这些约束得在拆之前就和产品、前端对齐清楚,不然上线后改需求比重构还疼。