mysql 多数据源与事务嵌套不兼容解决方案

需求与问题

最近,因为某一个项目需求的数据量过大,已经对其它服务造成了一定的影响。所以为了给数据库减负,将这部分业务需求涉及到的数据给迁到新库中。但这就出现了个问题,这部分业务涉及到的业务表我虽然迁到新库中,但涉及到一些扩展类的业务表,比如日志信息等,因为监控的是整个系统的日志,所以肯定不能把这个表给单独拿出来。所以,这里就涉及到了双数据源的访问。并且在一个方法中涉及到双数据源问题,切换以及对应的事务信息怎么解决。

数据源配置

spring:
  autoconfigure:
    exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
  datasource:
    dynamic:
      primary: master 
      druid: # 全局druid参数,绝大部分值和默认保持一致。(现已支持的参数如下,不清楚含义不要乱设置)
        initial-size: 5
        max-active: 20
        min-idle: 5
        max-wait: 60000
        time-between-eviction-runs-millis: 50000
        min-evictable-idle-time-millis: 120000
        max-evictable-idle-time-millis: 280000
        test-while-idle: true
        test-on-borrow: false
        test-on-return: false
        validation-query: SELECT  1
        keep-alive: true
        pool-prepared-statements: true
        max-pool-prepared-statement-per-connection-size: 20
        filters: stat,slf4j # 注意这个值和druid原生不一致,默认启动了stat,wall
      datasource:
        master:
          url: jdbc:p6spy:mysql://172.16.15.92:3306/questionnaire_audit?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&connectTimeout=6000&socketTimeout=30000&autoReconnect=true&failOverReadOnly=false&serverTimezone=Asia/Shanghai
          driver-class-name: com.p6spy.engine.spy.P6SpyDriver
          username: airita
          # @s#wwsOPw$RF6tfc%e
          password: ENC(u2iKA7ejQVSQW32OvNAcErwYiXI2RjacdCTGli7PjxQ=)
        ds2:
          url: jdbc:p6spy:mysql://172.16.15.92:3306/airita?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&connectTimeout=6000&socketTimeout=30000&autoReconnect=true&failOverReadOnly=false&serverTimezone=Asia/Shanghai
          driver-class-name: com.p6spy.engine.spy.P6SpyDriver
          username: airita
          password: ENC(u2iKA7ejQVSQW32OvNAcErwYiXI2RjacdCTGli7PjxQ=)
jasypt:
  encryptor:
    password: airita

解决方案

个人在思考怎么解决这方面的问题时,想的解决方案。现在切换数据源的操作,我就用baomidou的@DS注解来做了。后续主要目的是为了解决事务与@DS注解的兼容问题,自身设想的方案为mysql触发器,spring事务隔离级别,以及@DSTransaction注解三种方案。最后采用了最后一种。

mysql触发器方案

既然问题是双数据源造成的,那么我把涉及到的扩展类业务表(这类业务表不多,而且只有查询方法)通过触发器的方式,实时同步到新库中,这样,在一个方法中,我访问的都是单独一个数据源的数据。那么@Transaction事务,不就没问题了?

- 创建sys_user触发器
create table questionnaire_audit.sys_user like sys_user;
insert into questionnaire_audit.sys_user
select * from sys_user;
CREATE TRIGGER sys_user_insert AFTER INSERT ON sys_user FOR EACH ROW
BEGIN
    -- 插入目标表
    INSERT INTO
      questionnaire_audit.sys_user(user_id,login_id,password,org_id,user_name,user_sex,user_mobile,user_mail,create_time,update_time,user_state,comments,incharge_flag,source,accessMethod,userCreateType,userAuditStatus,userOrg,secretKey,foura_status,qyzx_login_id,qyzx_status)
    VALUES
      (new.user_id,new.login_id,new.password,new.org_id,new.user_name,new.user_sex,new.user_mobile,new.user_mail,new.create_time,new.update_time,new.user_state,new.comments,new.incharge_flag,new.source,new.accessMethod,new.userCreateType,new.userAuditStatus,new.userOrg,new.secretKey,new.foura_status,new.qyzx_login_id,new.qyzx_status);
END;

CREATE TRIGGER sys_user_delete AFTER DELETE ON sys_user FOR EACH ROW 
BEGIN
  -- INSE
  DELETE FROM  questionnaire_audit.sys_user WHERE user_id = old.user_id;
END;


CREATE TRIGGER sys_user_update AFTER UPDATE ON sys_user FOR EACH ROW
BEGIN
  UPDATE questionnaire_audit.sys_user 
    SET user_id=new.user_id,login_id=new.login_id,password=new.password,org_id=new.org_id,user_name=new.user_name,user_sex=new.user_sex,user_mobile=new.user_mobile,user_mail=new.user_mail,create_time=new.create_time,update_time=new.update_time,user_state=new.user_state,comments=new.comments,incharge_flag=new.incharge_flag,source=new.source,accessMethod=new.accessMethod,userCreateType=new.userCreateType,userAuditStatus=new.userAuditStatus,userOrg=new.userOrg,secretKey=new.secretKey,foura_status=new.foura_status,qyzx_login_id=new.qyzx_login_id,qyzx_status=new.qyzx_status
  WHERE user_id = new.user_id;
END;

对旧库的表来创建新建,修改,删除三方面的触发器。

后续想了下,这块的业务虽然很少改动,但后期也不敢保证完全不变。也就是说,后续我在新库要对扩展业务表想进行插入时,假设老库同时有大批量数据插入,会出问题吗?而且有的表是整个大项目通用的全量业务表,如果其它核心业务内容进行操作,那同步到我新库,我觉得也不合适。而且性能因为同步,所以性能也降低了太多。废弃。

这个其实我的目的就是练习一下触发器,以前也没用过。

Spring事务隔离级别

上面的解决方案是从数据库层面来解决的,该怎么说,太粗暴了。那么,既然事务出现的问题,那么我是否可以在事务层面来对这个问题进行解决?一个事务有问题,多个事务行不行?

@Transaction开的事务,可以理解为它是默认开启了主数据源的事务,在这个主事务的数据远源中,使用@DS注解然后对数据源进行切换,肯定是不行的。

Spring的事务隔离级别,默认情况下是require类型的事务。这种事务代表了,当外部存在事务时,内部方法存在事务,则需要将内部事务加入到外部事务中。而外部不存在事务时,内部事务自己新开一个事务来处理。也就是说,如果存在外部事务,内部事务与外部事务同属一个事务。这肯定不行的。

那么别的事务级别,require_new级别。当外部存在事务时,内部方法存在事务,则需要将内部事务新开一个事务处理。而外部不存在事务时,内部事务自己新开一个事务来处理。如果这么处理的话,把针对各自不同的数据源操作,放到不同的方法中,使用require_new开启新的事务,可以解决的。具体过程去网上查一下,很多文章的,只不过这种方法我没实践,后续也会练习一下。

不采用这种方式的原因有俩,一方面,将针对不同的数据源的操作存放到不同的方法中,设置对应的隔离级别。那么对业务的操作顺序有一定要求。另一方面,因为在不同的事务中,事务回滚该怎么办。这种的回滚方式,我后续也网上查下资料,并且实践下。

@DSTransaction注解

这个也是在网上找到的,看名字就知道了,和@DS注解同属baomidou一家公司的,不过包的版本要求是高版本。使用方法很简单,使用@DSTransaction直接替换了原先的@Transaction即可。

/**
     * 语音转写结果回传地址
     */
    @RequestMapping("/{version}/{identificationType}/returnService/inside")
    @ResponseBody
    //baomidou的DS与事务注解冲突
//    @Transactional(rollbackFor = Exception.class)
    @DSTransactional
 public void transferReturnServiceInside(HttpServletRequest request, @PathVariable String version, @PathVariable String identificationType) {
}

感想

其实,学习和解决问题,我觉得我们还得有个系统的方式,就像这次的@DS注解一样,很明显同一家公司人家有配套的工具,我却一定要去网上乱查,说实在的,也是浪费时间。个人觉得,这也是大牛与菜鸡的一个区别,搜索问题,该怎么培养自己的思路呢?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL多数据源是指一个应用程序连接和操作多个不同的MySQL数据库。这种情况可能发生在以下几种情况下: 1. 分布式系统:当应用程序需要同时连接和操作多个MySQL数据库时,例如分布在不同地理位置的数据库服务器。 2. 多租户系统:在一个多租户系统中,每个租户有自己独立的数据库。应用程序需要连接和操作多个租户的数据库。 3. 数据库分片:当数据库数据量巨大时,可能需要将数据分散到多个数据库服务器上。应用程序需要连接和操作多个分片的数据库。 为了实现MySQL多数据源,可以采取以下几种方法: 1. 手动管理多个数据源:在应用程序中手动创建和管理多个数据库连接,每个连接对应一个不同的MySQL数据库。然后根据需要在应用程序中选择合适的数据库连接进行操作。这种方法相对简单,但需要自行处理连接管理和切换。 2. 使用连接池:可以使用连接池技术,如Apache Commons DBCP、HikariCP等,来管理多个数据库连接。通过配置连接池参数,可以设置多个连接池,每个连接池对应一个MySQL数据库。应用程序可以从连接池中获取合适的数据库连接进行操作。 3. 使用ORM框架:一些ORM框架,如Hibernate、MyBatis等,提供了支持多数据源的功能。通过配置多个数据源,应用程序可以使用框架提供的API来连接和操作多个MySQL数据库。 无论采用哪种方法,都需要在应用程序中进行相应的配置和代码编写,以实现对多个MySQL数据库的连接和操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值