SpringBoot+Durid+dynamic实现多数据源分布式事务
引言:
在现代的应用程序中,使用多个数据源来处理不同的业务需求已成为常态。然而,处理多数据源之间的分布式事务是一个复杂的问题。本文将介绍如何使用Spring Boot、Druid和dynamic-datasource-spring-boot-starter来实现多数据源的分布式事务。
背景
在传统的单数据源应用中,事务管理相对简单。但当引入多个数据源时,事务管理变得更加复杂。为了解决这个问题,我们将使用Spring Boot作为应用程序的基础框架,Druid作为数据源连接池,并结合dynamic-datasource-spring-boot-starter来实现动态数据源切换和分布式事务管理。
集成Druid数据源连接池和dynamic动态数据源切换和分布式事务管理
Druid是一款高性能的数据库连接池,具有强大的监控和统计功能。在Spring Boot项目中集成Druid非常简单,只需添加相关依赖并进行配置即可。可以通过以下步骤来完成集成:
- 在pom.xml文件中添加依赖。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
- 在application.properties或application.yml文件中配置Druid和dynamic的相关参数。
spring:
datasource:
dynamic:
# druid连接池设置
druid:
# 配置初始化线程数
initialSize: 5
# 最小线程数
minIdle: 5
# CPU核数+1,也可以大些但不要超过20,数据库加锁时连接过多性能下降
maxActive: 11
# 最大等待时间,内网:800,外网:1200(三次握手1s)
maxWait: 60000
# 连接可空闲存活时间(ms)
timeBetweenEvictionRunsMillis: 60000
# 连接保持空闲而不被驱逐的最长存活时间(ms)
minEvictableIdleTimeMillis: 300000
# 用来检测连接是否有效的sql,如果validationQuery为空,那么testOnBorrow、testOnReturn、testWhileIdle这三个参数都不会起作用
validationQuery: SELECT 1
# 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效;
testWhileIdle: true
# 建议配置为false,申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnBorrow: false
# 建议配置为false,归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能;
testOnReturn: false
# PSCache对支持游标的数据库性能提升巨大
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat
# 保持minIdle数量的长连接
keepAlive: true
# 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。
# 在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100。缺省值为-1
maxPoolPreparedStatementPerConnectionSize: 20
# 是否合并多个DruidDataSource的监控数据
useGlobalDataSourceStat: true
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
primary: huawei
datasource:
# 主库数据源
master:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.211.55.6:13306/ddz?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
username: root
password: root
# 从库数据源
slave:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.211.55.5:13306/ddz?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
username: root
password: root
# MyBatis Plus配置
mybatis-plus:
# 搜索指定包别名
typeAliasesPackage: com.ddz.**.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
configuration:
# 使全局的映射器启用或禁用缓存
cache-enabled: true
# 允许JDBC 支持自动生成主键
use-generated-keys: true
# 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新
default-executor-type: simple
# 指定 MyBatis 所用日志的具体实现
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
# 使用驼峰命名法转换字段
map-underscore-to-camel-case: true
- 创建两个Mapper类和实体类
@DS("master")
public interface UserMapper extends BaseMapper<User> {
/**
* 新增数据
*
* @param user 实例对象
* @return 影响行数
*/
int insertSql(User user);
}
@DS("slave")
public interface ScoreMapper extends BaseMapper<Score> {
/**
* 新增数据
*
* @param score 实例对象
* @return 影响行数
*/
int insertSql(Score score);
}
@Data
public class User {
private Integer id;
private String name;
private Integer balance;
private String remark;
public User(String name, Integer balance) {
this.name = name;
this.balance = balance;
}
}
@Data
public class Score {
private Integer id;
private Integer count;
public Score(Integer count) {
this.count = count;
}
}
- 编写Controller测试
给方法加上@DSTransactional注解实现事务管理
@RestController
@RequestMapping("user")
public class UserController {
@Resource
private UserMapper userMapper;
@Resource
private ScoreMapper scoreMapper;
@GetMapping("/get")
@DSTransactional
public void get() {
// 随机触发异常检查两个数据源的一致性
int i = random.nextInt(10);
userMapper.insertSql(new User("ddz", i));
scoreMapper.insertSql(new Score(i*10));
Random random = new Random();
if (i % 2 == 0) {
i = 1 / 0;
}
}
}
- 需要durid监控页面的添加配置类
@Configuration
public class DruidConfig {
/**
* 配置Druid 监控启动页面
*
* @return servletRegistrationBean
*/
@Bean
@ConditionalOnMissingBean
public ServletRegistrationBean<Servlet> druidStartViewServlet() {
ServletRegistrationBean<Servlet> servletRegistrationBean = new ServletRegistrationBean<Servlet>(new StatViewServlet(), "/druid/*");
// 白名单
servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
// 黑名单
// servletRegistrationBean.addInitParameter("deny", "");
// 登录查看信息的账密,用于登录Druid监控后台
servletRegistrationBean.addInitParameter("loginUsername", "ddz");
servletRegistrationBean.addInitParameter("loginPassword", "ddz2023");
// 是否能够重置数据
servletRegistrationBean.addInitParameter("resetEnable", "true");
return servletRegistrationBean;
}
/**
* Druid监控过滤器配置规则
* ConditionalOnMissingBean 防止注册相同的bean
*
* @return filterFilterRegistrationBean
*/
@Bean
@ConditionalOnMissingBean
public FilterRegistrationBean<Filter> filterRegistrationBean() {
FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
filterFilterRegistrationBean.setFilter(new WebStatFilter());
// 添加过滤规则
filterFilterRegistrationBean.addUrlPatterns("/*");
// 添加不需要忽略的格式信息
filterFilterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterFilterRegistrationBean;
}
}
- 添加好配置类后重启项目在页面输入http://127.0.0.1:8080/druid/login.html访问
分布式事务的注意事项
在使用多数据源进行分布式事务时,需要注意以下几点:
- 数据源配置和事务管理的正确性,包括数据源的连接信息、事务的传播行为等。
- 事务的超时和回滚策略,确保事务在适当的时间内完成或回滚。
- 对于不同数据源之间的数据一致性要求高的场景,可以使用分布式事务管理器(如Atomikos、Bitronix等)来实现XA事务。
- 针对不同的数据库类型,可能需要配置特定的事务管理器和驱动程序。
总结
本文介绍了如何使用Spring Boot、Druid和dynamic-datasource-spring-boot-starter来实现多数据源的分布式事务。通过合理配置数据源连接池、动态数据源和事务管理,我们可以有效地处理多数据源下的事务一致性问题。在实际应用中,根据具体场景和需求,可以选择适合的分布式事务管理方案。
参考资料:
dynamic-datasource-spring-boot-starter文档:https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611
Spring事务管理文档:https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction
Druid官方文档:https://github.com/alibaba/druid