前言
谈到事务,那是面试必问的问题,事务比较抽象,他可以是一条sql,也可以是一段程序,如果没有事务这个概念,我们在编程过程中的复杂度会大大增加,比如一个很贴近生活的例子:
你的爸爸妈妈同时像你支付宝里转了500块钱,你爸爸的转账请求读到你当前钱包里有1000块钱,与此同时你妈妈的转账请求也读到了你钱包的余额,于是一起对余额进行操作,1000+500=1500,于是你的余额变成了1500块。
之后你爸爸打电话问:“儿子给你转了500块,收到了吗?”
再过了一会你妈妈也打电话过来问:“儿子给你转了500块,多买点吃的。”
这你就纳闷了,明明爸妈都给我转了500余额应该是2000才对,为什么才1500?
这就是脏读现象,当一个事务修改了数据还未提交的时候,另一个事务使用该数据。如果这个问题不处理那支付宝不是大乱套了。
事务的特性
- 原子性(atomicity):一个事务是不可分割的,一个事务里的操作,要么全部成功,要么全部失败。
- 一致性(consistency):一个事务执行前后数据的完整性保持一致。
- 隔离性(isolation):一个事务在执行过程中不该受到其他事务影响,每个事务是相互隔离的,互不影响。
- 持久性(durability):指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
多事务并发下的问题
- 脏读:就是一个事务修改了数据,在未提交数据前,另一个事务使用了该数据。
- 不可重复读:就是一个事务第一次查看数据,比如说是3,然后一个事务进来修改为了5并提交了事务,第一次的事务再次读的时候发现变成了5就是不可重复读。
- 幻读:在对全表进行操作的时候,发现修改了100条记录,在此期间有另一个事务进来新增了一条数据,最后之前明明为100条的数据,发现还有没有被修改的数据行,就跟产生了幻觉一样。
数据库隔离级别
- 读未提交:数据库最低隔离级别,可能会发生脏读,不可重复读,幻读。
- 读已提交:不会发生脏读,可能发生不可重复读和幻读。
- 可重复读:可以避免脏读和不可重复读,无法避免幻读,mysql默认的隔离级别。
- 串行化:数据库读最高隔离级别,可以避免幻读,但是会导致大量超时现象和锁竞争。
Spring下的事务传播机制
事务传播行为,既然是传播肯定要有两个东西,才可能发生传播的行为,指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何执行。
传播行为 | 含义 |
---|---|
Propagation_Required | 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则会启动一个新事务。 |
Propagation_Supports | 表示当前方法不需要事务上下文,但如果存在当前事务的话,那么该方法会在这个事务中运行。 |
Propagation_Mandatory | 表示这个方法必须在事务中运行,如果事务不存在,则会抛出一个异常。 |
Propagation_Required_New | 表示当前方法必须运行在自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务将会被挂起。 |
Propagation_Not_Supported | 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。 |
Propagation_Nerver | 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常。 |
Propagation_Nested | 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与Propagation_Required一样。 |
Propagation_Required:
默认到事务传播方式,指该方法必须在事务中运行,如果当前没有事务就创建一个事务,如果当前有事务就加入到当前事务中去。
1 | (propagation = Propagation.REQUIRED) |
单独调用方法B的时候,会发现上下文中没有事务,则会开启一个事务,当调用方法A的时候,发现上下文环境中没有事务,就会新建一个事务,当调用到方法B的时候,发现已经有A事务的上下文环境就加入到A事务中。
Propagation_Supports:
指该方法不需要事务上下文环境,如果没有上下文环境就非事务执行,如果有上下文环境就加入到该事务中。
1 | (propagation = Propagation.REQUIRED) |
单独调用方法B到时候是以非事务的方式执行方法B,当调用方法A的时候,方法B就加入了方法A的事务中事务的执行。
Propagation_Mandatory:
指方法必须在事务中进行,如果当前没有事务则会抛出异常
1 | (propagation = Propagation.REQUIRED) |
当单独调用方法B的时候,会因为没有事务的上下文,会抛出IllegalTransactionStateException异常,当调用方法A的时候就会加入事务A中。
Propagation_Required_New:
表示当前方法必须运行在自己的事务中,一个事务将被启动,如果存在当前事务,在方法执行期间,当前事务会被挂起。使用PROPAGATION_REQUIRES_NEW,需要使用 JtaTransactionManager作为事务管理器。
1 | (propagation = Propagation.REQUIRED) |
单独调用方法B的时候会新建一个事务运行,当调用方法A的时候,执行到B方法的时候会将事务A挂起,等B提交后,再执行doSomeThingB
,如果doSomeThingB
执行失败了不会回滚事务B,只会回滚事务A里除了事务B之外的东西。
Propagation_Not_Supported:
表示该方法不应该运行在事务上下文中,如果存在当前事务,当前事务将会被挂起
Propagation_Nerver:
表示当前方法不应该在事务上下文中,如果当前正有一个事务在运行,就会抛出异常
Propagation_Nested:
如果当前已经存在一个事务,那么该方法会在嵌套事务中运行,嵌套的事务可以独立于当前事务进行单独地提交或回滚,如果当前事务不存在,那么其行为与Propagation_Required一样
外层事务失败的时候,会回滚内层事务所做的动作。但是内层事务失败并不会引起外层事务的回滚。