什么是事务?
事务(Transaction),在计算机术语中,指将多个单独的操作组合在一起,其中一个操作执行失败,所有执行的操作全部撤回,只有当所有操作全部执行成功,才算成功,这多个操作组合成的整体叫做事务;
例如:A给B转账100元,分两个操作,A减100元,B加100元,只有当这两个操作都执行成功,这个转账事务才算成功;如果B加100元失败,A则必须回退减100元操作。
事务的四大特性
特性 | 描述 |
---|---|
原子性(Atomicity) | 指事务是一个不可分割的单位,事务中的操作要么全部成功,要么全部失败 |
一致性(Consistency) | 事务执行前后,数据从一个一致性状态变为另一个一致性状态(数据按照预期变化) 例如:A给B转账100元事务 事务前状态:A有1000元,B有1000元; 事务后状态:A有900元,B有1100元; |
隔离性(Isolation) | 在多个事务同时执行过程中,各个事务之间的影响;这里又分为四个隔离级别,我们后面会讲到; 例如:脏读问题 1. A发起事务1,查询账户有1000元,此时事务1未关闭; 2. B发起事务2,给A转账100元,此时事务2也未关闭; 3. A在事务1中,再次查询账户,发现有1100元,读到了事务2未提交的数据; 这是脏读问题。 |
持久性(Durability) | 指事务一旦提交成功,他对数据的改变是永久性的,接下来即使服务器崩溃关闭,也不会对数据有任何影响。 |
事务的隔离级别
事务并发问题
- 脏读:
一个事务,读取到另一个事务未提交的数据 - 不可重复读:
一个事务读取到另一个事务已经提交的Update数据(事务内前后查询同一个数据不一致)- 场景:
a. A开启事务1,查询账户有1000元,未关闭事务1
b. B开启事务2,给A转账100元,提交事务2
c. A在事务1内再次查询账户,有1100元,和第一次查询数据不一致;
- 场景:
- 幻读(虚读):
一个事务读取到另一个事务已经提交的Insert和Delete数据(事务内前后统计同一条件数据条数不同)- 场景:
a. 事务1查询员工总数有10人
b. 事务2新增2个员工,提交事务2
c. 事务1再次查询员工有12人
- 场景:
数据库定义四种隔离级别
隔离级别依次提升,数据安全性依次提高,数据库查询效率依次降低
- 读未提交(read-uncommitted)
在一个事务中,可以读取其他事物未提交的数据变化;这种能读取其他事物未提交的数据变化的现象叫脏读,生产环境请勿使用。 - 读已提交(read-committed)
在一个事务中,可以读取其他事物已提交的数据变化;由于同一事物中两次读取可能得到不一样的结果,所以也交不可重复读。 - 可重复读(repeatable-read)
Mysql的默认隔离级别,在一个事务中,可以重复读取到事务刚开始时看到的数据,并不会因为其他事物提交数据而改变,避免了脏读,不可重复读;但是他无法解决幻读问题。 - 串行化(serializable)
这是最高的隔离级别,它强制事务串行执行,避免了幻读;它会在每一行数据上加锁,,多以会导致大量超时和锁争用问题。
隔离级别 | 读数据一致性 | 脏读 | 不可重复的 | 幻读 |
---|---|---|---|---|
未提交读 | 最低级别,只保证不读取物理上损坏的数据 | 有 | 有 | 有 |
已提交读 | 语句级 | 无 | 有 | 有 |
可重复读 | 事务级 | 无 | 无 | 有 |
串行化 | 最高级别,事务级 | 无 | 无 | 无 |
扩展:
Mysql使用了快照读,当前读
,MVCC,Next-Lock,GAP锁
等思想实现了可重复读
事务的传播行为
事务的控制往往是在程序的Service
层,而Service
层的方法我们有时候,需要互相调用,而每个Service
方法都可能有自己的事务,这时就需要事务传播行为的概念。
以下我们讲解一下Spring
定义的事务传播
事务定义 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中, 加入到这个事务中。这是最常⻅的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
注意:
PROPAGATION_REQUIRES_NEW
和PROPAGATION_NESTED
是独立事务,但是如果被调用方法事务回滚后异常抛出,而外部方法没捕获还是会导致外部方法事务回滚;只有try_catch处理后外部方法事务才不会回滚PROPAGATION_NESTED
作为事务嵌套,同时兼具了required,requires_new的特性。- 当外层事务回滚时
PROPAGATION_NESTED
与PROPAGATION_REQUIRED
同时内层全部进行回滚 - 当内层事务回滚时,
PROPAGATION_NESTED
与PROPAGATION_REQUIRES_NEW
不影响外层事务
- 当外层事务回滚时