学完这篇你能判断自己的库到底该不该拆、按什么顺序拆,并能在 ShardingSphere-JDBC 和 Proxy 之间做出有依据的选型。
第一步:先判断到底该不该拆
单表 2000 万行、单库磁盘超 1TB、写入 QPS 长期打满单实例,这三个信号出现任意两个再考虑拆。没到这一步,优先做索引优化、读写分离、冷热分离。
-- 看最大的表
SELECT table_name, table_rows,
ROUND(data_length/1024/1024) AS data_mb,
ROUND(index_length/1024/1024) AS idx_mb
FROM information_schema.tables
WHERE table_schema='your_db'
ORDER BY data_length DESC LIMIT 10;
注意:table_rows 是估算值,InnoDB 下误差可能到 30% 以上,要精确行数用 SELECT COUNT(*)。
第二步:垂直拆分——先按业务切库
垂直拆分的动作是"把一张宽表按列拆开"或"把一个库按业务拆开",它解决的是耦合和 IO 问题,不解决单表行数问题。
垂直分表:把 user 表里的大字段(intro TEXT、avatar BLOB)搬到 user_profile,主表只留高频查询列。
CREATE TABLE user_profile (
user_id BIGINT PRIMARY KEY,
intro TEXT,
avatar VARCHAR(255)
) ENGINE=InnoDB;
INSERT INTO user_profile SELECT id, intro, avatar FROM user;
ALTER TABLE user DROP COLUMN intro, DROP COLUMN avatar;
垂直分库:按域拆成 db_user、db_order、db_content 三个库,各自独立实例。
注意:垂直拆分后跨库 JOIN 直接失效。拆之前先用 slow_query_log 找出所有多表关联查询,改成应用层拼装或冗余字段,否则上线当天就会报错。
第三步:水平拆分——选分片键是生死线
水平拆分把同一张表的数据按规则分散到 N 个物理表。核心只有两个决策:
1)分片键(sharding key):必须满足"高频查询都带它"。订单表的 user_id、消息表的 conversation_id 都是好键;create_time 通常不是好键,因为按时间查的请求会让你扫全部节点。
2)路由算法:取模最均匀但扩容要重分布;按范围(range)扩容方便但容易热点;一致性哈希可以缓解扩容问题。
rules:
- !SHARDING
tables:
t_order:
actualDataNodes: ds_${0..1}.t_order_${0..1}
databaseStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: db_inline
tableStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: tbl_inline
shardingAlgorithms:
db_inline:
type: INLINE
props:
algorithm-expression: ds_${user_id % 2}
tbl_inline:
type: INLINE
props:
algorithm-expression: t_order_${user_id % 2}
全局唯一 ID 必须同步解决,不能再用 AUTO_INCREMENT。三种常见做法:雪花算法(Snowflake,需处理时钟回拨)、号段模式(一次取 1000 个 ID 缓存在内存,DB 只存最大值)、Redis INCR。
注意:分片后 COUNT(*)、ORDER BY ... LIMIT 100000,20、非分片键的 WHERE 都会退化成全节点扫描再内存归并,性能可能比单库还差。分页深翻页建议改成"按上次最大 ID 游标翻页"。
第四步:中间件选型
| 方案 | 形态 | 适合场景 | 代价 |
|---|
| ShardingSphere-JDBC | 客户端 jar,嵌入应用 | Java 应用、追求性能、运维能力弱 | 多语言不通用,升级要动应用 |
| ShardingSphere-Proxy | 独立代理进程 | 多语言混布、想对应用透明 | 多一跳网络,代理层要单独运维 |
| MyCat | 代理 | 存量项目、社区老方案 | 社区活跃度低,不建议新项目 |
| Vitess | 代理 + 编排 | 超大规模、K8s 环境 | 学习曲线陡,组件多 |
选型口诀:**Java 单体优先 JDBC,异构语言或想灰度切流用 Proxy,两者可以共用同一套分片规则**。
落地顺序建议:先做读写分离验证链路 → 再垂直分库 → 最后水平分表。迁移用双写 + 数据校验:
mysqldump -h old_host -u root -p --single-transaction --no-create-info \
--where="id < 10000000" your_db t_order > t_order_part1.sql
注意:--single-transaction 只对 InnoDB 有效,且迁移期间必须停掉 DDL 操作,否则一致性快照会被打断。
小结
- 单表 2000 万行、单库 1TB 是拆分参考线,没到就别拆。
- 垂直拆分解决耦合,不解决数据量;拆完跨库 JOIN 必须先在应用层消化掉。
- 水平拆分的关键是分片键,选错等于白拆;全局 ID 要在拆之前就换成雪花或号段。
- 中间件:Java 优先 ShardingSphere-JDBC,多语言或要透明代理选 Proxy。
- 迁移顺序是读写分离 → 垂直分库 → 水平分表,每步都配双写和数据校验。