ShardingSphere 与 @DS 注解如何共存?深度解析分库分表数据源的整合方案
在微服务或高并发系统中,分库分表已成为应对海量数据的标配方案。而 Apache ShardingSphere 作为国内主流的分布式数据库中间件,提供了强大的透明化分片能力。与此同时,很多项目也使用了 @DS 注解(如 MyBatis-Plus 的 dynamic-datasource)来实现多数据源切换。
但问题来了:ShardingSphere 和 @DS 能否一起用?怎么用?
本文将深入剖析 ShardingSphere 的工作原理,并给出 ShardingSphere 与 @DS 完美整合的实战方案。
一、核心矛盾:两个“数据源管理层”的冲突
@DS(如 dynamic-datasource):在应用层通过ThreadLocal切换 顶层数据源(DataSource),适用于普通数据库连接。- ShardingSphere:它本身就是一个 逻辑数据源(
ShardingSphereDataSource),内部封装了多个真实的物理数据源,并负责 SQL 解析、路由、改写、执行与结果归并。
❗关键点:ShardingSphere 不识别
@DS,@DS也 不知道 ShardingSphere 内部结构。
因此,直接混用会导致:
- 分片失效
- 数据源切换混乱
- 连接池冲突
那怎么办?
✅ 正确思路是:把 ShardingSphere 当作一个“黑盒数据源”,注册到动态数据源框架中,由 @DS 来切换它。
二、ShardingSphere 如何知道有哪些库和表?
很多人误以为 ShardingSphereDriver 会自动发现库表,其实完全不是!
真相:一切靠配置!
ShardingSphere 的分片规则、数据源列表、逻辑表与实际节点的映射关系,全部来自 显式配置,常见方式有:
- YAML 文件(最常用)
- Spring Boot Properties
- 编程式 API 构建
示例:YAML 配置片段
# shardingsphere-config.yaml
dataSources:
ds0:
url: jdbc:mysql://localhost:3306/order_db_0
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
ds1:
url: jdbc:mysql://localhost:3306/order_db_1
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
rules:
- !SHARDING
tables:
t_order:
actualDataNodes: ds${0..1}.t_order_${0..1}
databaseStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: db_inline
tableStrategy:
standard:
shardingColumn: order_id
shardingAlgorithmName: table_inline
shardingAlgorithms:
db_inline:
type: INLINE
props:
algorithm-expression: ds${user_id % 2}
table_inline:
type: INLINE
props:
algorithm-expression: t_order_${order_id % 2}
在这个配置中,ShardingSphere 明确知道:
- 有两个物理数据源:
ds0、ds1 - 逻辑表
t_order对应 4 个真实表 - 分库依据
user_id,分表依据order_id
🔍 重点:这些信息 不是由
ShardingSphereDriver自动获取的,而是你写死在配置里的!
ShardingSphereDriver 只是一个 JDBC 驱动类,用于兼容 jdbc:shardingsphere:... 格式的 URL,在 Spring Boot + YAML 模式下通常 不需要显式使用它。
三、关键澄清:什么是“物理数据源”?
这是最容易混淆的地方!
✅ ShardingSphere 中所说的“多个物理数据源”,指的就是你在配置中定义的那些真实的、可直接连接数据库的
DataSource实例(如 HikariCP、Druid 等)——它们确实是标准意义上的“顶层数据源”(Top-level DataSource)。
但在 ShardingSphere 的上下文中,它们被封装在 ShardingSphereDataSource 内部,对外不可见。
举个例子:
当你配置了 ds0 和 ds1,ShardingSphere 在启动时会:
- 为
ds0创建一个HikariDataSource实例; - 为
ds1创建另一个HikariDataSource实例; - 将这两个 物理数据源 保存在内部路由引擎中;
- 对外只暴露一个 逻辑数据源
ShardingSphereDataSource。
当你执行 SQL 时:
- 应用从
ShardingSphereDataSource.getConnection()获取连接; - ShardingSphere 解析 SQL,根据分片规则决定目标(比如
ds1); - 内部调用
ds1.getConnection(),在真实数据库上执行; - 结果返回给应用。
整个过程对开发者透明。
四、整合难点:为什么不能直接在 YAML 里配 ShardingSphere?
你可能会想:既然 dynamic-datasource 支持多数据源,那我直接在 application.yml 里加一个 shardingDs 不就行了?
shardingDs:
url: jdbc:mysql://... # ❌ 问题来了:ShardingSphere 没有单一 URL!
但 ShardingSphere 不是由一个 URL 定义的!它需要:
- 多个物理库地址
- 分片规则
- 表映射关系
这些无法通过 standard 的 url/username/password 表达。
所以,我们必须手动构建 ShardingSphereDataSource,并通过 Spring Bean 注入。
五、核心技巧:“fake 配置”机制详解
这是整合成功的关键!我们来彻底讲清楚这句常被误解的话:
“fake 配置,实际会被 Spring 容器中的同名 Bean 替换”
🤔 什么是 fake 配置?
dynamic-datasource-spring-boot-starter 在启动时,会扫描 spring.datasource.dynamic.datasource 下的所有 key(如 master, slave1),并尝试为每个 key 创建 DataSource。
但它有一个重要设计原则:
如果 Spring 容器中已经存在一个同名的
DataSourceBean,则优先使用该 Bean,忽略 YAML 中的配置!
这就给了我们一个“后门”:
我们可以先用 Java 代码创建好 ShardingSphere 数据源,再在 YAML 中写一个“空壳”配置仅用于注册名字。
✅ 正确做法分两步:
第一步:Java Config 中定义真实数据源
@Configuration
public class ShardingSphereConfig {
@Bean("shardingDs") // 名字必须匹配
public DataSource shardingDataSource() throws SQLException {
Resource resource = new ClassPathResource("shardingsphere-config.yaml");
return YamlShardingSphereDataSourceFactory.createDataSource(resource.getFile());
}
}
第二步:YAML 中写“fake 配置”占位
spring:
datasource:
dynamic:
datasource:
master:
url: jdbc:mysql://localhost:3306/app_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
shardingDs:
# ⚠️ 注意:这里没有 url/username/password!
# 这只是一个“名字声明”,告诉 dynamic-datasource:“存在一个叫 shardingDs 的数据源”
# 实际对象来自上面的 @Bean("shardingDs")
driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
💡 为什么还要写
driver-class-name?
因为 dynamic-datasource 要求每个数据源至少有一个驱动类,否则会报校验错误。
这里随便写一个(如ShardingSphereDriver)即可,不会真正使用它!
🔍 类比理解
想象公司 HR 系统:
- 正常员工:填入职表(YAML),HR 自动生成档案。
- 外部专家:你直接把他的名片(
@Bean)交给 HR。 - 但为了让他在通讯录里出现,你仍需在系统里“新建一条记录”,只写名字。
- HR 一看:“哦,这个人已经有实体名片了”,就直接用名片,忽略表格内容。
这里的“新建记录”就是 fake 配置,“名片”就是你的 @Bean。
❌ 如果不写 fake 配置?
即使你有 @Bean("shardingDs"),dynamic-datasource 根本不知道这个数据源要纳入管理!
结果:使用 @DS("shardingDs") 时抛出异常:
com.baomidou.dynamic.datasource.exception.CannotFindDataSourceException:
Cannot find dataSource of name [shardingDs]
六、整合方案:完整代码示例
1. 创建 ShardingSphere 配置类
@Configuration
public class ShardingSphereConfig {
@Bean("shardingDs")
public DataSource shardingDataSource() throws SQLException {
Resource res = new ClassPathResource("shardingsphere-config.yaml");
return YamlShardingSphereDataSourceFactory.createDataSource(res.getFile());
}
}
2. application.yml 配置
spring:
datasource:
dynamic:
primary: master
datasource:
master:
url: jdbc:mysql://localhost:3306/app_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
shardingDs:
driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver # fake
3. Service 中使用 @DS
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
public void saveLog(String log) {
logMapper.insert(log); // 默认走 master
}
@DS("shardingDs")
public List<Order> queryByUserId(Long userId) {
return orderMapper.selectByUserId(userId); // 自动分库分表
}
}
七、数据源层级结构图(重点!)
┌──────────────────────────────────────┐
│ Dynamic Datasource(@DS 切换层) │
├───────────────────┬──────────────────┤
│ master │ shardingDs │ ←─ @DS 切换的目标(逻辑数据源)
│ (普通 DataSource) │ (ShardingSphere) │
└─────────┬─────────┴─────────┬────────┘
│ │
│ ▼
│ ┌──────────────────────┐
│ │ ShardingSphere 内部 │
│ ├───────────┬──────────┤
│ │ ds0 │ ds1 │ ←─ 物理数据源(真实 Hikari/Druid)
│ │ (db_0) │ (db_1) │
│ └───────────┴──────────┘
│
▼
app_db(普通库)
✅ 关键结论:
@DS只能切换到master或shardingDs这一层;shardingDs内部的ds0/ds1路由由 ShardingSphere 自动完成;- 物理数据源确实是标准
DataSource,但属于 ShardingSphere 的内部实现。
八、常见误区与注意事项
| 误区 | 正确认知 |
|---|---|
| “ShardingSphere 会自动扫描所有库表” | ❌ 必须显式配置 actualDataNodes |
| “要用 ShardingSphereDriver 才能分片” | ❌ 在 YAML 模式下,物理数据源仍用 MySQL/PG 驱动 |
| “@DS 可以在 ShardingSphere 内部切换子库” | ❌ @DS 只能切换顶层数据源,不能干预内部路由 |
| “fake 配置是用来连数据库的” | ❌ 它只是名字占位符,真实数据源来自 @Bean |
九、总结
| 组件 | 职责 |
|---|---|
@DS | 在 多个顶层数据源之间切换(如普通 DB 与 ShardingSphere 逻辑数据源) |
| ShardingSphere | 在 其内部封装的物理数据源之间 自动完成分库分表、SQL 路由、结果归并 |
✅ 最佳实践:
将 ShardingSphere 视为一个“功能增强型数据源”,通过
@Bean注册到 Spring 容器,并在 dynamic-datasource 中用 fake 配置占位。用@DS("shardingDs")切入分片世界,其余交给 ShardingSphere。
这样,你既能享受动态数据源的灵活性,又能利用 ShardingSphere 的强大分片能力,二者各司其职,完美协同。
十、扩展建议
- 若业务复杂,可考虑使用 ShardingSphere-Proxy(独立部署),应用直连 Proxy,彻底解耦分片逻辑。
- 分片键设计至关重要,避免跨库查询、全表扫描。
- 结合 Seata 可实现分布式事务(需额外配置)。
更多推荐




所有评论(0)