SpringBoot项目中ShardingSphere 5.x与dynamic-datasource的深度整合实践
在分布式系统架构中,数据源管理一直是开发者面临的核心挑战之一。当我们需要同时处理分库分表和多租户数据隔离时,ShardingSphere和dynamic-datasource这两个强大的工具往往会"狭路相逢"。本文将深入探讨如何让它们在SpringBoot生态中和谐共存,不仅解决常见的配置冲突问题,还会分享一套经过生产验证的最佳实践方案。
1. 理解技术栈的定位与冲突根源
ShardingSphere 5.x作为分布式数据库中间件的领军者,提供了强大的分库分表能力。而dynamic-datasource则是MyBatis-Plus生态中广受欢迎的多数据源管理组件。当两者在同一项目中相遇时,冲突主要来自三个方面:
- Bean加载顺序问题:ShardingSphere会在Spring容器启动时自动创建DataSource Bean,而dynamic-datasource也需要管理数据源
- 代理机制冲突:两者都通过动态代理技术对数据源进行增强,导致代理嵌套引发异常
- 事务管理兼容性:Spring的事务管理器需要明确知道应该管理哪个层级的DataSource
关键冲突表现通常为:
- 启动时报
BeanCurrentlyInCreationException - 分片规则不生效
- 动态数据源切换失败
- 事务注解(@Transactional)行为异常
2. 核心配置方案设计
2.1 基础环境准备
首先确保项目依赖版本兼容,推荐使用以下组合:
<dependencies> <!-- ShardingSphere --> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId> <version>5.3.2</version> </dependency> <!-- dynamic-datasource --> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.6.1</version> </dependency> <!-- 其他必要依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.16</version> </dependency> </dependencies>2.2 配置类关键实现
创建核心配置类ShardingDataSourceConfig,这是解决兼容性问题的核心所在:
@Configuration @AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class}) public class ShardingDataSourceConfig { @Lazy @Autowired private DataSource shardingSphereDataSource; @Autowired private DynamicDataSourceProperties properties; @Bean public DynamicDataSourceProvider dynamicDataSourceProvider() { return new AbstractDataSourceProvider() { @Override public Map<String, DataSource> loadDataSources() { Map<String, DataSource> dataSourceMap = createDataSourceMap(properties.getDatasource()); // 将ShardingSphere数据源纳入动态数据源管理 dataSourceMap.put("sharding", shardingSphereDataSource); return dataSourceMap; } }; } @Primary @Bean public DataSource dataSource(DynamicDataSourceProvider provider) { DynamicRoutingDataSource ds = new DynamicRoutingDataSource(); ds.setPrimary(properties.getPrimary()); ds.setProvider(provider); ds.setStrategy(properties.getStrategy()); return ds; } }关键注解解析:
| 注解 | 作用 | 必要性 |
|---|---|---|
@Lazy | 延迟加载ShardingSphere数据源 | 必须,解决循环依赖 |
@Primary | 标记主数据源 | 必须,确保事务管理器使用正确数据源 |
@AutoConfigureBefore | 控制配置加载顺序 | 建议,确保动态数据源优先初始化 |
3. 多场景下的配置策略
3.1 混合使用场景配置
在application.yml中配置示例:
spring: datasource: dynamic: primary: master # 默认数据源 datasource: master: type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql://localhost:3306/master username: root password: root slave: type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql://localhost:3306/slave username: root password: root shardingsphere: datasource: names: ds0,ds1 ds0: url: jdbc:mysql://localhost:3306/ds0 username: root password: root ds1: url: jdbc:mysql://localhost:3306/ds1 username: root password: root rules: sharding: tables: t_order: actual-data-nodes: ds$->{0..1}.t_order_$->{0..1} table-strategy: standard: sharding-column: order_id precise-algorithm-class-name: com.example.OrderShardingAlgorithm3.2 动态切换与分片协同工作
在实际业务代码中,可以这样使用:
@Service public class OrderService { // 使用分片数据源 @DS("sharding") public void createShardingOrder(Order order) { // 会自动路由到正确的分片 orderMapper.insert(order); } // 使用普通数据源 @DS("master") public void createNormalOrder(Order order) { orderMapper.insert(order); } // 事务方法示例 @Transactional @DS("slave") public void updateOrder(Long id) { // 跨数据源操作需要特别注意事务边界 } }4. 生产环境中的注意事项
4.1 性能调优建议
- 连接池配置:为不同类型的数据源设置不同的连接池参数
spring: datasource: dynamic: datasource: master: # 连接池配置 initial-size: 5 max-active: 20 min-idle: 5 sharding: # ShardingSphere连接池单独配置 initial-size: 10 max-active: 50- 监控集成:建议为每个数据源配置监控
@Bean public ServletRegistrationBean<StatViewServlet> druidServlet() { ServletRegistrationBean<StatViewServlet> reg = new ServletRegistrationBean<>(); reg.setServlet(new StatViewServlet()); reg.addUrlMappings("/druid/*"); return reg; }4.2 常见问题排查指南
问题1:启动时报BeanCurrentlyInCreationException
- 检查是否遗漏
@Lazy注解 - 确认配置类加载顺序
问题2:分片规则不生效
- 检查数据源是否正确地加入了动态数据源管理
- 确认
@DS注解没有覆盖分片逻辑
问题3:事务不生效
- 确保
@Primary注解配置正确 - 检查事务管理器是否绑定了正确的数据源
@Bean @ConditionalOnMissingBean public PlatformTransactionManager transactionManager(DynamicRoutingDataSource dataSource) { return new DataSourceTransactionManager(dataSource); }在实际项目落地过程中,我们发现最大的挑战不在于技术实现本身,而在于对两种组件工作原理的深入理解。通过合理的设计,不仅能让ShardingSphere和dynamic-datasource和谐共处,还能发挥出1+1>2的效果——既保留了ShardingSphere强大的分片能力,又能利用dynamic-datasource灵活的切换机制。