《DB2隔离级别和锁教学内容.doc》由会员分享,可在线阅读,更多相关《DB2隔离级别和锁教学内容.doc(122页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Good is good, but better carries it.精益求精,善益求善。DB2隔离级别和锁-深入解析DB2-高级管理、内部体系结构与诊断案例第6章我们在进行客户支持时遇到最多的话题之一就是锁。“为什么DB2锁住了这个表、行或者对象?”,“这个锁会阻塞多长时间及为什么?”;“为什么出现了死锁?”,“我的锁请求在等待什么?”,诸如此类问题等等。更仔细地分析一些常见的锁示例可以说明DB2锁定策略背后的原则。在国内很多DB2用户都会碰到有关锁等待、死锁和锁升级等锁相关的问题,本章将会对这些问题以及解决方法做详细的讲解。本章主要讲解如下内容: 隔离级别和锁 加锁总结 乐观锁 内部锁
2、设置锁相关的注册变量6.1隔离级别和锁要维护数据库的一致性和数据完整性,同时又允许多个应用程序同时访问一个数据库,将这样的特性称为并发性。DB2数据库尝试强制实施并发性的方法之一是使用隔离级别,它决定在第一个事务访问数据时,如何对其他事务锁定或隔离该事务所使用的数据。DB2使用下列隔离级别来强制实施并发性: 可重复读(ReapeatableRead,RR) 读稳定性(ReadStability,RS) 游标稳定性(CursorStability,CS) 未提交的读(UncommittedRead,UR)隔离级别是根据称为现象(Phenomena)的三个禁止操作序列来声明的: 脏读(DirtyR
3、ead):在事务A提交修改结果之前,其他事务即可看到事务的修改结果。 不可重复读(Non-RepeatableRead):在事务提交之前,允许其他事务修改和删除事务涉及的数据,导致事务中执行同样操作的结果集变小。 幻像读(PhantomRead):事务在提交查询结果之前,其他事务可以插入或者更改事务A涉及的数据,导致事务A中执行同样操作的结果集增大。数据库并发性(可以同时访问同一资源的事务数量)因隔离级别不同而有所差异,可重复读隔离级别可以防止所有现象,但是会大大降低并发性。未提交读隔离级别提供了最大的并发性,但可能会造成“脏读”、“幻像读”或“不可重复读”现象。DB2默认的隔离级别是CS。6
4、.1.1可重复读可重复读隔离级别是最严格的隔离级别。在使用它时,一个事务的操作结果完全与其他并发事务隔离,脏读、不可重复读、幻像读都不会发生。当使用可重复读隔离级别时,在事务执行期间会共享(S)锁定该事务以任何方式引用的所有行,在该事务中多次执行同一条SELECT语句,得到的结果数据集总是相同的。因此,使用可重复读隔离级别的事务可以多次检索同一行集,并可以对它们执行任意操作,直到提交或回滚操作终止事务。但是,在事务提交前,不允许其他事务执行会影响该事务正在访问的任何行的插入、更新或删除操作。为了确保这种行为,需要锁定该事务所引用的每一行而不是仅锁定被实际检索或修改的那些行。因此,如果一个表中有
5、1000行,但只检索两行,则整个表(1000行,而不仅是被检索的两行)都会被锁定。输出结果如下:C:db2+cselectempno,firstnme,salaryfromemployeewhereempnobetween000010and000020withrrEMPNOFIRSTNMESALARY-000010CHRISTINE152750.00000020MICHAEL94250.002条记录已选择。我们通过“getsnapshotforlocksonsample”命令来监控表加锁情况,输出结果如下:C:db2updatemonitorswitchesusinglockonDB20000
6、IUPDATEMONITORSWITCHES命令成功完成。C:db2getsnapshotforlocksonsample|more-略-锁定列表锁定名称=0x020006000E0040010000000052锁定属性=0x00000010发行版标志=0x00000004锁定计数=1挂起计数=0锁定对象名=20971534对象类型=表表空间名=USERSPACE1表模式=DB2ADMIN表名=EMPLOYEE方式=S-注:虽然读取了两行,但是整个表加S锁如果使用这种隔离级别,不管你从表中读多少数据,整个表上都加S锁,直到该事务被提交或回滚,表上的锁才会被释放。这样可以保证在一个事务中即使多次
7、读取同一行,都会得到相同结果集。另外,在同一事务中如果以同样的搜索标准重新打开已被处理过的游标,那么得到的结果集不会改变。可重复读相对于读稳定性而言,加锁的范围更大:对于读稳定性,应用程序只对符合要求的所有行加锁;而对于重复读,应用程序将对整个表都加S锁。可重复读会锁定应用程序在工作单元中引用的整个表。利用可重复读,一个应用程序在打开游标的相同工作单元内发出一个SELECT语句两次,每次都返回相同的结果。利用可重复读隔离级别,不可能出现丢失更新、脏读和幻像读的情况。在该工作单元完成之前,“可重复读”应用程序可以多次检索和操作这些行。但是,在该工作单元完成之前其他应用程序均不能更新、删除或插入可
8、能会影响结果表的行。“可重复读”应用程序不能查看其他应用程序未提交的更改。6.1.2读稳定性读稳定性隔离级别没有可重复读隔离级别那么严格;因此,它没有将事务与其他并发事务的效果完全隔离。读稳定性隔离级别可以防止脏读和不可重复读,但是可能出现幻像读。在使用这个隔离级别时,只锁定事务实际检索和修改的行。因此,如果一个表中有1000行,但只检索两行(通过索引扫描),则只锁定被检索的两行(而不是所扫描的1000行)。因此,如果在同一个事务中发出同一个SELECT语句两次或更多次,那么每次产生的结果数据集可能不同。与可重复读隔离级别一样,在读稳定性隔离级别下运行的事务可以检索一个行集(ROWSSET),
9、并可以对它们执行任意操作,直到事务终止。在这个事务存在期间,其他事务不能执行那些会影响这个事务检索到的行集的更新或删除操作,但是可以执行插入操作。如果插入的行与第一个事务的查询的选择条件匹配,那么这些行可能作为幻像出现在后续产生的结果数据集中。其他事务对其他行所作的更改,在提交之前是不可见的。下面我们还用上面的那个例子锁定读稳定性,输出结果如下:C:db2+cselectempno,firstnme,salaryfromemployeewhereempnobetween000010and000020withrsEMPNOFIRSTNMESALARY-000010CHRISTINE152750.
10、00000020MICHAEL94250.002条记录已选择。我们通过“getsnapshotforlocksonsample”命令来监控表加锁情况,输出结果如下:C:db2updatemonitorswitchesusinglockonDB20000IUPDATEMONITORSWITCHES命令成功完成。C:db2getsnapshotforlocksonsample|more-略-锁定列表锁定名称=0x02000600050040010000000052锁定属性=0x00000010发行版标志=0x00000001锁定计数=1挂起计数=0锁定对象名=20971525对象类型=行表名=EM
11、PLOYEE方式=S-注:只在读取的行上加S锁锁定名称=0x02000600040040010000000052锁定属性=0x00000010发行版标志=0x00000001锁定计数=1挂起计数=0锁定对象名=20971524对象类型=行表名=EMPLOYEE方式=S-注:只在读取的行上加S锁锁定名称=0x02000600000000000000000053锁定属性=0x00000010发行版标志=0x00000001锁定计数=1挂起计数=0锁定对象名=6对象类型=表表名=EMPLOYEE方式=IS-注:表上加IS锁如果使用这种隔离级,那么在一个事务中将有N+1个锁,其中N是所有被读取(通过索
12、引扫描)过的行的数目,这些行上都会被加上NS锁,在表上加上1个IS锁。这些锁直到该事务被提交或回滚才会被释放。这样可以保证在一个事务中即使多次读取同一行,得到的值也不会改变。但是使用这种隔离级别,在一个事务中,如果使用同样的搜索标准重新打开已被处理过的游标,则结果集可能改变(可能会增加某些行,这些行被称为幻影行(Phantom)。这是因为RS隔离级别不能阻止通过插入或更新操作在结果集中加入新行。注意:NS是下一键共享锁,此时锁拥有者和所有并发的事务都可以读(但不能更改)被锁定行中的数据。这种锁用来在使用读稳定性或游标稳定性事务隔离级别读取的数据上代替共享锁。读稳定性(RS)只锁定应用程序在工作
13、单元中检索的那些行。它确保在某个工作单元完成之前,在该工作单元运行期间的任何限定行读取不被其他应用程序进程更改,且确保不会读取由另一个应用程序进程所更改的任何行,直至该进程提交了这些更改。也就是说,不可能出现“不可重复读”情形。“读稳定性”隔离级别的其中一个目标是提供较高并行性以及数据的稳定视图,为了有助于达到此目标,优化器确保在发生锁定升级前不获取表级锁定。“读稳定性”隔离级别最适用于包括下列所有特征的应用程序: 在并发环境下运行。 需要限定某些行在工作单元运行期间保持稳定。 在工作单元中不会多次发出相同的查询,或者在同一工作单元中发出多次查询时并不要求该查询获得相同的回答。6.1.3游标稳
14、定性游标稳定性隔离级别在隔离事务效果方面非常宽松。它可以防止脏读;但有可能出现不可重复读和幻像读。这是因为在大多数情况下,游标稳定性隔离级别只锁定事务声明并打开的游标当前引用的行。当使用游标稳定性隔离级别的事务通过游标从表中检索行时,其他事务不能更新或删除游标所引用的行。但是,如果被锁定的行本身不是用索引访问的,那么其他事务可以将新的行添加到表中,以及对被游标锁定行前后的行进行更新或删除操作。所获取的锁一直有效,直到游标重定位或事务终止为止(如果游标重定位,原来行上的锁就被释放,并获得游标现在引用的行上的锁)。此外,如果事务修改了它检索到的任何行,那么在事务终止之前,其他事务不能更新或删除该行
15、,即使在游标不再位于被修改的行时。与可重复读和读稳定性隔离级别一样,其他事务在其他行上进行的更改,在这些更改提交之前对于使用游标稳定性隔离级别的事务(这是默认的隔离级别)是不可见的。我们还用上面那个例子,一个表中有1000行数据,我们只检索其中两行数据。那么对于可重复读隔离级别会锁住整个表,对于读稳定性隔离级别会对读到的数据(两行)加锁,而对于游标稳定性隔离级别只对游标当前所在那一行加锁,游标所在行的前一行和下一行都不加锁。下面我们举一个游标稳定性的例子,输出结果如下:C:db2+cdeclarec1cursorforselectempno,firstnme,salaryfromemploye
16、ewhereempnobetween000010and000020withcsC:db2+copenc1C:db2+cfetchc1EMPNOFIRSTNMESALARY-000010CHRISTINE152750.00-注:游标当前所在行,DB2只对这一行加锁。游标的上一行和下一行都不加锁。当游标移动到下一行时,锁自动释放。1条记录已选择。我们通过“getsnapshotforlocksonsample”命令来监控表加锁情况,输出结果如下:C:db2updatemonitorswitchesusinglockonDB20000IUPDATEMONITORSWITCHES命令成功完成。C:db
17、2getsnapshotforlocksonsample|more-略-锁定名称=0x02000600040040010000000052锁定属性=0x00000010发行版标志=0x00000001锁定计数=1挂起计数=0锁定对象名=20971524对象类型=行表名=EMPLOYEE方式=S-注:只在游标所在行上加S锁锁定名称=0x02000600000000000000000053锁定属性=0x00000010发行版标志=0x00000001锁定计数=1挂起计数=0锁定对象名=6对象类型=表表名=EMPLOYEE方式=IS-注:表上加IS锁如果使用这种隔离级,那么在一个事务中只有两个锁:结
18、果集中只有正在被读取的那一行(游标指向的行)被加上NS锁,在表上加IS锁。其他未被处理的行上不加锁。这种隔离级别只能保证正在处理的行的值不会被其他并发的程序所改变。该隔离级别是DB2默认的隔离级别。游标稳定性(CS)当在行上定位游标时会锁定任何由应用程序的事务所访问的行。此锁定在读取下一行或终止事务之前有效。但是,如果更改了某一行上的任何数据,那么在对数据库提交更改之前必须挂起该锁定。对于具有“游标稳定性”的应用程序已检索的行,当该行上有任何可更新的游标时,任何其他应用程序都不能更新或删除该行。“游标稳定性”应用程序不能查看其他应用程序的未提交操作。使用“游标稳定性”,可能会出现不可重复读和幻
19、像读现象。“游标稳定性”是默认隔离级别,应在需要最大并行性,但只看到其他应用程序中的已提交行的情况下才使用。6.1.4未提交读未提交读隔离级别是最不严格的隔离级别。实际上,在使用这个隔离级别时,仅当另一个事务试图删除或更改被检索的行所在的表时,才会锁定一个事务检索的行。因为在使用这种隔离级别时,行通常保持未锁定状态,所以脏读、不可重复读和幻像读都可能会发生。因此,未提交读隔离级别通常用于那些访问只读表和视图的事务,以及某些执行SELECT语句的事务(只要其他事务的未提交数据对这些语句没有负面效果)。顾名思义,其他事务对行所做的更改在提交之前对于使用未提交读隔离级别的事务是可见的。但是,此类事务
20、不能看见或访问其他事务DDL(CREATE、ALTER和DROP)语句所创建的表、视图或索引,直到那些事务被提交为止。类似地,如果其他事务删除了现有的表、视图或索引,那么仅当进行删除操作的事务终止时,使用未提交读隔离级别的事务才能知道这些对象不再存在了。一定要注意一点:当运行在未提交读隔离级别下的事务使用可更新游标时,该事务的行为和在游标稳定性隔离级别下运行一样,并应用游标稳定性隔离级别的约束。下面我们举一个例子。我们编写一个SQL存储过程,在存储过程中我们显式地在SELECT语句中使用UR隔离级别。创建一个存储过程,保存为LOCKS.SQL,输出结果如下:CREATEPROCEDUREloc
21、ks()LANGUAGESQLBEGINdeclarec1cursorforselect*fromstaffwithUR;openc1;while1=1do注:死循环endwhile;END为了方便抓住锁信息,我们在这个存储过程的结尾处使用了一个死循环。利用一个命令窗口运行存储过程,输出结果如下:C:db2td-vflocks.sqlC:db2calllocks()再打开一个新的窗口,得到在STAFF表上的当前锁信息,输出结果如下:C:db2pd-dbsample-locksshowdetailLocks:AddressTranHdlLocknameTypeModeStsOwnerDurHld
22、CntAttReleaseFlg0x408E0290200020003000000000000000054Table.ISG2100x00000x00000001TbspaceID2TableID3但是会发现此时在STAFF表上出现的是IS锁,而不是IN锁。是什么原因呢?这是因为UR隔离级别允许应用程序存取其他事务的未落实的更改,但是对于只读和可更新这两种不同的游标类型,UR的工作方式有所不同。对于可更新的游标,当它使用隔离级别UR运行程序时,应用程序会自动使用隔离级别CS。在上面的例子当中,虽然显式地指定了SQL语句的隔离级别是UR,但是,由于在存储过程中使用的游标是模糊游标(也就是没有显式
23、地声明游标是只读的还是可更新的),因而系统会默认地将这个模糊游标当成可更新游标处理,存储过程的隔离级别自动从UR升级为CS。要防止此升级,可以采用以下办法: 修改应用程序中的游标,以使这些游标是非模糊游标。将SELECT语句更改为包括FORREADONLY子句。 将模糊游标保留在应用程序中,但是预编译程序或使用BLOCKINGALL和STATICREADONLYYES选项绑定它以允许在运行该程序时将任何模糊游标视为只读游标。我们还是使用上面的例子,显式地将该游标声明成只读游标,输出结果如下:declarec1cursorforselect*fromstaffforreadonlywithUR;
24、此时我们再运行这个存储过程,并利用DB2PD获取锁的情况,输出结果如下:c:db2pd-dbsample-locksshowlocksLocks:AddressTranHdlLocknameTypeModeStsOwnerDurHldCntAttReleaseFlg0x408E07E0200020003000000000000000054Table.ING2100x00000x00000001TbspaceID2TableID3-注:可以看到STAFF表上出现的锁是IN锁。从上面的例子中我们可以看到:“未提交读(UR)”隔离级别允许应用程序访问其他事务的未提交的更改。除非其他应用程序尝试删除或
25、改变该表,否则该应用程序也不会锁定正读取的行而使其他应用程序不能访问该行。对于只读和可更新的游标,“未提交的读”的工作方式有所不同。如果使用这种隔离级别,那么对于只读操作不加行锁。典型的只读操作包括:SELECT语句的结果集只读(比如语句中包括ORDERBY子句);定义游标时指明起为FORFETCHONLY或FORREADONLY。该隔离级别可以改善应用程序的性能,同时可以达到最大程度的并发性。但是,应用程序的数据完整性将受到威胁。如果需要读取未提交的数据,该隔离级是唯一选择。使用“未提交的读”,可能出现不可重复读行为和幻像读现象。“未提交读”隔离级别最常用于只读表上的查询,或者在仅执行选择语
26、句且不关心是否可从其他应用程序中看到未提交的数据时也最常用。以上我们所讲的隔离级别的加锁范围和持续时间都是针对读操作而言的。对于更改操作,被修改的行上会被加上X锁,无论使用何种隔离级别,X锁都直到提交或回滚之后才会被释放。6.1.5隔离级别加锁示例讲解假设有一张表EMP1,表中有42条记录,我们使用FORREADONLY分别在UR、CS、RS和RR隔离级别下加锁。EMP1表在本章后续的内容中也会使用到,其创建过程如下:C:db2createtableemp1likeemployeeC:db2insertintoemp1select*fromemployee我们使用EMP1表中JOB字段内容为C
27、LERK的数据,输出结果如下:C:db2+cselectempno,job,salaryfromemp1wherejob=CLERKforreadonlyEMPNOJOBSALARY-000120CLERK49250.00000230CLERK42180.00000240CLERK48760.00000250CLERK49180.00000260CLERK47250.00000270CLERK37380.00200120CLERK39250.00200240CLERK37760.008条记录已选择。在上面的SQL语句中,我们从表的42条记录中返回8条记录。下面我们分别看看这条语句在不同的隔离级
28、别下加锁的情况:UR隔离级别,输出结果如下:C:db2+cselectempno,job,salaryfromemp1wherejob=CLERKforreadonlywithurEMPNOJOBSALARY-000120CLERK49250.00000230CLERK42180.00000240CLERK48760.00000250CLERK49180.00000260CLERK47250.00000270CLERK37380.00200120CLERK39250.00200240CLERK37760.008条记录已选择。在另外一个窗口中使用“db2getsnapshotforlockson
29、sample”命令监控,发现在UR隔离级别下,在表上有一个IN锁,没有加任何行锁。CS隔离级别,输出结果如下:C:db2+cdeclarec1cursorforselectempno,job,salaryfromemp1wherejob=CLERKforreadonlywithCSC:db2+copenc1C:db2+cfetchc1EMPNOJOBSALARY-000120CLERK49250.001条记录已选择。在另外一个窗口中使用“db2getsnapshotforlocksonsample”命令监控,发现在CS隔离级别下,共有两个锁:在表上有一个IS锁,在行上有一个NS锁。RS隔离级别
30、,输出结果如下:C:db2+cselectempno,job,salaryfromemp1wherejob=CLERKforreadonlywithRSEMPNOJOBSALARY-000120CLERK49250.00000230CLERK42180.00000240CLERK48760.00000250CLERK49180.00000260CLERK47250.00000270CLERK37380.00200120CLERK39250.00200240CLERK37760.008条记录已选择。在另外一个窗口中使用“db2getsnapshotforlocksonsample”命令监控,发现
31、在RS隔离级别下,共有9个锁:在表上有一个IS锁,在读取的8行上分别有1个NS锁。RR隔离级别,输出结果如下:C:db2+cselectempno,job,salaryfromemp1wherejob=CLERKforreadonlywithRREMPNOJOBSALARY-000120CLERK49250.00000230CLERK42180.00000240CLERK48760.00000250CLERK49180.00000260CLERK47250.00000270CLERK37380.00200120CLERK39250.00200240CLERK37760.008条记录已选择。在另
32、外一个窗口中使用“db2getsnapshotforlocksonsample”命令监控,发现在RR隔离级别下,分为两种情况:如果该SQL语句使用全表扫描,那么即使只读取了8行,也会在整个表上加一个S锁,输出结果如下:C:dynexpln-dsample-qselectempno,job,salaryfromemp1wherejob=CLERKforreadonlywithrrtAccessTableName=DB2ADMIN.EMP1ID=3,12|#Columns=2|RelationScan-注:全表扫描|Prefetch:Eligible|IsolationLevel:Repeatab
33、leRead-注:RR隔离级别|LockIntents|Table:Share-注:整个表上加S锁|Row:None|SargablePredicate(s)|#Predicates=1|ReturnDatatoApplication|#Columns=3ReturnDataCompletionEndofsection如果创建索引,并进行索引扫描,那么表上加IS锁,读取的每行上加S锁。所以对于RR隔离级别来说,为了保证并发,尽可能创建合理的索引以减少加锁的范围,输出结果如下:C:db2createindexjobonDB2ADMIN.emp1(job)DB20000ISQL命令成功完成。C:d
34、b2runstatsontableDB2ADMIN.emp1andindexesallDB20000IRUNSTATS命令成功完成。C:dynexpln-dsample-qselectempno,job,salaryfromemp1wherejob=CLERKforreadonlywithrr-tAccessTableName=DB2ADMIN.EMP1ID=3,12|IndexScan:Name=DB2ADMIN.JOBID=1-注:索引扫描|RegularIndex(NotClustered)|IndexColumns:|1:JOB(Ascending)|#Columns=2|#KeyCo
35、lumns=1|StartKey:InclusiveValue|1:CLERK|StopKey:InclusiveValue|1:CLERK|DataPrefetch:Eligible0|IndexPrefetch:None|IsolationLevel:RepeatableRead-注:RR隔离级别|LockIntents|Table:IntentShare-注:表上加IS锁|Row:Share-注:行上加S锁|SargablePredicate(s)|ReturnDatatoApplication|#Columns=3ReturnDataCompletionEndofsection6.1.
36、6隔离级别摘要表6-1按不期望的结果概述了几个不同的隔离级别。表6-1隔离级别摘要隔离级别访问未提交的数据不可重复读幻像读现象可重复读(RR)不可能不可能不可能读稳定性(RS)不可能不可能可能游标稳定性(CS)不可能可能可能未提交读(UR)可能可能可能表6-2提供了简单的试探方法,以帮助您为应用程序选择初始隔离级别。首先考虑表中列示的方法,并参阅先前对影响各隔离级别因素的讨论,可能会找到另一个更适合的隔离级别。表6-2选择隔离级别的准则应用程序类型需要高数据稳定性不需要高数据稳定性读写事务RSCS只读事务RR或RSUR为避免应用程序出现用户无法容忍的现象,必须为其选择适当的隔离级别。在不同隔离
37、级别下,应用程序锁定或释放资源需要不同的CPU和内存资源,所以隔离级别不但影响应用程序之间的隔离程度,还可能影响应用程序的个别性能特征。潜在的锁等待情况也会随隔离级别的不同而不同。因为隔离级别确定访问数据时如何锁定数据并使数据不受其他进程影响,所以您在选择隔离级别时应该平衡并行性和数据完整性需求。您指定的隔离级别在工作单元运行期间生效。选择正确的隔离级别使用的隔离级别不仅影响数据库的并发性,而且影响并发应用程序的性能。通常,使用的隔离级别越严格,并发性就越小,某些应用程序的性能可能会随之越低,因为它们要等待资源上的锁被释放。那么,如何决定要使用哪种隔离级别呢?最好的方法是先确定哪些现象是不可接
38、受的,然后选择能够防止这些现象发生的隔离级别。以下列举了各种隔离级别的适用情况: 如果正在执行大型查询,而且不希望并发事务所做的修改导致查询的多次运行返回不同的结果,则使用可重复读隔离级别。 如果希望在应用程序之间获得一定的并发性,还希望限定的行在事务执行期间保持稳定,则使用读稳定性隔离级别。 如果希望获得最大的并发性,同时不希望查询看到未提交的数据,则使用游标稳定性隔离级别。 如果正在只读的表/视图/数据库上执行查询,或者并不介意查询是否返回未提交的数据,则使用未提交读隔离级别。设置隔离级别尽管隔离级别控制事务级上的行为,但实际上它们是在应用程序级被指定的: 对于嵌入式SQL应用程序,在预编
39、译时或在将应用程序绑定到数据库(如果使用延迟绑定)时指定隔离级别。在这种情况下,使用PRECOMPILE或BIND命令的ISOLATION选项来设置隔离级别。 对于开放数据库连接(ODBC)和调用级接口(CallLevelInterface,CLI)应用程序,隔离级别是在应用程序运行时通过调用指定了SQL_ATTR_TXN_ISOLATION连接属性的SQLSetConnectAttr()函数进行设置的。另外,也可以通过指定DB2CLI.INI配置文件中的TXNISOLATION关键字的值来设置ODBC/CLI应用程序的隔离级别;但是,这种方法不够灵活,不能像第一种方法那样为一个应用程序中的不
40、同事务修改隔离级别。 对于Java数据库连接(JDBC)和SQLJ应用程序,隔离级别是在应用程序运行时通过调用DB2的JAVA.SQL连接接口中的“setTransactionIsolation()”方法设置的。当没有使用这些方法显式指定应用程序的隔离级别时,默认使用游标稳定性(CS)隔离级别。这个默认设置被应用于从命令行处理程序(CLP)执行的DB2命令、SQL语句和脚本,以及嵌入式SQL、ODBC/CLI、JDBC和SQLJ应用程序。因此,也可以为从CLP执行的操作(以及传递给DB2CLP进行处理的脚本)指定隔离级别。在这种情况下,隔离级别是通过在建立数据库连接之前在CLP中执行CHANG
41、EISOLATION命令设置的,输出结果如下:C:ppdb2changeisolationtourDB21027E当连接至数据库时未能更改隔离级别。C:ppdb2connectresetDB20000ISQL命令成功完成。C:ppdb2changeisolationtourDB21053W当连接至不支持UR的数据库时,会发生自动升级。DB20000ICHANGEISOLATION命令成功完成。在DB2V7.1及更高版本中,能够指定特定查询所用的隔离级别,方法是在SELECTSQL语句中加上WITHRR|RS|CS|UR子句。大家可以看到,本章前面的示例均使用这种方法举例。回页首6.2加锁总结6
42、.2.1如何获取锁在大多数情况下,DB2数据库管理程序在需要锁时隐式地获取它们,因此这些锁在DB2数据库管理程序的控制之下。除了使用未提交读隔离级别的情况外,事务从不需要显式地请求锁。实际上,唯一有可能被事务显式锁定的数据库对象是表(LOCKTABLE)。图6-1说明了用何种逻辑确定为所引用的对象获取什么类型的锁。图6-1如何获取锁从图6-1中我们可以看到,数据库首先判断该SQL语句是采用全表扫描还是索引扫描。如果是全表扫描,那么会在整个表上加表级别的锁;如果是读操作,那么获取表级S锁;如果是DML(INSERT、UPDATE和DELETE)操作,那么获取表级X锁。假设SQL语句采用的是索引扫
43、描,如果是读操作,在读取的行上加NS锁,同时在表上加IS锁;如果是DML操作,那么在操作的行上加X锁,同时在表上加IX锁。注意:假设一个表中有1000行数据,某个SQL语句访问该表中的两行数据。如果该表没有索引,那么这条SQL只能进行全表扫描,这种情况下即使你只访问两行数据,但是由于没有索引也必须进行全表扫描,这时整个表都被加锁。DB2数据库管理程序默认总是尝试获取行级锁。但是,可以通过执行特殊形式的ALTERTABLE语句来修改这种行为,输出结果如下:ALTERTABLETableNameLOCKSIZETABLE其中的TableName标识一个现有表的名称,所有事务在访问它时都要获取表级锁
44、。ALTERTABLE语句的LOCKSIZE子句指定行级别或表级别的锁定作用域(详细程度)。默认情况下,使用行锁定。这些已定义的表锁定仅请求S(共享)和X(互斥)锁定。ALTERTABLE语句的LOCKSIZEROW子句不会阻止正常的锁定升级。也可以在应用程序中通过执行LOCKTABLE语句,强制DB2数据库管理程序为特定事务在表上获取表级锁,输出结果如下:LOCKTABLETableNameINSHARE|EXCLUSIVEMODE其中的TableName标识一个现有表的名称,对于这个表应该获取表级锁(假定其他事务在该表上没有不兼容的锁)。如果在执行这个语句时指定了共享(SHARE)模式,就
45、会获得一个允许其他事务读取(但不能更改)表中数据的表级锁;如果执行时指定了互斥(EXCLUSIVE)模式,就会获得一个不允许其他事务读取或修改表中数据的表级锁。在下列情况下,由ALTERTABLE语句定义的永久表锁定可能比使用LOCKTABLE语句获得的单个事务表锁定更可取,原因如下: 表是只读的,且将始终只需要S锁定,其他用户也可以获取表的S锁定。 表通常由只读应用程序访问,但有时由单个用户访问可以进行简要维护,而该用户需要X锁定。当维护程序运行时,将只读应用程序锁定在外,但在其他情况下,只读应用程序可以使用最小的锁定开销同时访问表。总结一下:ALTERTABLE语句全局指定锁定,它影响访问该表的所有应用程序和用户。单个应用程序可以使用LOCKTABLE语句来指定应用程序级别的表锁定。6.2.2意图锁和非意图锁对于IN、IX、IS和SIX这些意图(INTENT)锁,读者可以这样理解:严格来说它们并不是一种锁,而是用来存放表中行锁的信息。举个通俗的例子,我们去住一个酒店。我们把整个酒店比喻成一张表,每个房间是一行。那么当我们预订一个房间时,就对该行(房间)加X锁,但是同时会在