一、事务简介
Transaction也就是所谓的事务,用大白话来说,就是一件事,要么全部成功,要么全部失败。
二、事务特性
原子性(Atomicity):事务里面的操作单元不可分割,要么全部成功,要么全部失败。就像化学里面的原子一样,原子是构成物质的最小单位,不能够在切分。
一致性(Consistency):事务执行前后,业务状态和其他业务状态保持一致。也就是说,张三转200元给李四,要么钱还在张三账户上,要么钱就是在李四账户上,不可能平白无故消失或者多出。
隔离性(Isolation):一个事务的执行最好不要受到其他事务的影响。
持久性(Durability):一旦事务提交或者回滚,这个状态都要持久到数据库中。
归纳:以上4条事务特性,把他们的英文首字母合起来就是ACID,这就是事务ACID特性。
三、读问题
不考虑隔离性,那么会出现读问题。
脏读:在一个事务中读取到另一个事务没有提交的数据。
不可重复读:在一个事务中,两次查询结果不一样(针对update操作)
幻读:在一个事务中,两次查询结果不一样(针对update操作)
四、隔离级别
通过设置数据库的隔离级别来避免读问题
读未提交(Read uncommitted)
全部读问题不可避免
事务中的修改即使没有提交也会被其他事务读取,这样会产生脏读,如果事务失败回滚,则其他事务之前读到的数据都是脏数据。从性能上来说,不会提高太多,但是及其不安全。推荐不用。
读已提交(Read committed)也叫不可重复读
可以避免脏读问题。
不能避免不可重复读:在事务完成提交之前,其他事务是看不到该事务的修改结果。执行两次同样的查询可能看到不一样的结果。
比如,张三和李四共同一个账户,账户里面有100元,张三和李四查看余额都发现有100元,这时李四购买商品花费了100元,张三再次查看账户余额,发现金额变0了,这是就出现两次查询出现不一样的结果。
但这种情况我们可以忽略,但有些场景不能使用。
比如购买电影票,现在查看这场电影还剩下10张票,开始选座位,选完座位提交下款的时候,发现电影票已经被人买完了。
大多数数据库使用隔离级别是读已提交。
可重复读(Repeatable read)
可避免脏读和不可重复读问题。
在当前事务中不能够读取到其他事务已经修改尚未提交的数据。
其它任何事务也不能修改在当前事务完成之前,由当前事务读取的数据。
不能避免幻读。
幻读造成的影响是:比如有个需求,需要查找出用户表数据,给全部用户发送一条祝福短信,这时候新注册了一个用户,那么新注册用户就收不了短信了。就会可能出现问题。
为什么Mysql的默认级别是Repeatable read?
mysql 默认Repeatable read主要是为了主从同步时,采用逻辑 sql 同步时的一致性,因为主库的 sql 是并发执行的,会有两个事务一起再跑,从库同步是单线程的,不会有两个事务同时在跑,如果不是 rr,主从数据就不一致了。
序列化(Serializable)
Serializable是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读。
Spring事务传播行为
传播行为 | 含义 |
REQUIRED | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 |
SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。 |
REQUIRED_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
NESTED | 支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。 |
Spring 给我们带来了事务传播行为,这确实是一个非常强大而又实用的功能。除此以外,也提供了一些小的附加功能,比如:
- 事务超时(Transaction Timeout):为了解决事务时间太长,消耗太多的资源,所以故意给事务设置一个最大时常,如果超过了,就回滚事务。
- 只读事务(Readonly Transaction):为了忽略那些不需要事务的方法,比如读取数据,这样可以有效地提高一些性能。
参考地址:https://my.oschina.net/huangyong/blog/160012