分库分表前必须确认容量超限(单表>2gb或行数>500万)、业务可划分明确拆分维度、sql不依赖跨分片join/全局自增/count(*);拆分键不可频繁更新,需去除外键,分页改游标;shardingsphere配置须确保sharding-column与algorithm-expression变量名严格一致;迁移需双写、校验、切流三步,禁用直接drop;in查询、批量插入、无分片键update、max等sql在分库后行为异常。

分库分表前必须确认的三个前提条件
没做容量评估就硬拆,90%会翻车。先看 SHOW TABLE STATUS LIKE 'orders' 的 Data_length 和 Index_length,单表超 2GB 或行数超 500 万,才值得考虑物理拆分;业务上要能明确划分维度(比如按 user_id 或 order_no 取模/范围路由);现有 SQL 必须基本不依赖跨分片 JOIN、全局唯一自增 ID、SELECT COUNT(*) 全表统计——这些在分库后要么失效,要么代价极高。
- 拆分键不能是频繁更新的字段(如
status),否则路由不稳定 - 现有外键约束必须全部去掉,MySQL 分库后无法保证跨库一致性
- 所有带
ORDER BY ... LIMIT的分页查询,需改写为基于游标(cursor-based)方式,否则结果错乱
用 ShardingSphere-JDBC 做透明分片的实际配置要点
ShardingSphere-JDBC 是目前最轻量、侵入最小的选择,但配置稍有偏差就会路由失败或全库扫表。核心是 sharding-strategy 中的 sharding-column 和 sharding-algorithm 对齐。
spring:
shardingsphere:
rules:
- !SHARDING
tables:
t_order:
actual-data-nodes: ds${0..1}.t_order_${0..3}
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: t_order_inline
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: ds_inline
sharding-algorithms:
t_order_inline:
type: INLINE
props:
algorithm-expression: t_order_${order_id % 4}
ds_inline:
type: INLINE
props:
algorithm-expression: ds${user_id % 2}
-
algorithm-expression中的变量名必须和sharding-column完全一致(大小写敏感) -
actual-data-nodes的表达式要覆盖所有可能的组合,漏掉会导致插入时找不到目标节点 - 如果用了
ORDER BY user_id,而user_id不是分库键,ShardingSphere 会自动归并结果,但性能下降明显,应避免
迁移中如何安全替换老表而不停服
直接 DROP TABLE t_order 然后建分片逻辑表?不行。得走双写 + 校验 + 切流三步,每步都可能卡住。
- 第一阶段:应用同时往旧单表和新分片表写(用监听 binlog 或应用层双写),新写入走分片逻辑,历史数据用工具同步(推荐
gh-ost或pt-online-schema-change配合自定义导出脚本) - 第二阶段:用
SELECT COUNT(*)和CHECKSUM TABLE对比关键分片的数据一致性,注意CHECKSUM在不同 MySQL 版本行为不一致,5.7+ 推荐用SELECT MD5(GROUP_CONCAT(...))聚合校验 - 第三阶段:切读流量前,先关掉旧表的写入,等双写延迟归零再切换,否则会出现短暂数据丢失
分库后那些突然失效的 SQL 写法
不是所有 SQL 都能原样跑通。以下写法在分库环境下会出错或结果异常:
-
SELECT * FROM t_order WHERE order_id IN (1,2,3,4):如果这 4 个 ID 分布在不同库,ShardingSphere 默认只路由到一个库,查不到全部结果 -
INSERT INTO t_order VALUES (...), (...), (...):批量插入若跨分片,会被拆成多条单条语句执行,性能暴跌 -
UPDATE t_order SET status=1 WHERE create_time > '2023-01-01':没有分片键条件,触发广播执行,所有分片都扫一遍 -
SELECT MAX(order_id) FROM t_order:结果只是各分片最大值中的最大者,不是全局最大值,需要用SELECT MAX(order_id) FROM (SELECT MAX(order_id) FROM t_order GROUP BY ds_id) t二次聚合
分片键设计一旦定死,后续调整成本极高;路由规则上线前,务必用真实流量抽样压测,观察慢查询日志里是否出现 logic SQL 和 actual SQL 数量严重不匹配的情况。











