← 返回场景决策

🔷 ShardingSphere 分库分表实战

结果导向 · 分片策略 / 读写分离 / 扩容 / 跨库问题 / 分布式 ID

分库分表 ShardingSphere-JDBC 分片路由 读写分离 分布式ID 扩容 跨库问题

① 直接给结论

ShardingSphere 是什么(一句话):Apache 顶级项目,一套 JDBC 增强层,让你的 Java 应用透明地操作多个数据库实例——你写 SQL 像操作单库一样,它自动帮你路由、改写、合并结果。不需要改业务代码。

什么时候需要分库分表(信号清单):

信号说明阈值参考
单表数据量过大MySQL 单表超过一定行数,B+ 树层级变深,查询变慢单表 > 1000 万行 / 单表 > 50GB
单库写 QPS 过高MySQL 单实例写能力有上限,主从延迟拉大写 QPS > 5000 / 主从延迟 > 1s
单库磁盘吃紧数据增长快,磁盘扩容跟不上磁盘使用率 > 70%
单库连接数打满应用多、连接池大,单库 max_connections 扛不住连接数 > 2000
慢查询比例上升索引优化已到极限,还是慢慢查询占比 > 5%
⚠️ 重要原则:能不分就不分。分库分表是最后的手段,不是上来就用的。在分之前,先穷尽这些优化:①加索引 ②SQL 调优 ③读写分离 ④加 Redis 缓存 ⑤归档冷数据 ⑥升级硬件(SSD/内存)。这些做完还扛不住,才上分库分表。原因:分库分表后跨库 JOIN、分布式事务、跨库分页全是麻烦事,运维成本翻倍。
一句话选型:Java 项目要分库分表 → ShardingSphere-JDBC(嵌入式,无额外进程,性能好)。不用 MyCat(已过时,中间件模式有性能瓶颈)。不需要分库分表只是读写分离 → ShardingSphere 也能做,或者干脆用 MyCAT 替代品们。

② ShardingSphere 是什么 + 能力全景

定位:ShardingSphere 是一个 JDBC 增强层(不是数据库,不是中间件进程)。你的 Java 应用引入它的 jar 包后,DataSource 由它接管,所有 SQL 都先过它一遍——它根据分片规则把 SQL 路由到正确的库和表,执行后把多个库的结果合并回来。对业务代码来说,就像操作一个单库

关键认知:ShardingSphere 有两种部署模式
ShardingSphere-JDBC(推荐):嵌入式 jar 包,每个应用实例内置。无额外进程,性能最好。适合 Java 项目。
ShardingSphere-Proxy:独立部署的代理进程,应用连 Proxy 就像连 MySQL。多语言通用,但多一跳网络开销。适合非 Java 或 DBA 统一管理场景。
ShardingSphere-JDBC 工作原理
Java 应用 ShardingSphere-JDBC
(jar 包,进程内)
真实库 ds0 真实库 ds1 真实库 ds2 真实库 ds3
应用写 SELECT * FROM order WHERE id=123 → ShardingSphere 根据分片规则算出 id=123 在 ds1 → 只查 ds1 → 返回结果

核心能力全景(6 大块):

✂️
分库分表(分片)
按分片键把数据拆到多个库多个表。支持行表达式、标准、复合、Hint 四种分片算法。
📖
读写分离
写主库、读从库。自动识别写语句走主库,读语句走从库,从库可配多个做负载。
🔐
分布式事务
支持 XA、BASE、LOCAL 三种事务模式。跨库操作要么全成功要么全回滚。
🎭
数据加密
透明数据加密,写入自动加密、读取自动解密。密文存储,应用无感知。
🌙
影子库/压测
压测流量自动路由到影子库,不污染生产数据。全链路压测利器。
📊
分布式治理
配置中心(Nacos/Zookeeper)动态修改分片规则,不用重启应用。
PRO TIP ShardingSphere 最大的价值是"透明"。你的业务代码完全不用改——JdbcTemplateMyBatisJPA 该怎么用还怎么用。SQL 里的表名还是 order(逻辑表),ShardingSphere 自动改写成 order_0 order_1 等真实表。这是它和自研分库分表最大的区别——自研要改代码,它不用。

③ ShardingSphere vs 其他方案

分库分表的方案主要有:ShardingSphere、MyCat(老牌)、Vitess(Google 出品)、TDDL(阿里自用)、自研。结果导向直接看下表:

维度 ShardingSphere-JDBC ShardingSphere-Proxy MyCat Vitess 自研
类型 嵌入式 jar 代理进程 代理进程 代理进程 应用层
性能 ✅ 最好(无额外跳转) ⚠️ 多一跳网络 ⚠️ 多一跳网络 ⚠️ 多一跳网络 ✅ 最好
语言支持 仅 Java ✅ 多语言(MySQL 协议) ✅ 多语言 ✅ 多语言 看实现
维护状态 ✅ Apache 顶级项目,活跃 ✅ 同上 ❌ 基本停更 ✅ 活跃(CNCF) 自己维护
运维成本 ✅ 最低(无额外进程) ⚠️ 要维护 Proxy 集群 ⚠️ 要维护 MyCat ⚠️ 复杂,要 etcd ❌ 最高
什么时候选它 ✅ Java 项目首选 多语言/DBA 统一管理 ❌ 不推荐(过时) K8s 环境/超大规模 特殊需求/学习
实际怎么选:Java 项目 → ShardingSphere-JDBC(90% 场景都是它)。多语言团队或 DBA 要统一管理 → ShardingSphere-Proxy。K8s 原生 + 超大规模 → Vitess。不要用 MyCat——已经基本停更,社区没人维护,出了问题没人帮你。

④ 分库分表策略(怎么拆)

分库分表不是随便拆,要先想清楚两个问题:①按什么字段拆(分片键)?②按什么规则拆(分片算法)?这两个选错,后面全是坑。

4.1 拆分维度:垂直 vs 水平

垂直分库 = 按业务拆库 user 库 order 库 product 库
一个库变多个库,每个库放不同业务的表。类似微服务按业务拆服务。
垂直分表 = 按字段拆表 user_basic (id/name) user_detail (id/bio/avatar)
一张表的字段拆成多张表。把不常用的大字段拆出去,常用字段变小表查询快。
水平分库 = 同结构表分到不同库 ds0.order ds1.order ds2.order
同一张表的数据按行分到不同库。解决单库数据量/连接数/磁盘问题。
水平分表 = 同库内拆多张表 ds0.order_0 ds0.order_1 ds0.order_2
同一个库里把一张表拆成多张。解决单表数据量过大的问题(不增加库实例)。
实际怎么用:最常见的是 水平分库 + 水平分表 组合(也叫"分库分表")。比如订单表按 user_id 分到 4 个库,每个库再分 4 张表,总共 16 个物理表。垂直分库通常配合微服务化先做了,不在 ShardingSphere 这层做。

4.2 分片键怎么选(最关键的决策)

分片键就是"按哪个字段拆数据"。选错了,所有查询都会变成全库广播(查所有库再合并),性能灾难。

分片键适合场景优点
user_id(用户ID)订单/消息/购物车等"用户维度"数据✅ 90% 查询带 user_id,单库命中某些用户数据量异常大时倾斜
order_id(订单ID)按订单查询为主✅ 订单查询精准路由❌ 按 user_id 查订单时广播
create_time(创建时间)日志/流水类按时间归档✅ 时间范围查询精准❌ 热点:最新数据全在一个分片
id(主键)无明确业务维度的数据✅ 分布均匀❌ 业务查询几乎不带 id,全广播
选分片键的一句话原则:选查询条件里"几乎每次都带"的字段。订单系统每次查询都带 user_id("我的订单"),选 user_id。商品系统每次都带 category_id("分类浏览"),选 category_id。选错分片键 = 分库分表失败的一半。

4.3 分片算法(4 种)

算法规则例子适合
取模分片键 % 分片数user_id % 4 = 0/1/2/3数据分布均匀,但扩容要迁移大量数据
范围按区间分片id 1-1000万→ds0,1000万-2000万→ds1扩容简单(新加区间),但热点集中
一致性 hashhash 环hash(user_id) 映射到环上✅ 扩容只迁移一部分数据(推荐)
Hint代码里手动指定走哪个库HintManager.set("ds1")特殊场景,灵活但侵入业务
PRO TIP 取模 vs 一致性 hash,怎么选?取模分布最均匀,但扩容是噩梦——从 4 库扩到 8 库,几乎所有数据都要重新分布(4%4=0 但 4%8=4)。一致性 hash 扩容时只迁移一部分数据,生产推荐一致性 hash。如果用取模,规划时要预留倍数扩容(4→8→16,每次翻倍,迁移量可控)。

⑤ 核心配置实战

5.1 Spring Boot 引入 ShardingSphere-JDBC

pom.xml
<!-- ShardingSphere-JDBC 5.x --> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-jdbc-core</artifactId> <version>5.4.1</version> </dependency> <!-- 连接池(ShardingSphere 用的还是 HikariCP)--> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </dependency>

5.2 分库分表配置(最核心)

application-sharding.yml
📁 resources/
application.yml
⭐ application-sharding.yml
mapper/
📁 java/
OrderMapper.java
OrderService.java
spring: shardingsphere: datasource: names: ds0,ds1,ds2,ds3 # 4 个真实数据源 ds0: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://10.0.0.1:3306/order_db username: root password: xxx ds1: { jdbc-url: jdbc:mysql://10.0.0.2:3306/order_db, ... } ds2: { jdbc-url: jdbc:mysql://10.0.0.3:3306/order_db, ... } ds3: { jdbc-url: jdbc:mysql://10.0.0.4:3306/order_db, ... } rules: sharding: tables: t_order: # 逻辑表名 actualDataNodes: ds${0..3}.t_order_${0..3} # 4库×4表=16张物理表 databaseStrategy: # 分库策略 standard: shardingColumn: user_id shardingAlgorithmName: db_mod tableStrategy: # 分表策略 standard: shardingColumn: user_id shardingAlgorithmName: table_mod keyGenerateStrategy: # 分布式ID column: order_id keyGeneratorName: snowflake shardingAlgorithms: db_mod: type: MOD # 取模算法 props: { sharding-count: 4 } table_mod: type: MOD props: { sharding-count: 4 } keyGenerators: snowflake: type: SNOWFLAKE # 雪花算法生成分布式ID props: { worker-id: 123 } props: sql-show: true # 打印真实路由SQL(开发期开,生产关)
这段配置在说什么:①定义 4 个真实数据库 ds0-ds3;②逻辑表 t_order 映射到 16 张物理表(ds0-3 各 4 张);③按 user_id 取模分库分表(user_id % 4 决定库,再 % 4 决定表);④用雪花算法自动生成 order_id。配完后,业务代码里写 SELECT * FROM t_order WHERE user_id=123,ShardingSphere 自动路由

5.3 读写分离配置

application-readwrite.yml
spring: shardingsphere: datasource: names: master,slave0,slave1 master: { jdbc-url: jdbc:mysql://10.0.0.1:3306/db, ... } slave0: { jdbc-url: jdbc:mysql://10.0.0.2:3306/db, ... } slave1: { jdbc-url: jdbc:mysql://10.0.0.3:3306/db, ... } rules: readwrite-splitting: dataSources: readwrite_ds: writeDataSourceName: master # 写主库 readDataSourceNames: [slave0, slave1] # 读从库(轮询) transactionalReadQueryStrategy: PRIMARY # 事务内读走主库 loadBalancerName: round_robin
读写分离的关键认知:ShardingSphere 自动识别 SQL 类型——INSERT/UPDATE/DELETE 走主库,SELECT 走从库。但有个坑:事务内的 SELECT 也会走主库(因为从库有延迟,刚写完马上读可能读不到)。配 transactionalReadQueryStrategy: PRIMARY 就是干这个的,这是必须配的,否则"写完读不到"的 bug 会让你排查到怀疑人生。

5.4 业务代码(完全不用改)

OrderMapper.java
@Mapper public interface OrderMapper { // 写入:ShardingSphere 自动生成 order_id(雪花),按 user_id 路由到对应库表 @Insert("INSERT INTO t_order(user_id, amount, status) VALUES(#{userId}, #{amount}, 0)") int insert(Order order); // 查询:带 user_id → 精准路由到单个库表 @Select("SELECT * FROM t_order WHERE user_id = #{userId} AND order_id = #{orderId}") Order findByUserAndOrder(long userId, long orderId); // ⚠️ 不带 user_id → 全库广播!慎用 @Select("SELECT * FROM t_order WHERE status = 0 LIMIT 10") List<Order> findPending(); }
PRO TIP 分库分表后,查询尽量带分片键WHERE user_id=? 精准路由一个库;不带 user_id 的查询会广播到所有库,性能差 4 倍以上(4 个库的情况)。后台管理类的全量查询(比如"查所有待处理订单")建议走独立的非分片库(用 Canal 同步一份数据),不要走分片库。

⑥ 生产场景(3 个带界面演示)

场景 1:订单表分库分表(user_id 取模,4库×4表)

背景:电商订单表 3 年积累 8000 万行,单表查询慢、单库磁盘吃紧。按 user_id 分到 4 个库,每个库 4 张表,共 16 张物理表,单表降到 500 万行,查询快了 10 倍。

ShardingSphere SQL 路由日志(sql-show: true 开启后看到的):
🔷 ShardingSphere 路由日志 SQL 路由 数据分布 实时 ▾
📝 实际执行的 SQL(逻辑SQL → 路由到真实库表):
Logic SQL: SELECT * FROM t_order WHERE user_id = 10086
Actual SQL: SELECT * FROM t_order_2 WHERE user_id = 10086 → ds2.t_order_2
✅ 精准路由:user_id=10086 % 4 = 2 → ds2,% 4 = 2 → t_order_2。只查 1 个库 1 张表。
Logic SQL: SELECT * FROM t_order WHERE user_id IN (10086, 10087, 10088)
Actual SQL: SELECT * FROM t_order_2 WHERE user_id IN (10086, 10088) → ds2.t_order_2
Actual SQL: SELECT * FROM t_order_3 WHERE user_id IN (10087) → ds3.t_order_3
⚠️ 多分片路由:3个user_id落2个库,ShardingSphere自动合并同库IN查询。
Logic SQL: SELECT * FROM t_order WHERE status = 0 LIMIT 10
Actual SQL: SELECT * FROM t_order_0 WHERE status = 0 LIMIT 10 → ds0.t_order_0
Actual SQL: SELECT * FROM t_order_1 WHERE status = 0 LIMIT 10 → ds1.t_order_1
Actual SQL: SELECT * FROM t_order_2 WHERE status = 0 LIMIT 10 → ds2.t_order_2
Actual SQL: SELECT * FROM t_order_3 WHERE status = 0 LIMIT 10 → ds3.t_order_3
🔴 全库广播!不带分片键 → 16张表全查 → 合并结果。慎用!

数据分布情况(分片后各库表数据量):

📊 数据分布 按库统计 按表统计 8000 万行总量 ▾
总数据量
8000万
分片前
单表数据量
500万
分片后
查询耗时
45ms
原 520ms
单库磁盘
12GB
原 48GB
4 个库数据分布(取模算法,基本均匀):
ds0
2,001万
ds1
1,984万
ds2
2,008万
ds3
2,007万
分片效果:单表从 8000 万降到 500 万,查询从 520ms 降到 45ms(10 倍提升)。磁盘从单库 48GB 降到单库 12GB。取模算法数据分布基本均匀(差异 <1%)。

场景 2:读写分离 + 主从延迟排查

背景:订单系统读写分离后,用户反馈"下单成功后立刻看订单列表,有时看不到"。典型主从延迟问题——写主库后从库还没同步,读从库读不到。

📖 读写分离监控 流量分布 主从延迟 最近 1 小时 ▾
主库 QPS(写)
1,240
INSERT/UPDATE/DELETE
从库0 QPS(读)
8,420
轮询分担
从库1 QPS(读)
8,180
轮询分担
主从延迟
1.8s
⚠️ 偏高
SQL 路由分布:
INSERT INTO t_order ...master ✓ 写主库
SELECT * FROM t_order WHERE user_id=?slave0 ✓ 读从库
SELECT * FROM t_order WHERE user_id=?slave1 ✓ 轮询
SELECT * FROM t_order WHERE user_id=? (事务内)master ⚠️ 事务内读走主库
主从延迟趋势(最近 1 小时):
01:00 ▁▁▂▂▃▁▁ 平均 0.3s ✅
01:15 ▃▄▅▆▇▆▅ 平均 1.2s ⚠️
01:30 ▇█▇█████ 平均 1.8s 🔴
01:45 ▆▅▄▃▂▃▂ 平均 0.8s ⚠️
⚠️ 大促期间主库写压力大,从库同步跟不上,延迟最高 1.8s
解决方案(3 种,按推荐度排序):
// 方案1(推荐):关键读操作强制走主库 public Order createOrderAndReturn(Order order) { orderMapper.insert(order); // 写主库 // 强制接下来读主库(HintManager) try (HintManager hint = HintManager.getInstance()) { hint.addDatabaseShardingValue("t_order", order.getUserId()); hint.setMasterRouteOnly(); // ← 关键:强制走主库 return orderMapper.findByUserAndOrder(order.getUserId(), order.getOrderId()); } } // 方案2:@Transactional 内的读自动走主库(配置 transactionalReadQueryStrategy: PRIMARY) @Transactional public Order createAndQuery(Order order) { orderMapper.insert(order); return orderMapper.findByUserAndOrder(...); // 事务内自动走主库 } // 方案3:前端短暂延迟(简单粗暴,不推荐) // 下单成功后前端等 500ms 再刷新列表
PRO TIP 主从延迟是读写分离的"原罪",没法彻底消除,只能规避。原则:写完立刻要读的场景,读走主库(用 HintManager 或 @Transactional)。不要求强一致的场景(比如别人的订单)走从库没问题。大促期间延迟会拉大,提前压测从库同步能力。

场景 3:扩容(4 库 → 8 库,翻倍扩容)

背景:业务增长,4 个库扛不住了(单库 QPS 5000 接近上限),要扩到 8 个库。取模算法扩容的痛点:user_id % 4 和 user_id % 8 结果不同,几乎所有数据都要重新分布

🔄 扩容任务 4库→8库 迁移进度 扩容进行中 ▾
总数据量
8000万
待迁移
已迁移
4823万
60%
迁移速度
12万
行/分钟
预计完成
4.3h
剩余
扩容前后数据分布对比(取模算法翻倍扩容):
扩容前 (4库):
user_id % 4 = 0 → ds0 (2000万)
user_id % 4 = 1 → ds1 (2000万)
user_id % 4 = 2 → ds2 (2000万)
user_id % 4 = 3 → ds3 (2000万)
扩容后 (8库): 每个旧库一半数据要迁到新库
user_id % 8 = 0 → ds0 (1000万) ← 留在原库
user_id % 8 = 4 → ds4 (1000万) ← 从ds0迁来
user_id % 8 = 1 → ds1 (1000万) ← 留在原库
user_id % 8 = 5 → ds5 (1000万) ← 从ds1迁来
... (每个旧库拆成2个新库)
扩容步骤(双写过渡法,不停机):
[1/5] 新增 ds4-ds7,搭建 ds0→ds4/ds1→ds5 的同步 ✓
[2/5] 应用改成双写(同时写旧4库和新8库规则) ✓
[3/5] 数据校验(旧4库 vs 新8库数据一致性) ⏳ 进行中
[4/5] 读流量切到新8库(灰度 10%→50%→100%) ○ 等待
[5/5] 下线旧4库 ○ 等待
取模扩容为什么这么痛?4 库扩 8 库,每个旧库要拆出一半数据迁到新库。8000 万行数据要迁移 4000 万行(50%)。而且迁移期间要双写保证不停服,复杂度很高。这就是为什么推荐一致性 hash——扩容时只迁移 hash 环上受影响的一小段数据,迁移量远小于取模。
PRO TIP 扩容规划要提前预留倍数。取模算法扩容必须翻倍(2→4→8→16),不能加 1 个(4→5),否则数据分布会乱。所以初期规划就要想好:4 库够用多久?什么时候扩 8 库?别等到扛不住才扩——扩容本身要几小时,扛不住的当天扩不完。建议在单库 QPS 到 70% 时启动扩容

⑦ 坑与事故(4 个真实翻车)

事故 1:跨库 JOIN 直接报错,后台报表全挂

现象:分库分表后,运营后台报表查询 SELECT o.*, u.name FROM t_order o JOIN t_user u ON o.user_id=u.id 直接报错。订单表按 user_id 分了 4 库,用户表在另一个库,跨库 JOIN 不支持。

原因:ShardingSphere 不支持跨库 JOIN(跨同一个分片键的可以,跨不同库的不行)。分库后订单和用户不在一个库,JOIN 就废了。

解法(3 选 1):

绑定表(推荐):把 user 表也按 user_id 分片,保证 user 和 order 在同一个库,JOIN 就能正常走。bindingTables: [t_order, t_user]

广播表:小表(如字典表、配置表)在每个库都存一份,JOIN 时本地查。适合数据量小且不怎么变的表。

应用层 JOIN:先查订单,再批量查用户,代码里合并。性能差,适合低频后台查询。

事故 2:分页查询越翻越慢,第 100 页直接超时

现象:分库分表后,SELECT * FROM t_order ORDER BY create_time LIMIT 100000, 10(查第 1 万页)直接超时。前几页很快,越往后越慢。

原因:分库分表后的分页是归并排序——ShardingSphere 要查所有分片的 LIMIT 0, 100010,然后在内存里合并再取 10 条。页越深,每个分片查的数据越多,4 个分片每个查 10 万行,内存爆了。

# 逻辑SQL:LIMIT 100000, 10 # ShardingSphere 实际执行的(4个分片各查10万+10行): SELECT * FROM t_order_0 ORDER BY create_time LIMIT 0, 100010 # ds0 SELECT * FROM t_order_1 ORDER BY create_time LIMIT 0, 100010 # ds1 SELECT * FROM t_order_2 ORDER BY create_time LIMIT 0, 100010 # ds2 SELECT * FROM t_order_3 ORDER BY create_time LIMIT 0, 100010 # ds3 # 然后内存归并排序 40万行,取第10万-10万10行

解法:禁止深度分页,超过 100 页报错或提示"请缩小查询范围";②游标分页WHERE create_time > last_time ORDER BY create_time LIMIT 10),每次记住上一页最后一条;③后台深度查询走 ES 或独立汇总库。

事故 3:分布式事务没配,跨库操作只成功一半

现象:下单操作要同时写 t_order(ds0)和 t_inventory(ds2),没配分布式事务。ds0 写成功后 ds2 写失败,结果订单创建了但库存没扣,超卖。

原因:默认 @Transactional 只能保证单库事务。跨库操作要么用 ShardingSphere 的分布式事务(XA/BASE),要么自己实现补偿。

解法:

# 方案1:XA 事务(强一致,性能差) spring.shardingsphere.rules.transaction.default-type: XA // 方案2:BASE 事务(最终一致,性能好,推荐) @Transactional // 普通注解,ShardingSphere 自动接管 public void createOrder(Order order) { orderMapper.insert(order); // ds0 inventoryMapper.deduct(order.getProductId()); // ds2 // BASE模式下,某个失败会记录重试任务,最终一致 } // 方案3:业务补偿(最可靠但最繁琐) // 写订单成功后,扣库存失败 → 发MQ → 补偿任务重试扣库存 → 失败超时则回滚订单
事故 4:不带分片键的查询把数据库打挂

现象:运营跑了条 SELECT COUNT(*) FROM t_order WHERE create_time > '2026-06-01',16 张表全查一遍,每张表 500 万行,单次查询耗时 40 秒,把 4 个库的连接池全占满,线上服务全部超时。

原因:不带分片键 user_id 的查询会全库广播,16 张表全表扫描。运营后台一个查询把生产搞挂。

解法:后台统计类查询禁止走分片库,走 Canal 同步到 ES/汇总库查;②ShardingSphere 配置 sql-show: true 开发期就能发现广播 SQL;③代码审查:所有 SQL 必须带分片键,不带的不让上线。这是分库分表后最常见的线上事故

⑧ FAQ(5 个高频问题)

分库分表后,主键 ID 怎么保证全局唯一?

单库时用 MySQL 自增 ID 就行,分库后每个库各自自增会冲突。3 种方案:

雪花算法(Snowflake,推荐):64 位 = 1 位符号 + 41 位时间戳 + 10 位机器 ID + 12 位序列号。ShardingSphere 内置支持,配 keyGeneratorName: snowflake 即可。优点:趋势递增、不依赖第三方、性能好。

数据库号段:用一张独立表分配 ID 段(一次发 1000 个),应用缓存本地用完再取。优点:简单;缺点:依赖 DB,重启丢一段。

UUID:不推荐——无序、占空间(36 字符)、索引性能差。

ShardingSphere-JDBC 和 Proxy 该选哪个?

优先选 JDBC。理由:

性能:JDBC 是进程内调用,零网络开销;Proxy 多一跳网络,QPS 损失 10-20%。

运维:JDBC 是 jar 包,跟随应用部署,无额外进程;Proxy 要独立部署集群,还得高可用。

故障点:JDBC 挂了应用也挂了(一起的);Proxy 挂了所有应用都受影响(单点风险)。

什么时候选 Proxy:①多语言团队(Java + Python + Go 都要用);②DBA 要统一管理所有数据库连接;③不想改 Java 应用配置(Proxy 对应用透明)。

分库分表后还能用 MyBatis/JPA 吗?

能,完全不用改。ShardingSphere 接管的是 DataSource 层,对上层 ORM 框架透明。MyBatis、JPA(Hibernate)、JdbcTemplate、Spring Data 全部正常用。

你写的 SQL 里表名还是逻辑表名 t_order,ShardingSphere 自动改写成真实表名 t_order_0。MyBatis 的 XML 映射、注解 SQL 一个字都不用改。这是 ShardingSphere 最大的优势——零侵入

唯一要注意的是:某些 ShardingSphere 不支持的 SQL 语法(比如某些跨库子查询),会报错。这种情况要么改写 SQL,要么走非分片库。

数据怎么从单库迁移到分库分表?

这是个大工程,核心是"不停机平滑迁移"。标准流程:

搭建分片库:建好 4 个新库 16 张表,配好 ShardingSphere。

双写:应用改成同时写旧库和新分片库(旧库用旧逻辑,新库按分片规则写)。

数据同步:用工具(DataX/Canal)把旧库存量数据按分片规则同步到新库。

校验:对比新旧库数据一致性,有差异修复。

切读:读流量灰度切到新分片库(10% → 50% → 100%)。

下线旧库:停止双写,下线旧库。整个过程通常 1-2 周。

什么情况不要分库分表?

以下情况不要急着分

单表 < 1000 万行:MySQL 单表 1000 万内优化好索引完全够用,分了反而增加复杂度。

没试过读写分离:先读写分离(读压力分摊到从库),能解决 70% 的性能问题,比分库分表简单 10 倍。

没试过 Redis 缓存:热点数据放 Redis,DB 压力直接降一个量级。

没归档冷数据:3 年前的历史订单归档到冷库,主表数据量直接砍半。

团队没专职 DBA:分库分表运维复杂度高,没专人维护容易出事。分库分表是放大器——放大性能,也放大复杂度和故障风险。

✅ ShardingSphere 三句话速记

1. 定位:JDBC 增强层,透明分库分表。业务代码不用改,配 YAML 就能用。Java 项目首选 JDBC 模式。

2. 分片键是命脉:选查询里"几乎每次都带"的字段。查询不带分片键 = 全库广播 = 性能灾难。取模分布均匀但扩容痛,一致性 hash 扩容友好。

3. 能不分就不分:分库分表是最后手段。先穷尽索引/读写分离/缓存/归档/硬件升级。分了之后跨库 JOIN、深度分页、分布式事务全是坑,运维成本翻倍。

相关专题:MySQL 场景决策 · Redis 专题 · 消息队列决策

全栈资料库 · ShardingSphere 分库分表实战