事务隔离级别和传播行为

事务隔离级别和传播行为

再次之前自认为我对数据库事务和隔离级别很熟悉了,至少以前整理过,看过网上很多文章,有些带有命令行操作的印象挺深,但是事务传播(包块调用外部系统服务)、分布式锁、mybatis一级缓存等一起存在业务代码中,导致了很多看起来听不可思议的问题,这都是线上案例。

项目中架构师是否跟你说类似的话,事务最好不要嵌套,为什么?如果这样的话事务传播应如何使用,为了方便本片先将事务相关的基础知识做下汇总:

数据库事务ACID特性

1)原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
2)一致性(Consistency)事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
3) 隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账(其实是隔离级别中的串行化)。
4)持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

事务4种隔离级别

隔离级别 脏读 不可重复读 幻读
Read Uncommitted Yes Yes Yes
Read Committed No Yes Yes
Repeatable Read No No Yes
Serializable No No No

1):Read Uncommitted 简单来说就是A读取B未提交内容(有可能回滚)【多个事务|修改操作】
2):Read Committed又叫做不可重复读(读取提交内容) 这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)【同一个事务,多次读取,读取了别人提交的事务导致两次查询结果不同|修改操作】
3): Repeatable Read(可重读) 这是MySQL的默认事务隔离级别解决了2)的问题但是引发了新的问题–幻读(Phantom Read):幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题【同一事务,多次读取|插入操作】。
4): Serializable(可串行化) 这是最高的隔离级别,它通过强制事务排序,并发写入后者会报错,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

数据库隔离级别网上资料太多了,有些人做了一些很有意思的实验(不是在代码中),弄两个命令行客户端,设置下隔离级别,A窗口开启事务修改,提交,B窗口读取。这种对你理解事务隔离级别还是比较有用,这里就不做过多的演示。

事务在代码中体现(给小白看的):

1) 一个事务多笔操作在,代码中就是service多次调用dao(insert、update)

2) service1有自己的dao同时又调用service2,service2也有dao操作,但是它们都是在同一个线程内,这样就导致了事务传播【service嵌套】。

3)service嵌套了之后内部事务失败,外部要如何处理,或者外部失败了内部如何处理,这就是靠事务传播行为来控制了。

事务7种传播行为

传播行为类型 说明
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

其中REQUIRES_NEW和NESTED容易混淆,看起来都是在原有事务的基础上再开一个嵌套事务,他们的区别在哪?嵌套事务机制到底是怎样,内外事务的提交和回滚分别是何时触发的?

REQUIRES_NEW会启动一个独立的新事务,这个事务将被完全 commited 或 rollback 而不依赖于外部事务,它拥有自己的隔离范围,自己的锁等等。
当内部事务开始执行时,外部事务将被挂起,内务事务结束时,外部事务将继续执行。
NESTED会开始一个 “嵌套的” 事务,它是已经存在事务的一个真正的子事务。 嵌套事务开始执行时,它将取得一个savepoint,如果这个嵌套事务失败,将回滚到此 savepoint。
嵌套事务是外部事务的一部分,只有外部事务结束后它才会被提交。

由此可见:

REQUIRES_NEW启动的新事务不依赖于外部事务,是完全独立的,这意味着事务commit和rollback操作都是独立的,不受外部事务commit或者rollback影响。
NESTED是依赖于外部事务的子事务,只有当外部事务commit时,子事务才能commit;外部事务发生异常rollback,子事务也要回滚。

我工作中只用过三种,PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED
先不管抛出异常的类型 rollbackFor=RuntimeException 为默认的
对于Service嵌套,无非就是内层service和外层service的注解@Transaction(propagation=)的组合,
有篇文章写的不错:Spring事务传播行为详解,这里也不做够多介绍,主要是为了下文的事务高级篇做铺垫的。

猜你喜欢

转载自blog.csdn.net/mayongzhan_csdn/article/details/80806167