Spring 下,关于动态数据源的事务问题的探讨
开心一刻
毒蛇和蟒蛇在讨论谁的捕猎方式最高效。
毒蛇:我只需要咬对方一口,一段时间内它就会逐渐丧失行动能力,最后死亡。
蟒蛇冷笑:那还得等生效时间,我只需要缠住对方,就能立刻致它于死地。
毒蛇大怒:你缠它身子,你下贱!
蟒蛇:你不也亲了它吗?
前情回顾
看着文章的标题,不知道大家能否想到具体是什么问题,如果你有点懵,那就对了! (你不懵的话我这篇文章就没存在的意义了,嘿嘿)
在给大家指出具体是什么问题时,我们先来回顾一些内容
spring 事务原理
相信大家对这个都能说上来一些,spring 事务是 spring aop 的一种具体应用,底层依赖的是动态代理
大致流程类似如下
通过代理对象来调用目标对象,而在代理对象中有事务相关的增强处理
具体细节可参考以下文章
结合 threadlocal 来看 spring 事务源码,感受下清泉般的洗涤!
spring 动态数据源原理
原理解密 → spring aop 实现动态数据源(读写分离),底层原理是什么中已经详细介绍过了,流程大体如下
spring aop → 将我们指定的 lookupkey 放入 threadlocal
threadlocal → 线程内共享 lookupkey
dynamicdatasource → 对多数据源进行封装,根据 threadlocal 中的 lookupkey 动态选择具体的数据源
有什么问题
既然事务和动态数据源都是 spring aop 的具体应用,那么代理就存在先后顺序了
要么是
要么是
我们来看看这两者有什么区别
事务在前,动态数据源在后
此时,事务的前置增强处理会先生效,那么此时开始事务获取的 connection 从哪来 ? 肯定是从 dynamicdatasource 来,因为我们给事务管理器配置的就是它
@bean public platformtransactionmanager transactionmanager(@qualifier("dynamicdatasource") datasource dynamicdatasource) { // 配置事务管理, 使用事务时在方法头部添加@transactional注解即可 return new datasourcetransactionmanager(dynamicdatasource); }
既然是从 dynamicdatasource 获取的 connection,那 dynamicdatasource 根据 lookupkey 获取 connection 的时候,会从 masterdatasource 数据源获取还是从 slavedatasource 数据源获取 ?因为此时还未将 lookupkey 绑定到当前线程,那么 dynamicdatasource 会从默认数据源获取,而我们配置的默认数据源是 slavedatasource
/** * 获取当前线程的数据源 * @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 注解中
/** * 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
@component @slf4j @order(1) public class dynamicdatasourceaspect {
我们在来看看是否真的可行
已经不是默认的 slavedatasource ,而是我们指定的 masterdatasource(通过 @masterslave(master) 指定)
至此,相信大家已经弄清楚了有什么问题,以及如何解决它
什么,还没理解 ? 你过来,我保证不打死你
总结
1、不只是动态数据源和事务,只要涉及到多个 aop,就可能会有顺序问题,这是值得大家注意的
2、相关约束
主数据库执行 insert update delete 操作,可能还有部分 select 操作(主从同步多少有延时)
从数据库只执行 select 操作
默认数据源最好设置成主数据源,防止粗心将更新操作执行到了从数据库;楼主之所以设置成从数据源,是考虑到绝大多数数据库操作是查询,这样可以减少代码量;具体怎么选,需要大家结合实际情况来决定
上一篇: 中缀表达式转后缀表达式