6.3.2 数据库并发控制概述

6.3.2 数据库并发控制概述

前面讲到事务是并发控制的基本单位,保证事务的ACID特性是事务处理的重要任务,而事务的ACID特性遭到破坏的原因之一是多个事务对数据库的并发操作造成的。为了保证事务的隔离性,DBMS需要对并发的事务进行正确的调度。下面将主要讨论对事务的并发操作导致哪些数据库的不一致性以及如何判断对并发事务的调度是正确的。

1)并发操作带来的数据不一致性

考虑常见的火车售票系统的活动序列:

步骤1:甲客户端(事务T1)读出某趟列车的二等座余票A,设A=20;

步骤2:乙客户端(事务T2)读出同一趟列车的二等座余票A,也为20;

步骤3:甲客户端买了一张二等座的票,修改余票A←A-1,将A=19写回数据库;

步骤4:乙客户端也买了一张二等座的票,修改余票A←A-1,也将A=19写回数据库。

结果明明卖出了2张二等座的票,数据库中的余票只减少了1。这种情况称为数据库的不一致性。是由多个事务对相同的数据进行并发操作引起的。在并发操作的情况下,对T1、T2两个事务的操作序列的调度是随机的,若按上面的调度序列执行,则T1事务的修改就被丢失。这是由于步骤4中T2事务修改A写回后覆盖了T1事务的修改。

并发操作带来的数据不一致性包括3类:丢失修改、不可重复读和读“脏”数据。

(1)丢失修改

丢失修改(Lost Updates)是指两个事务T1和T2读入同一数据并修改,T2提交的结果破坏了T1提交的结果,导致T1的修改被丢失,如图6-10(a)所示。上面火车售票例子就属于此类。

图6-10 3种数据不一致性

(2)不可重复读

不可重复读(Non-Repeatable Read)是指事务T1读取数据后,事务T2执行更新操作,使T1无法再现前一次读取结果。具体来讲,有3种情况:

①事务T1读取某一数据后,事务T2对其进行了修改,当事务T1再次读取该数据时,得到与前一次不同的值。例如在图6-10(b)中,T1读取B=100进行运算,T2读取了同一数据B,对其进行修改后将“B=200”写回数据库。T1为了对读取值校对重新读B,B已为200,与第一次读取的值不同。

②事务T1按一定条件从数据库中读取某些数据记录后,事务T2删除了其中部分记录,当T1再次按相同的条件读取数据时,发现某些记录神秘地消失了。

③事务T1按一定条件从数据库中读取某些数据记录后,事务T2插入了一些记录,当T1再次按相同条件读取数据时,发现多了一些记录。

(3)读“脏”数据

读“脏”数据(Dirty Read)是指事务T1修改某一数据,并将其写回磁盘,事务T2读取同一数据后,T1由于某种原因被撤销,这时T1已修改过的数据恢复原值,T2读到的数据就与数据库中的数据不一致,则T2读到的数据为“脏”数据,即不正确的数据。图6-10(c)中T1将C值修改为200,T2读到C为200,而T1由于某种原因撤销,C恢复原值100,这时T2读到的C的值200为不正确的数据。

产生上述3类数据不一致性的主要原因是并发操作破坏了事务的隔离性。并发控制就是要用正确的方式调度并发的事务,使一个用户事务的执行不受其他事务的干扰,从而避免造成数据的不一致性。

另一方面,对数据库的应用有时运行某些不一致性,例如有些统计工作涉及数据量很大,读到一些“脏”数据对统计精度影响不大,这时可以降低对一致性的要求以减少系统开销。

2)事务调度的可串行性

为了保证隔离性,一种方式是所有事务串行执行,让事务之间不互相干扰。但是串行执行效率非常低,为了增大吞吐,减小响应时间,数据库通常允许多个事务同时执行。因此并发控制模块需要保证:事务并发执行的效果,与事务串行执行的效果完全相同,以达到隔离性的要求。

多个事务的并发执行是正确的,当且仅当其结果与按某一次序串行执行它们时的结果相同,称这种调度策略为可串行性的调度。可串行性是并发事务正确性的准则。

为了方便描述并发控制如何保证隔离性,人们简化事务模型。事务是由一个或多个操作组成,所有的操作最终都可以拆分为一系列读和写。一批同时发生的事务,所有读、写的一种执行顺序,被定义为一个调度,例如:T1、T2同时执行,一个可能的调度:T1.read(A),T2.read(B),T1.write(A),T1.read(B),T2.write(A)。

一个调度中,如果相邻的两个操作调换位置导致事务结果变化,那么这两个操作就是冲突的。冲突需要同时满足以下条件:

这两个操作来自不同事务。

至少有一个是写操作。

操作对象相同。

数据库只要保证,并发事务的调度,保持冲突操作的执行顺序不变,只调换不冲突的操作,可以成为串行化调度,就可以认为它们等价。这种等价判断方式称为冲突等价,即两个调度的冲突操作顺序相同。在图6-11(a)中,T1.write(A)与T3.read(A)冲突,且T1先于T3发生。T1.read(B)和T2.write(B)冲突,且T2先于T1发生,与图6-11(b)中T2,T1,T3串行执行的调度是等价的,因此图6-11(a)是可串行化的调度。

图6-11 并发事务的调度

并不是所有的调度都是可串行化的,在图6-11(c)中,T1.read(A)与T2.wirte(A)冲突且T1先于T2,T1.write(A)与T2.read(A)冲突,且T2先于T1,因此无法与任何一个串行化调度等价,是一个不可串行化的调度,会造成丢失修改的问题。

为了保证并发操作的正确性,DBMS的并发控制机制要提供一定的手段来保证调度是可串行化的。总的来说,可串行化是比较严格的要求,为了提高数据库系统的并发性能,很多用户愿意降低隔离性的要求来寻求更高的性能。数据库系统往往会实现多种隔离机制供用户灵活选择。并发控制的要求清楚了,后面将根据冲突检测的乐观程度,介绍并发控制常见的实现方法。