事务
事务是什么?
在知道事务的相关信息之前,我们需要先理解:什么是事务?
事务是一组不可再分的最小的执行单元,即事务内的数据,必须要么全部执行完成,要么不执行,即可为不可再分。
Mysql中只有支持事务的存储引擎,才可以使用事务,例如InnoDB。
事务的四个特性
原子性
事务的原子性要求事务内的一组操作是原子操作,不可中断的,如果发生了中断,则所有的修改都会被回滚。即只有所有的操作都是成功的,这个事务才会成功。
例如一个转账的操作,分为了两个部分:从A账户扣除金额,B账户加上相应的金额。只有两个部分的操作都成功,则整个事务才算成功,转账操作才算完成。如果第一部分成功,第二部分失败,则整个事务会失败,A账户的金额也不会减少,B账户的金额也不会增多。
一致性
一致性是指:在执行事务的前后,数据库是从一个一致性状态,转移到另一个一致性状态。即数据库的一致性,不会因为事务的执行而破坏。
例如转账,假如转账前,A和B的账户总额是100元,转账后A和B的账户总额也应该是100元,不会增多,也不会减少。
隔离性
隔离性是指在多个事务并行执行的时候,多个事务之间不会相互影响,即A事务的操作,对与B事务的操作是透明的。
例如转账的时候,如果有A账户向B账户转账,则不能影响C账户向A账户转账。
持久性
持久性是指在事务的执行后的操作结果,对与后续的操作都是可见的,并不会随着服务器的重启,而导致数据不可见。
事务的实现
原子性
事务的原子性由redo log 实现。在提交事务的时候,会使用到两阶段提交的方式提交数据,如果在提交的过程中,服务器发生了意外的宕机,当服务器启动的时候,会通过 redo log 去恢复内存中的数据,如果发现redo log中的数据并未完全提交,会直接回滚之前的操作。
一致性
Mysql会使用 undo log 来保证数据的一致性状态。如果事务在执行过程中失败,或发生未知错误的时候,会使用 undo log 回滚之前的操作。在事务隔离级别中,也会使用 undo log 去实现Mysql的MVCC机制,实现可重复读事务隔离级别。
隔离性
隔离性的保证是使用锁的机制实现,如果多个事务之间在修改同一条数据的时候,会使用锁和MVCC的方式,保证当前事务修改的数据,对其他事务没有影响。
例如如果一个事务在读取的一个数据,正在被其他事务删除或者修改,则Mysql不会等待锁的释放,而是会去读取他的快照版本,来获取数据。
持久性
持久性的实现也会使用 redo log,Mysql修改数据的时候,并不会立马直接将修改的数据落盘,而是通过写 redo log 的方式,先保证数据的修改在redo log 中存在,然后会有后台线程在适当的时候,将内存中修改的数据落盘。
为什么需要事务?
在一组操作中,如果需要一组操作要么同时成功,要么同时失败的时候,就需要使用事务。例如转账过程,如果从A中扣除了金额,但是在B中加上金额之前,服务器意外宕机,导致操作中断,如果此时未使用事务,就会导致A账户的钱少了,但是B账户的钱并未增多,出现错误。
使用事务的时候,可以借助事务的四个特性,保证数据的一致性和完整性,避免脏数据的产生。
事务的隔离级别
并发事务带来的问题
数据更新丢失:如果两个事务之间,互相无感知或者互相不知道对方的存在,就会导致第二个事务在更新的时候,会覆盖掉第一个事务更新的数据,导致数据丢失。
脏读:脏读是指事务在第二次读取到的数据和第一次读取到的数据不一致。因为读取的事务是未提交的事务,所以事务如果回滚,就会导致第二次读取的数据和第一次读取的数据不一样,就产生的脏读的现象。
不可重复读:不可重复读是事务在查询的两次查询中,两次查询的结果不一致。例如,如果A事务在第一次读取数据的时候,读取的是正常的数据,如果此时B事务去修改了他,A事务查出来的数据是B事务修改后的指,导致两次查询的数据不一致。
幻读:幻读是指一开始A事务去查询数据的时候,无法查询到,但是在A事务第二次查询数据的时候,由于其他事务插入了一个新的数据,A事务又可以查询到数据,产生幻读。
- 读未提交
读未提交是指,一个事务可以读取另一个没有提交的事务中修改的数据。例如,A事务可以读取B事务中修改的数据,但是此时事务B还未提交。
因为B事务还未提交,所以就会导致脏读、幻读和不可重复读。
- 读已提交
读已提交是指一个事务只能读取其他事务已经提交的数据,对于未提交的数据,不会查询到,可以解决脏读的情况,但是会产生不可重复读和幻读的情况。
- 可重复读
可重复读:可重复读也只能读取已经提交的数据,对于未提交的数据无法读取。然后借助了MVCC机制,增加了版本链和修改了ReadView的创建时期,解决了不可重复读的情况,具体参考MVCC机制。
- 串行化
要求最高的一个事务隔离级别,要求所有的事务必须串行化执行,因此并发效率特别低。
事务的分类
扁平事务
扁平事务是最简单的一种事务,他的所有的操作都是在一个层面,其由 begin work开始,由rollback 或者是commit结束。因此其间的操作都是要么都完成,要么都不会完成。
带保存点的扁平事务
带保存点的扁平事务是指,在扁平事务的基础上,增加了保存点的概念。如果一个事务的执行数据比较多,当执行到某一部分,发生错误的时候,如果此时全部回滚,产生的代价比较高,并且局部回滚是可以接收的情况下,可以使用保存点的概念。可以将执行会滚到之前设置的任意一个保存点的位置,则回滚保存点的位置和回滚命令之间的操作都会回滚,其他的操作不会有影响。最终提交的时候,只会提交未回滚的操作。
链事务
链事务更像是带有保存点的扁平事务的一个变种。当带保存点的扁平事务在执行的时候,如果系统崩溃,则所有的保存点都会消失,事务需要重新执行。链事务更像是把保存点当做是一个链接的位置,当上一个事务提交的时候,会把一些必要的数据,传递给下一个事务,并且上一个事务的结束和下一个事务的开始将会原子性的执行,更像是在同一个事务中一样。链事务的方式如下图所示
嵌套事务
嵌套事务是指一个事务中,可以包含子事务,子事务还可以继续包含子事务,形成一个事务树。当子事务回滚的时候,只会回滚子事务中的操作。当父事务回滚的时候,会回滚该父事务,和父事务下的子事务。子事务提交的时候不会真正的提交,只有在父事务提交的时候,才会一起提交。
分布式事务
分布式事务通常情况下是在分布式环境中运行的扁平事务。在分布式环境下,对与数据的操作可能分布在不同的节点上,如果此时事务需要回滚的时候,需要两个节点的数据同时进行回滚操作。