分库分表需精准设计sharding key、shard count及跨分片操作方案,否则易致性能下降或数据异常;路由逻辑与事务边界是核心难点,缺一不可。

分库分表不是加个中间件就完事
MySQL横向扩展本质是把单点压力拆开,但拆错比不拆更危险。真实场景里,90%的“分表后变慢”“数据查不到”问题,都出在路由逻辑和事务边界上。
分库分表必须回答三个问题:按什么字段拆(sharding key)、拆成多少份(shard count)、跨分片操作怎么处理。缺一个,后续全是坑。
- 别用
user_id做分片键却让查询总依赖order_no——这会导致全库扫描 -
shard count别拍脑袋定 16 或 32,得结合未来 2 年预估写入量和单实例吞吐反推,比如单机扛不住 5000 QPS,而你预估峰值 4 万,至少得 8 个分片 - 跨分片
JOIN、GROUP BY、ORDER BY在多数分库中间件里要么不支持,要么性能极差,得提前在应用层收口重写逻辑
ShardingSphere-JDBC 的 standard 分片策略怎么配才不翻车
StandardShardingStrategy 是最常用也最容易配错的策略。它只对单个分片键生效,且要求 shardingAlgorithm 返回的值必须是已声明的数据库/表名之一。
常见错误:actual-data-nodes: ds${0..1}.t_order_${0..3} 声明了 2 库 × 4 表,但算法返回 "t_order_5" ——直接报 java.lang.IllegalArgumentException: Cannot find actual data node。
- 分片算法里别用
Math.abs(key.hashCode()) % 4这种看似均匀实则倾斜的逻辑,整数取模前先做key.toString().hashCode()再转为正整数(防负数) - 测试时一定用真实业务数据跑一遍分片路由,验证
sharding_key分布是否均匀,比如导出 10 万条user_id算哈希后统计各分片数量,偏差超 20% 就得调算法 - 配置里
sharding-column必须和 SQL 中WHERE条件里的字段完全一致(大小写敏感),写成userId却在 SQL 里用user_id,路由直接失效
分库后 SELECT COUNT(*) 和 MAX(id) 为什么不准
因为这些聚合函数默认只在单个分片执行,中间件不会自动合并结果——除非你显式开启 sql-show: true 并看到日志里发出了多条语句,否则大概率只查了一个分片。
这不是 Bug,是设计使然:全分片聚合成本高、延迟大,框架默认不做。
- 要准确总数,得用
UNION ALL手动拼多个COUNT(*)查询,或改用 Elasticsearch 同步做聚合 -
MAX(id)类操作同理,如果id是自增主键且未全局唯一,它根本不能代表最新记录——此时应该用带sharding_key的查询 +ORDER BY create_time DESC LIMIT 1 - 某些中间件(如 ShardingSphere)支持
SELECT COUNT(*) FROM t_order自动改写为分布式聚合,但需开启props.sql-show=true并确认版本 ≥ 5.3.0;低于此版本一律视为单分片执行
不停机迁移老库数据到分库分表架构
双写 + 校验 + 切流是最稳妥路径,但关键在“校验怎么写”和“切流断点在哪”。很多团队卡在数据比对阶段,花一周写脚本,结果发现时间戳精度不一致、空字符串和 NULL 判定不同,白忙。
核心原则:校验必须基于业务主键,而非自增 ID;比对逻辑必须和线上应用读取逻辑一致。
- 双写期间,新老库写入用同一套参数,尤其注意
timezone、sql_mode是否一致,否则NOW()结果可能差 8 小时 - 校验脚本不要自己实现 diff,用
pt-table-checksum(Percona Toolkit)或开源工具datax的对比模块,它们已处理 NULL / 时区 / 字符集等边界 - 切流前最后一刻,停写老库 → 等双写队列清空 → 对齐位点 → 切读流量 → 观察 10 分钟无报错 → 切写流量。任何环节跳过,都有概率丢数据
分库分表最难的从来不是技术落地,而是把业务查询模式、数据生命周期、运维响应节奏全盘纳进来算一笔账。少算一个,上线后半夜就会收到告警。











