事务原理与事务隔离级别

世界上一切好东西对于我们,除了加以使用外,实在没有别的好处。  ——《鲁滨逊漂流记》

1、引言

数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。
事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。

2、案例解析

本文以银行转账案例为例,如图所示为张三转账100元给李四,交易过程是,开始交易时,张三账户扣扣除100元,接着李四账户增加100元,然后结束交易。这是正常的交易逻辑。
这里写图片描述

(1)原子性

如果交易过程中,张三的账户扣除了100元,李四的账户没有增加100元,则这个过程存在问题;如果交易过程中,张三的账户没有扣除100元,李四的账户增加100元,也同样有问题。

所以整个交易过程,必须是一个整体,不可分割,要么全做,要么全不做,即这是事务的原子性。
这里写图片描述

原子性 (Atomic)(Atomicity):事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。通常,与某个事务关联的操作具有共同的目标,并且是相互依赖的。如果系统只执行这些操作的一个子集,则可能会破坏事务的总体目标。原子性消除了系统处理操作子集的可能性。

(2)一致性

在交易前,张三账户100元,李四0元;在交易后,张三账户0元,李四账户100元。交易前后整个钱数不变,则是事务的一致性。
这里写图片描述

一致性 (Consistent)(Consistency):事务在完成时,必须使所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。事务结束时,所有的内部数据结构(如B树索引或双向链表)都必须是正确的。某些维护一致性的责任由应用程序开发人员承担,他们必须确保应用程序已强制所有已知的完整性约束。例如,当开发用于转帐的应用程序时,应避免在转帐过程中任意移动小数点。

(3)隔离性

如图所示,交易一与交易二并发进行,交易一中读取张三账户余额100元,交易二中也读取张三账户余额为100元,交易一中因为张三给李四转账100元,则更新张三账户余额为0元,而交易二中,因为赵五给张三转账200元,则余额为300元。这里出现了隔离性的问题,即两个交易同时执行时,一个交易进行时不能受到另一个交易的影响。
这里写图片描述

隔离性 (Insulation)(Isolation):由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。这称为隔离性,因为它能够重新装载起始数据,并且重播一系列事务,以使数据结束时的状态与原始事务执行的状态相同。当事务可序列化时将获得最高的隔离级别。在此级别上,从一组可并行执行的事务获得的结果与通过连续运行每个事务所获得的结果相同。由于高度隔离会限制可并行执行的事务数,所以一些应用程序降低隔离级别以换取更大的吞吐量。

(4)持久性

当银行系统发生故障时,交易不能无效,需要保证故障前和故障后的数据一致性,这就是持久性。
这里写图片描述
持久性

(Duration)(Durability): 事务完成之后,它对于系统的影响是永久性的。该修改即使出现致命的系统故障也将一直保持。

3、事务的隔离性案例分析

本节通过银行转账存钱的案例介绍下事务的隔离级别,如图,张三转账100元给李四,然后张三又存银行100元。
这里写图片描述

(1)脏读

如图,T1事务为转账事务,T2事务为存钱事务,它们并发执行。
T1读取张三的账户余额为100元,然后转账100元给李四,则更新张三账户余额为0元,T2此时读取张三账户余额为0元,此时存入200元,则更新张三账户余额为200元,T2事务结束,但是T1的事务失败导致回滚。这种读取事务的中间状态则为脏读。
这里写图片描述

脏读:读“脏”数据是指事务T1修改某一数据,并将其写回磁盘,事务T2读取同一数据后,T1由于某种原因被除撤消,而此时T1把已修改过的数据又恢复原值,T2读到的数据与数据库的数据不一致,则T2读到的数据就为“脏”数据,即不正确的数据。即脏读是指一个事务读取了另一个事务未提交的更新。

(2)不可重复读

T1事务第一次读取张三的账户余额为100元,T2读取张三的账户余额为100元,存入200元,T2则更新张三的账户余额为300元,此时T1再次读取张三账户余额为300元,两次读取相同记录结果不一样则为不可重复读。
这里写图片描述

不可重复读:指事务T1读取数据后,事务T2执行更新操作,使T1无法读取前一次结果。不可重复读包括三种情况:事务T1读取某一数据后,T2对其做了修改,当T1再次读该数据后,得到与前一不同的值。同一次事务中两次读取相同的记录,结果不一样则为不可重复读。

(3)幻读

T1读取所有用户,用户列表只有两个用户,T2新增一个用户,T1再次读取所有用户,此时三个用户。两次读取的结果包含的行记录数不一样,则为幻读。
这里写代码片

按一定条件从数据库中读取了某些记录后,T2删除了其中部分记录,当T1再次按相同条件读取数据时,发现某些记录消失T1按一定条件从数据库中删除某些数据记录后,T2插入了一些记录,当T1再次按相同条件读取数据时,发现多了一些记录。两次读取的结果包含的行记录数不一样,则为幻读。

幻读是两次读取结果包含的行记录数不一样,不可重复读是两次读取结果的值不一样。

事务隔离级别

(1)读未提交(read uncommitted)

一个事务可以读取另一个未提交事务的数据。即允许脏读。

(2)读提交(read committed)

一个事务要等另一个事务提交后才能读取数据。不允许脏读,但允许重复读。

(3)重复读(repeatable read)

在开始读取数据(事务开启)时,不再允许修改操作。即不允许重复读,但允许幻读。(MySQL默认的事务隔离级别,事务隔离界别越高,数据库性能越差)

(4)串行化(serializable)

Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。

猜你喜欢

转载自blog.csdn.net/zhoutaochun/article/details/79890274