前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring 下,关于动态数据源的事务问题的探讨

Spring 下,关于动态数据源的事务问题的探讨

作者头像
好好学java
发布2020-09-24 11:34:13
1.1K0
发布2020-09-24 11:34:13
举报
文章被收录于专栏:好好学java的技术栈
代码语言:javascript
复制
重磅资讯、干货,第一时间送达
今日推荐:Nginx 为什么快到根本停不下来?个人原创100W+访问量博客:点击前往,查看更多

作者:青石路

cnblogs.com/youzhibing/p/12671004.htm

前情回顾

看着文章的标题,不知道大家能否想到具体是什么问题,如果你有点懵,那就对了!(你不懵的话我这篇文章就没存在的意义了,嘿嘿)

在给大家指出具体是什么问题时,我们先来回顾一些内容

Spring 事务原理

相信大家对这个都能说上来一些,Spring 事务是 Spring AOP 的一种具体应用,底层依赖的是动态代理

大致流程类似如下

通过代理对象来调用目标对象,而在代理对象中有事务相关的增强处理

具体细节可参考以下文章

  • https://www.cnblogs.com/youzhibing/p/6414780.html
  • https://www.cnblogs.com/youzhibing/p/6690341.html
  • https://www.cnblogs.com/youzhibing/p/12071391.html

Spring 动态数据源原理

原理解密 → Spring AOP 实现动态数据源(读写分离),底层原理是什么中已经详细介绍过了,流程大体如下

Spring AOP → 将我们指定的 lookupKey 放入 ThreadLocal

ThreadLocal → 线程内共享 lookupKey

DynamicDataSource → 对多数据源进行封装,根据 ThreadLocal 中的 lookupKey 动态选择具体的数据源

有什么问题

既然事务和动态数据源都是 Spring AOP 的具体应用,那么代理就存在先后顺序了

要么是

要么是

我们来看看这两者有什么区别

事务在前,动态数据源在后

此时,事务的前置增强处理会先生效,那么此时开始事务获取的 Connection 从哪来 ?肯定是从 DynamicDataSource 来,因为我们给事务管理器配置的就是它

代码语言:javascript
复制
    @Bean
    public PlatformTransactionManager transactionManager(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) {
        // 配置事务管理, 使用事务时在方法头部添加@Transactional注解即可
        return new DataSourceTransactionManager(dynamicDataSource);
    }

既然是从 DynamicDataSource 获取的 Connection,那 DynamicDataSource 根据 lookupKey 获取 Connection 的时候,会从 masterDataSource 数据源获取还是从 slaveDataSource 数据源获取 ?因为此时还未将 lookupKey 绑定到当前线程,那么 DynamicDataSource 会从默认数据源获取,而我们配置的默认数据源是 slaveDataSource

代码语言:javascript
复制
    /**
     * 获取当前线程的数据源
     * @return
     */
    public static DataSourceType getDataSourceType()
    {
        return  HOLDER.get() == null ? DataSourceType.SLAVE : HOLDER.get();
    }

说白了,此时的动态数据源对事务不生效,事务始终从默认数据源获取 Connection,而没有动态的效果,这就是问题了

Talk is cheap. Show me the code,我们来看看是不是真的如上所说

192.168.0.112 正是我们的从库,对应的就是我们的默认数据源 slaveDataSource

动态数据源在前,事务在后

此时,动态数据源的前置增强会先执行,DynamicDataSource 需要的 lookupKey 会先于事务绑定到当前线程,那么事务从 DynamicDataSource 获取 Connection 的时候就能根据当前线程的 lookupKey 来动态选择 masterDataSource 还是 slaveDataSource

此种情况是没有问题的

解决问题

总结下问题:如何保证事务中的动态数据源也有动态的效果,也就是如何保证动态数据源的前置增强先于事务

我们知道 Spring AOP 是能够指定顺序的,只要我们显示的指定动态数据源的 AOP 先于 事务的 AOP 即可;如何指定顺序,常用的方式是实现 Order 接口,或者使用 @Order 注解,Order 的值越小,越先执行,所以我们只需要保证动态数据源的 Order 值小于事务的 Order 值即可

我们先来看看事务的 Order 值默认是多少,在 EnableTransactionManagement 注解中

代码语言:javascript
复制
    /**
     * Indicate the ordering of the execution of the transaction advisor
     * when multiple advices are applied at a specific joinpoint.
     * <p>The default is {@link Ordered#LOWEST_PRECEDENCE}.
     */
    int order() default Ordered.LOWEST_PRECEDENCE;

默认是最低级别(值非常大),那么我们只需要保证动态数据源的 Order 比这个值小就好,我们就取 1

代码语言:javascript
复制
  @Component
  @Slf4j
  @Order(1)
  public&nbsp;class&nbsp;DynamicDataSourceAspect&nbsp;{

我们在来看看是否真的可行

已经不是默认的 slaveDataSource ,而是我们指定的 masterDataSource(通过 @MasterSlave(MASTER) 指定)

至此,相信大家已经弄清楚了有什么问题,以及如何解决它

什么,还没理解 ?你过来,我保证不打死你

总结

1、不只是动态数据源和事务,只要涉及到多个 AOP,就可能会有顺序问题,这是值得大家注意的

2、相关约束

  • 主数据库执行 INSERT UPDATE DELETE 操作,可能还有部分 SELECT 操作(主从同步多少有延时)
  • 从数据库只执行 SELECT 操作
  • 默认数据源最好设置成主数据源,防止粗心将更新操作执行到了从数据库;楼主之所以设置成从数据源,是考虑到绝大多数数据库操作是查询,这样可以减少代码量;具体怎么选,需要大家结合实际情况来决定

最后,再附上我历时三个月总结的 Java 面试 + Java 后端技术学习指南,笔者这几年及春招的总结,github 1.4k star,拿去不谢!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 好好学java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前情回顾
    • Spring 事务原理
      • Spring 动态数据源原理
      • 有什么问题
        • 事务在前,动态数据源在后
          • 动态数据源在前,事务在后
          • 解决问题
          • 总结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档