事务隔离级别详解

      事务的四种隔离级别

 

    大多数事务系统使用的SQL标准中定义了四种隔离级别:可串行化、不可重复都、读已提交和读未提交。我们通过下面的这个例子来对这四种隔离级别进行详细的说明。

 

 

场景:假如资源库里有两个包,分别是加锁包和多步提交包,其中加锁包中的文件个数是7个,而多步提交包中的文件个数是5个。现在Martin将要统计资源目录中的所有包的个数,而同时,David要向资源库里更新包的个数,即David将要向加锁包里再提交2个文件,向多步提交包里在提交3个文件。

由于,Martin的统计和David的更新是并发执行的,所以,下面将描述在使用某种隔离级别的情况下,会出现什么样的结果。

 

可串行化:执行情况是要不Martin先统计,统计完成后David再更新,或者David更新完成后,Martin再统计。这样,系统保证Martin的结果不是1217,它们都是正确的,但是可串行化可能无法保证同样条件下多次运行后会得到相同的结果。第一次运行Martin统计的结果是12,第二次统计的结果就变成了17

读已提交:它允许不可重复读。不可重复读的大致意思是,你第一次读取的数据假如是A,由于外界的干扰,那你在使用相同条件第二次读取的数据将会覆盖掉第一次读取的数据,即先前读取的结果A将不可重现。以上面场景为例,假如Martin从加锁包里读取的文件数是7,接着,David提交了更新,这时,Martin再从加锁包里读取文件个数,则得出的结果变成了9,以前的7将不可重现。可重复读的概念和不可重复读概念相反,即如果允许可重复读,则即使David提交了更新,Martin从加锁包里读出的文件个数还是7

 

可重复读:可重复读的概念在上面介绍了,可重复读可能会出现幻读。至于幻读,还是可以用上面例子来描述:首先,Martin从加锁包里读出7个文件,这时,David提交了更新(另一个事务),Martin又从多步提交包里读出8个文件,于是统计得出了一个错误的结果,即7+8=15。出现幻读的原因是读的结果只对Martin事务一部分有效,而不是对整个事务有效,而且通常是由于插入数据造成的。

 

读未提交:最低的隔离界别。可能会出现幻读、脏读以及不可重复读等。在这种隔离级别下,允许一个事务读取其它事务还未提交的数据。例如:Martin可能会在David刚往加锁包中添加完文件还没向多步提交包中添加文件时,就统计了David往加锁包中添加的文件,即统计结果为2+7+5=14。而此时,David事务并没提交,假如David更新成功,成功提交了事务,统计结果应该为17,或者David更新失败,事务回滚,统计结果应该为12

 

 

DB2数据库四种隔离级别描述

10.3  隔离级别(Isolation Levels)

维护数据库的一致性和数据完整性,同时又允许多个应用程序同时访问同一数据,这样的特性称为并发性。DB2数据库用来尝试强制实施并发性的方法之一是通过使用隔离级别,它决定在第一个事务访问数据时,如何对其他事务锁定或隔离该事务所使用的数据。DB2使用下列隔离级别来强制实施并发性:

●       可重复读(Repeatable Read)

●       读稳定性(Read Stability)

●       游标稳定性(Cursor Stability)

●       未提交读(Uncommitted Read)

可重复读隔离级别可以防止所有现象,但是会大大降低并发性(可以同时访问同一资源的事务数量)。未提交读隔离级别提供了最大的并发性,但是“脏读”、“幻像读”和“不可重复读”都可能出现。默认的隔离级别是CS。

10.3.1  可重复读(RR—Repeatable Read)

可重复读隔离级别是最严格的隔离级别。在该隔离级别下,一个事务的影响完全与其他并发事务隔离,脏读、不可重复的读、幻像读现象都不会发生。当使用可重复读隔离级别时,在事务执行期间会锁定该事务以任何方式引用的所有行。因此,如果在同一个事务中发出同一个SELECT语句两次或更多次,那么产生的结果数据集总是相同的。因此,使用可重复读隔离级别的事务可以多次检索同一行集,并对它们执行任意操作,直到提交或回滚操作终止该事务。但是,在事务存在期间,不允许其他事务执行会影响这个事务正在访问的任何行的插入、更新或删除操作。为了确保这种行为不会发生,锁定该事务所引用的每一行—— 而不是仅锁定被实际检索或修改的那些行。因此,如果一个事务扫描了1000行,但只检索10行,那么它所扫描的1000行(而不仅是被检索的10行)都会被锁定。

 

那么在现实环境中可重复读隔离级别是如何工作的呢?假定如家酒店使用DB2数据库跟踪如家酒店的客房信息,包括房间预订和房价信息,还有一个基于Web的应用程序,它允许顾客按“先到先服务”的原则预订房间。如果旅馆预订应用程序是在可重复读隔离级别下运行的,当顾客扫描某个日期段内的可用房间列表时,您(旅馆经理)将无法更改那些房间在指定日期范围内的房价。同样,其他顾客也无法进行或取消将会更改该列表的预订(直到第一个顾客的事务终止为止)。图10-6说明了这种行为。

 

图10-6  可重复读隔离级别的示例

在使用可重复读隔离级别时,一个事务中所有被读取过的行上都会被加上S锁,直到该事务被提交或回滚,行上的锁才会被释放。这样可以保证在一个事务中即使多次读取同一行,得到的值也不会改变。另外,在同一事务中即使以同样的搜索标准重新打开已被处理过的游标,得到的结果集也不会改变。可重复读相对于读稳定性而言,加锁的范围更大。对于读稳定性,应用程序只对符合要求的所有行加锁;而对于可重复读,应用程序将对所有被扫描过的行都加锁。

 

可重复读(RR)会锁定应用程序在工作单元中引用的所有行。利用“可重复读”,在打开游标的相同工作单元内一个应用程序发出一个SELECT语句两次,每次都返回相同的结果。利用“可重复读”,不可能出现丢失更新、脏读和幻像读的情况。

 

在该工作单元完成之前,“可重复读”应用程序可以尽可能多次地检索和操作这些行。但是,在该工作单元完成之前其他应用程序均不能更新、删除或插入可能会影响结果表的行。“可重复读”应用程序不能查看其他应用程序的未落实更改。

10.3.2  读稳定性(RS—Read Stability)

读稳定性隔离级别没有可重复读隔离级别那么严格;因此,它没有将事务与其他并发事务的效果完全隔离。读稳定性隔离级别可以防止脏读和不可重复读,但是可能出现幻像读。在使用这个隔离级别时,只是锁定事务实际检索和修改的行。因此,如果一个事务扫描了1000行,但只检索10行,则只有被检索的10行(而不是所扫描的1000行)被锁定。因此,如果在同一个事务中发出同一个SELECT语句两次或更多次,那么每次产生的结果数据集可能不同。

 

与可重复读隔离级别一样,在读稳定性隔离级别下运行的事务可以检索一个行集,并可以对它们执行任意操作,直到事务终止。在这个事务存在期间,其他事务不能执行那些会影响这个事务检索到的行集的更新或删除操作;但是其他事务可以执行插入操作。如果插入的行与第一个事务的查询的选择条件匹配,那么这些行可能作为幻像出现在后续产生的结果数据集中。其他事务对其他行所作的更改,在提交之前是不可见的。

 

那么,读稳定性隔离级别会如何影响如家酒店客房预定应用程序的工作方式呢?当一个顾客检索某个日期段内的所有可用房间列表时,您可以更改这个顾客的列表之外的任何房间的房价。同样,其他顾客可以进行或取消房间预订。如果第一个顾客再次运行同样的查询,其他顾客的操作可能会影响他获得的可用房间列表。如果第一个顾客再次查询同一个日期段内的所有可用房间列表,产生的列表中有可能包含新的房价或第一次产生列表时不可用的房间。图10-7 说明了这种行为。

 

图10-7  读稳定性隔离级别的示例

在使用读稳定性隔离级别时,一个事务中所有被读取过的行上都会被加上NS锁,直到该事务被提交或回滚,行上的锁才会被释放。这样可以保证在一个事务中即使多次读取同一行,得到的值也不会改变。但是,如果使用这种隔离级别,在一个事务中,如果使用同样的搜索标准重新打开已被处理过的游标,则结果集可能改变(可能会增加某些行,这些行被称为幻影行 (Phantom)。这是因为RS隔离级不能阻止通过插入或更新操作在结果集中加入新行)。

 

读稳定性(RS)只锁定应用程序在工作单元中检索的那些行。它确保在某个工作单元完成之前,在该工作单元运行期间的任何限定行读取不被其他应用程序进程更改,且确保不会读取由另一个应用程序进程所更改的任何行,直至该进程落实了这些更改。也就是说,不可能出现“不可重复读”情形。

“读稳定性”隔离级别的其中一个目标是提供较高并行性以及数据的稳定视图。为了有助于达到此目标,优化器确保在发生锁定升级前不获取表级锁定。

 

“读稳定性”隔离级别最适用于包括下列所有特征的应用程序:

       在并发环境下运行

       须要限定某些行在工作单元运行期间保持稳定

       在工作单元中不会多次发出相同的查询,或者在同一工作单元中发出多次查询时并不要求该查询获得相同的回答

10.3.3  游标稳定性(CS—Cursor Stability)

游标稳定性隔离级别在隔离事务效果方面非常宽松。它可以防止脏读,但有可能出现不可重复读和幻像读。这是因为在大多数情况下,游标稳定性隔离级别只锁定事务声明并打开的游标当前所引用的行。

 

当使用游标稳定性隔离级别的事务通过游标从表中检索行时,其他事务不能更新或删除游标所引用的行。但是,如果被锁定的行本身不是用索引访问的,那么其他事务可以将新的行添加到表中,以及对被锁定行前后的行进行更新和/或删除操作。所获取的锁一直有效,直到游标重定位或事务终止为止(如果游标重定位,原来行上的锁就被释放,并获得游标现在所引用行上的锁)。此外,如果事务修改了它检索到的任何行,那么在事务终止之前,其他事务不能更新或删除该行,即使游标不再位于被修改的行。与可重复读和读稳定性隔离级别一样,其他事务在其他行上进行的更改,在这些更改提交之前对于使用游标稳定性隔离级别(这是默认的隔离级别)的事务是不可见的。

 

如果如家酒店客房预定程序在游标稳定性隔离级别下运行,那么会有什么影响呢?当一个顾客检索某个日期段内所有可用房间的列表,然后查看产生的列表上每个房间的信息时(每次查看一个房间),您可以更改旅馆中任何房间的房价,但是这个顾客当前正在查看的房间除外(对于指定的日期段)。同样,其他顾客可以对任何房间进行或取消预订,但是这个顾客当前正在查看的房间除外(对于指定的日期段)。也就是说,对于第一个顾客当前正在查看的房间记录,您和其他顾客都不能进行任何操作。当第一个顾客查看列表中另一个房间的信息时,您和其他顾客就可以修改他刚才查看的房间记录(如果这个顾客没有预订这个房间的话)。图10-8说明了这种行为。

 

图10-8  游标稳定性隔离级别的示例

在使用游标稳定性隔离级别时,一个事务的结果集中只有正在被读取的那一行(游标指向的行)会被加上NS锁,其他未被处理的行上不被加锁。这种隔离级别只能保证正在被处理的行的值不会被其他并发的程序所改变。该隔离级别是DB2默认的隔离级别。

 

当在行上定位游标时,游标稳定性(CS)会锁定任何由应用程序的事务所访问的行。此锁定在读取下一行或终止事务之前有效。但是,如果更改了某一行上的任何数据,那么在对数据库落实更改之前必须挂起该锁定。

 

对于具有“游标稳定性”的应用程序已检索的行,当该行上有任何可更新的游标时,任何其他应用程序都不能更新或删除该行。“游标稳定性”应用程序不能查看其他应用程序的未落实更改。

使用“游标稳定性”隔离级别,可能会出现不可重复读和幻像读现象。“游标稳定性”是默认隔离级别,建议在需要最大并行性,但只看到其他应用程序中已落实行的情况下才使用。

10.3.4  未提交读(UR—Uncommitted Read)

未提交读隔离级别是最不严格的隔离级别。实际上,在使用这个隔离级别时,仅当另一个事务试图删除或更改被检索的行所在的表时,才会锁定该检索的行。因为在使用这种隔离级别时,行通常保持未锁定状态,所以脏读、不可重复读和幻像读都可能会发生。因此,未提交读隔离级别通常用于那些访问只读表和视图的事务,以及某些执行SELECT语句的事务(只要其他事务的未提交数据对这些语句没有负面效果)。

 

顾名思义,其他事务对行所作的更改在被提交之前对于使用未提交读隔离级别的事务是可见的。但是,此类事务不能看见或访问其他事务所创建的表、视图或索引,直到那些事务被提交为止。类似地,如果其他事务删除了现有的表、视图或索引,那么仅当进行删除操作的事务终止时,使用未提交读隔离级别的事务才能知道这些对象不再存在了(一定要注意一点:当运行在未提交读隔离级别下的事务使用可更新游标时,该事务的行为和在游标稳定性隔离级别下运行一样,并应用游标稳定性隔离级别的约束)。

 

那么未提交读隔离级别对如家酒店客房预定应用程序有什么影响呢?现在,当一个顾客检索某个日期段内的所有可用房间列表时,您可以更改旅馆中任何房间在任何日期的房价,而其他顾客也可以对任何房间进行或取消预订,包括第一个顾客当前正在查看的房间记录(对于指定的日期段)。另外,第一个顾客生成的房间列表可能包含其他顾客正在预订(因此实际上不可用)的房间。图10-9说明了这种行为。

 

图10-9  未提交读隔离级别示例

未提交读(UR)允许应用程序访问其他事务的未提交的更改。除非其他应用程序尝试删除或改变该表,否则该应用程序不会锁定正读取的行而使其他应用程序不能访问该行。对于只读和可更新的游标,“未提交的读”的工作方式有所不同。

 

在使用未提交读隔离级别时,对于只读操作,不加行锁。典型的只读操作包括:               SELECT语句的结果集(比如语句中包括ORDER BY子句);定义游标时指明FOR FETCH ONLY。

 

未提交读隔离级别可以改善应用程序的性能,同时可以最大程度地允许并发。但是,应用程序的数据完整性将受到威胁。如果需要读取未提交的数据,该隔离级是唯一选择。

 

只读游标可以访问大多数其他事务未落实的更改。但是,当该事务正在处理时,不能使用正由其他事务创建或删除的表、视图和索引。其他事务的任何其他更改在落实或回滚前都可被读取。

 

注意:

“未提交读”隔离级别下的可更新操作的游标的工作方式游标稳定性隔离级别下的相同。

当使用隔离级别UR运行程序时,应用程序可以使用隔离级别CS。发生这种情况的原因是应用程序中使用的游标是模糊游标。利用BLOCKING选项,可以将模糊游标升级为隔离级别CS。BLOCKING 选项的默认值是UNAMBIG。这意味着模糊游标是可更新的,并且隔离级别可以升级为CS。要防止此升级,有两种选择:

     修改应用程序中的游标为非模糊游标。将SELECT语句更改为包括FOR READ ONLY子句。

 

    将模糊游标保留在应用程序中,但是使用预编译程序或BLOCKING ALL和STATICREADONLY YES选项绑定它,以允许在运行该程序时将任何模糊游标视为只读游标。

 

同对扫描1000行的“可重复读”给出的示例一样,如果使用“未提交读”,那么不需要任何行锁定。

使用“未提交读”,可能出现不可重复读和幻像读现象。“未提交读”隔离级别最常用于只读表上的查询。如果仅执行选择语句且不关心是否可从其他应用程序中看到未提交的数据时,那么该隔离级别也是不错的选择。

 

以上我们所讲的隔离级别的加锁范围和持续时间都是针对读操作而言的。对于更改操作,被修改的行上会被加上X锁,无论使用何种隔离级别,X锁都直到提交或回滚之后才会被释放

 

 

 

参考文献: 1.  《企业架构模式》第五章

           2.  http://ajava.org/readbook/db/xxjjdb2/17240.html

<!--EndFragment-->

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值