一图看懂Spring事务传播的本质
一、事务传播本质
可能有点标题党,当可以作为一种思路。
总所周知,Spring事务传播有7种方式,我们经常会苦于记忆和理解。这是因为我们不理解他的本质,如下图:
同一个线程里关于事务的上下文会记录着当前线程拥有的MySQL Client socket,
每一个Socket只存在一个当前事务(后面会提到为什么)
事务的传播行为本质上是,当前service方法根据当先线程上下文是否存在事务,以此决定后续针对事务后续操作的行为。
最常见的行为,就是根据当前线程事务上下文决定是否创建新事务(如果有则,不做任务操作,等于跟当前线程的父方法共用一个事务; 如果没有,则创建一个新事务,只给自己service方法使用),这个就是 Requested 事务传播类型,也是最常见的。
后面还有,
-
根据事务上下文决定是否抛异常的极端操作。
-
根据事务上下文决定是否使用Spring的 “suspend事务机制”,创建新连接获取新事务。
-
根据事务上下文决定是否启用 Savepoint 机制 (需要Mysql服务器支持)。
二、个人观点:为什么不建议过分依赖 “事务传播”
事务是一种增强DB操作的行为,他可以做到,当操作发生异常的时候,尽量的避免脏数据,帮助我们减少修数据的工作,这是他的本质工作。
所以,
- 如果对事务没有要求的话,用 Support 类型事务传播,
- 如果对事务有要求的话,用 Request 类型事务传播。
有人说,
“吖,那要是我有个类似日志记录这种类型的操作,无论如何都不允许被事务会滚,该怎么处理?难道可以不用 Not-Support ?”
我的答案是,建议不用,因为会使得代码很晦涩,毕竟Not-support 用到了Spring自创的 “Suspend机制”,把这种有特殊诉求的方法,
用这种隐晦的机制去实现,可读性和维护性不是很高。
下面重点是研究 Spring 挂起事务的机制,当然我觉得这个很少用到,不看也是可以的。
三、什么是挂起事务?(MySQL可没有这个概念)
Mysql规定一个连接只能持有一个当前事务,
没有suspend挂起的概念,Spring如何实现的呢?
Spring在框架自身独创(不依赖DB)suspend机制,
所谓的挂起当前事务,开启新的事务,
是 Spring 又去申请了一个新的 Connection,这样就会对应到一个新的事务上,然后将这个连接绑定到当前线程,再继续执行;
当事务创建时,就会被绑定到一个线程上。该线程会伴随着事务整个生命周期,直到事务提交、回滚或挂起(临时解绑)。
线程和事务的关系是1:1,当线程绑定了一个事务后,其他事务不可以再绑定该线程,反之亦然(一个事务只能被一个线程拥有)。
四、为什么一个连接只能有一个MySQL innodb事务呢?
因为MySQL的请求是同步的,所有MySQL发起的客户端请求是同步串行发给MySQL服务器处理的,所以,一个MySQL连接有且只有一个当前事务。(这个是Innodb的设计使然,如果不考虑事务并发,它是一种简单易维护的设计)