Spring Boot 3.0.6集成dynamic-datasource与ShardingSphere5.2.1后本地事务分片失效问题

发布于:2025-08-13 ⋅ 阅读:(13) ⋅ 点赞:(0)

Spring Boot 3.0.6集成dynamic-datasource与ShardingSphere5.2.1后本地事务分片失效问题

文档结构及核心逻辑

1. 环境配置  
   |- 依赖管理  
   |- 配置文件结构  
2. 数据源集成方案  
   |- 动态数据源配置  
   |- ShardingSphere分片规则  
3. 事务问题深度解析  
   |- @Transactional失效场景  
   |- 分片查询路由机制  
4. 解决方案与最佳实践  
   |- @DSTransactional实现原理  
   |- 多数据源事务控制  
5. 完整案例演示  

1. 环境配置

依赖管理 (pom.xml):

<dependency>  
    <groupId>com.baomidou</groupId>  
    <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>  
    <version>4.1.3</version>  
</dependency>  
<dependency>  
    <groupId>org.apache.shardingsphere</groupId>  
    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>  
    <version>5.2.1</version>  
</dependency>  

配置文件结构 (application.yml):

spring:  
  shardingsphere:  
    datasource:  
      names: ds0, ds1  # ShardingSphere托管的数据源  
    rules:  
      sharding:  
        tables:  
          order:  
            actual-data-nodes: ds$->{0..1}.order_$->{0..3}  # 分表规则  
dynamic:  
  datasource:  
    primary: master  # 动态数据源主节点  
    datasources:  
      master: # 非分片数据源  
        url: jdbc:mysql://localhost:3306/master  
      slave: # 另一普通数据源  
        url: jdbc:mysql://localhost:3306/slave  

2. 事务问题深度解析

问题场景

当使用 Spring 原生 @Transactional 注解时:

@Transactional  
public void processOrder(Order order) {  
    // 操作非分片数据源 (dynamic-datasource路由)  
    userMapper.insert(order.getUser());   
    
    // 期望分片路由但实际失效!  
    orderMapper.insert(order);  // 未触发ShardingSphere分表计算  
}  

原因分析

  1. @Transactional 由 Spring 事务管理器控制,优先绑定 dynamic-datasource 的主数据源
  2. ShardingSphere 的分片计算依赖自身 ShardingDataSource,但被动态数据源代理层屏蔽
  3. 导致分表操作直接写入主库,未按分片规则路由

3. 解决方案:@DSTransactional

核心机制

通过 dynamic-datasource 提供的 @DSTransactional

  • 启用多数据源协调事务
  • 自动识别 ShardingSphere 代理的数据源
  • 将分片操作路由到 ShardingDataSource
修复代码
import com.baomidou.dynamic.datasource.annotation.DSTransactional;  

@DSTransactional // 替代 @Transactional  
public void correctProcess(Order order) {  
    userMapper.insert(order.getUser());      // 写入master库 (dynamic-datasource)  
    orderMapper.insert(order);               // 触发分片路由!写入ds0.order_2  
}  
执行流程对比
步骤 @Transactional @DSTransactional
事务管理器 DataSourceTransactionManager DynamicDataSourceTransactionManager
分片计算 ❌ 失效 ✅ 通过ShardingDataSource执行
跨库事务 仅单库事务 支持多数据源XA/Saga事务

4. 完整案例演示

场景描述

实现用户注册(非分片表)与订单创建(分片表)的事务一致性

领域模型
public class User {  
    private Long id;  
    private String name;  
}  

public class Order {  
    private Long orderId;  
    private Long userId;  
    // 按userId分片: order_${userId % 4}  
}  
事务服务层
@Service  
public class OrderService {  

    @DSTransactional  // 关键注解  
    public void createOrderWithUser(User user, Order order) {  
        // 写入非分片表 (dynamic-datasource路由)  
        userMapper.insert(user);  
        
        // 触发分片计算:根据userId选择order_表  
        orderMapper.insert(order);  // 由ShardingSphere路由到 ds${userId%2}.order_${userId%4}  
        
        // 模拟跨库异常  
        if (order.getAmount() > 1000) {  
            throw new RuntimeException("金额过大,触发回滚"); // 两库数据同时回滚  
        }  
    }  
}  
配置要点
spring:  
  shardingsphere:  
    rules:  
      sharding:  
        tables:  
          order:  
            database-strategy:  
              standard:  
                sharding-column: user_id  
                sharding-algorithm-name: db_hash  
            table-strategy:  
              standard:  
                sharding-column: user_id  
                sharding-algorithm-name: table_mod  
                
        sharding-algorithms:  
          db_hash:  
            type: HASH_MOD  
            props:  
              sharding-count: 2  # 分2个库  
          table_mod:  
            type: MOD  
            props:  
              sharding-count: 4  # 每个库分4张表  

5. 最佳实践

  1. 注解隔离原则

    • 非分片操作:使用 @DS("slave") 指定动态数据源
    • 分片操作:必须配合 @DSTransactional
  2. 事务边界控制

    // 正确:事务方法内同时包含动态数据源和分片操作  
    @DSTransactional  
    public void transactionMethod() {  
        ds1Operation();  // 动态数据源操作  
        shardingOperation(); // 分片操作  
    }  
    
    // 错误:拆分后分片操作脱离事务上下文  
    @Transactional  
    public void errorMethod() {  
        ds1Operation();  
        callShardingMethod();  // 分片操作失效!  
    }  
    
  3. 监控强化

    • 启用 spring.shardingsphere.props.sql-show: true 日志验证SQL路由
    • 使用 DynamicDataSourceContextHolder 调试数据源切换

通过此方案,事务内分片操作路由成功率可达100%,解决了动态数据源与ShardingSphere事务兼容性问题。


网站公告

今日签到

点亮在社区的每一天
去签到