[developerWorks] 深入解析DB2-高级管理、内部体系结构与诊断案例(6)
2009-12-16 10:05
441 查看
转贴自 http://www.db2china.net/?1167/viewspace-4021
我们在进行客户支持时遇到最多的话题之一就是锁。“为什么 DB2
本章主要讲解如下内容:
IBM DB2 数据库用户社区W[R%Ge/oq*l
隔离级别和锁
加锁总结
乐观锁
内部锁
设置锁相关的注册变量
8U&{a C
ZJp0
6.1 隔离级别和锁
IBM DB2 数据库用户社区5Y7m;j[4{5YT/
IBM DB2 数据库用户社区DX3f!v;y4pC
要
维护数据库的一致性和数据完整性,同时又允许多个应用程序同时访问一个数据库,将这样的特性称为并发性。 DB2
数据库尝试强制实施并发性的方法之一是使用隔离级别,它决定在第一个事务访问数据时,如何对其他事务锁定或隔离该事务所使用的数据。 DB2
使用下列隔离级别来强制实施并发性:
IBM DB2 数据库用户社区"a*Y{VRo!j'b.T
可重复读 (Reapeatable Read,RR)
读稳定性 (Read Stability,RS)
游标稳定性 (Cursor Stability,CS)
未提交的读 (Uncommitted Read,UR)
U+^8iAUp6m0
隔离级别是根据称为现象 (Phenomena) 的三个禁止操作序列来声明的:
$K6a9Ws#[:Sm0
脏读 (Dirty Read):在事务 A 提交修改结果之前,其他事务即可看到事务A的修改结果。
不可重复读 (Non-Repeatable Read):在事务A提交之前,允许其他事务修改和删除事务A涉及的数据,导致事务A中执行同样操作的结果集变小。
幻像读 (Phantom Read):事务A在提交查询结果之前,其他事务可以插入或者更改事务 A 涉及的数据,导致事务 A 中执行同样操作的结果集增大。
.P
@$G W:pk7lM0
数
据库并发性 ( 可以同时访问同一资源的事务数量 )
因隔离级别不同而有所差异,可重复读隔离级别可以防止所有现象,但是会大大降低并发性。未提交读隔离级别提供了最大的并发性,但可能会造成“脏读”、“幻
像读”或“不可重复读”现象。 DB2 默认的隔离级别是 CS 。
S,x}m
Q0
IBM DB2 数据库用户社区6jhb-Z4c{}kK
6.1.1 可重复读
IBM DB2 数据库用户社区5L.~f$hl^A"k&Aj}?
;kkd(AsFQ-tJ6h0
可
重复读隔离级别是最严格的隔离级别。在使用它时,一个事务的操作结果完全与其他并发事务隔离,脏读、不可重复读、幻像读都不会发生。当使用可重复读隔离级
别时,在事务执行期间会共享 (S) 锁定该事务以任何方式引用的所有行,在该事务中多次执行同一条 SELECT
语句,得到的结果数据集总是相同的。因此,使用可重复读隔离级别的事务可以多次检索同一行集,并可以对它们执行任意操作,直到提交或回滚操作终止事务。但
是,在事务提交前,不允许其他事务执行会影响该事务正在访问的任何行的插入、更新或删除操作。为了确保这种行为,需要锁定该事务所引用的每一行——
而不是仅锁定被实际检索或修改的那些行。因此,如果一个表中有 1000 行,但只检索两行,则整个表 (1000 行,而不仅是被检索的两行 )
都会被锁定。输出结果如下:
+q.l
q|Fu9W%[0
IBM DB2 数据库用户社区Y&Vyap5I;~;L#f
8fc{�pPQ&~0
我们通过“ get snapshot for locks on sample ”命令来监控表加锁情况,输出结果如下:
{}X&|P!Y0
IBM DB2 数据库用户社区0|,{A vk4FX
zMB?
j.eu0
如果
使用这种隔离级别,不管你从表中读多少数据,整个表上都加 S
锁,直到该事务被提交或回滚,表上的锁才会被释放。这样可以保证在一个事务中即使多次读取同一行,都会得到相同结果集。另外,在同一事务中如果以同样的搜
索标准重新打开已被处理过的游标,那么得到的结果集不会改变。可重复读相对于读稳定性而言,加锁的范围更大:对于读稳定性,应用程序只对符合要求的所有行
加锁;而对于重复读,应用程序将对整个表都加 S 锁。
'_uI/@�c[0
IBM DB2 数据库用户社区uOq0?g2`0@'Z
可重复读会锁定应用程序在工作单元中引用的整个表。利用可重复读,一个应用程序在打开游标的相同工作单元内发出一个 SELECT 语句两次,每次都返回相同的结果。利用可重复读隔离级别,不可能出现丢失更新、脏读和幻像读的情况。
-D}9tdpA0
IBM DB2 数据库用户社区�R�pSC,cXg]
在该工作单元完成之前,“可重复读”应用程序可以多次检索和操作这些行。但是,在该工作单元完成之前其他应用程序均不能更新、删除或插入可能会影响结果表的行。“可重复读”应用程序不能查看其他应用程序未提交的更改。
#Z5eJ
c9e?:f o0
t1u@ /Op0
6.1.2 读稳定性
m {.MfTet0
K4t%P�{i7ju%U0
读
稳定性隔离级别没有可重复读隔离级别那么严格;因此,它没有将事务与其他并发事务的效果完全隔离。读稳定性隔离级别可以防止脏读和不可重复读,但是可能出
现幻像读。在使用这个隔离级别时,只锁定事务实际检索和修改的行。因此,如果一个表中有 1000 行,但只检索两行 ( 通过索引扫描
),则只锁定被检索的两行 ( 而不是所扫描的 1000 行 ) 。因此,如果在同一个事务中发出同一个 SELECT
语句两次或更多次,那么每次产生的结果数据集可能不同。
~c2l!kB-o/9X%L_'y0
wf/D!y'V7qZX0
与
可重复读隔离级别一样,在读稳定性隔离级别下运行的事务可以检索一个行集 (ROWS
SET),并可以对它们执行任意操作,直到事务终止。在这个事务存在期间,其他事务不能执行那些会影响这个事务检索到的行集的更新或删除操作,但是可以执
行插入操作。如果插入的行与第一个事务的查询的选择条件匹配,那么这些行可能作为幻像出现在后续产生的结果数据集中。其他事务对其他行所作的更改,在提交
之前是不可见的。下面我们还用上面的那个例子锁定读稳定性,输出结果如下:
IBM DB2 数据库用户社区o_d5A9OiV"bw4i
M
IBM DB2 数据库用户社区
H C&b(tn5_%ECgB-|d
IBM DB2 数据库用户社区g3F
Zo:X
I$]
我们通过“ get snapshot for locks on sample ”命令来监控表加锁情况,输出结果如下:
`Hp.er
qW/v0
IBM DB2 数据库用户社区2k~;^{�G,vD8ia
kY,o9|%gg0
如果使用这
种隔离级,那么在一个事务中将有 N+1 个锁,其中 N 是所有被读取 ( 通过索引扫描 ) 过的行的数目,这些行上都会被加上 NS
锁,在表上加上 1 个 IS
锁。这些锁直到该事务被提交或回滚才会被释放。这样可以保证在一个事务中即使多次读取同一行,得到的值也不会改变。但是使用这种隔离级别,在一个事务中,
如果使用同样的搜索标准重新打开已被处理过的游标,则结果集可能改变 ( 可能会增加某些行,这些行被称为幻影行 (Phantom)) 。这是因为
RS 隔离级别不能阻止通过插入或更新操作在结果集中加入新行。
GW/^/7d y)ws2R;W0
IBM DB2 数据库用户社区dU7DW%A&M&U
注意:
-vbXB:bLKYf0
IBM DB2 数据库用户社区4t%IPjnlTCi
NS 是下一键共享锁,此时锁拥有者和所有并发的事务都可以读 ( 但不能更改 ) 被锁定行中的数据。这种锁用来在使用读稳定性或游标稳定性事务隔离级别读取的数据上代替共享锁。
BVg,ifZ!b.W0
m8u3C#~,Q0D-I0
读稳定
性 (RS)
只锁定应用程序在工作单元中检索的那些行。它确保在某个工作单元完成之前,在该工作单元运行期间的任何限定行读取不被其他应用程序进程更改,且确保不会读
取由另一个应用程序进程所更改的任何行,直至该进程提交了这些更改。也就是说,不可能出现“不可重复读”情形。
IBM DB2 数据库用户社区'GD�piVb-y
RPD
_
IBM DB2 数据库用户社区"Es0mD
fT
E(O'[tpb&E
“读稳定性”隔离级别的其中一个目标是提供较高并行性以及数据的稳定视图,为了有助于达到此目标,优化器确保在发生锁定升级前不获取表级锁定。
)}SH5M%U/E3/3N/Uf:})f$~0
IBM DB2 数据库用户社区~u:^^+K
Fug
“读稳定性”隔离级别最适用于包括下列所有特征的应用程序:
W5H AB]0
在并发环境下运行。
需要限定某些行在工作单元运行期间保持稳定。
在工作单元中不会多次发出相同的查询,或者在同一工作单元中发出多次查询时并不要求该查询获得相同的回答。
Tw2j6^%Cz:d"Lwx$Y*a0
6.1.3 游标稳定性
(i%d"eW{rs/U
{.l0
IBM DB2 数据库用户社区eL#p`5EB;[K9MQ
游标稳定性隔离级别在隔离事务效果方面非常宽松。它可以防止脏读;但有可能出现不可重复读和幻像读。这是因为在大多数情况下,游标稳定性隔离级别只锁定事务声明并打开的游标当前引用的行。
A[%h7?LMa0
IBM DB2 数据库用户社区jdd/gXVd
当
使用游标稳定性隔离级别的事务通过游标从表中检索行时,其他事务不能更新或删除游标所引用的行。但是,如果被锁定的行本身不是用索引访问的,那么其他事务
可以将新的行添加到表中,以及对被游标锁定行前后的行进行更新或删除操作。所获取的锁一直有效,直到游标重定位或事务终止为止 (
如果游标重定位,原来行上的锁就被释放,并获得游标现在引用的行上的锁 )
。此外,如果事务修改了它检索到的任何行,那么在事务终止之前,其他事务不能更新或删除该行,即使在游标不再位于被修改的行时。与可重复读和读稳定性隔离
级别一样,其他事务在其他行上进行的更改,在这些更改提交之前对于使用游标稳定性隔离级别的事务 ( 这是默认的隔离级别 )
是不可见的。我们还用上面那个例子,一个表中有 1000
行数据,我们只检索其中两行数据。那么对于可重复读隔离级别会锁住整个表,对于读稳定性隔离级别会对读到的数据 ( 两行 )
加锁,而对于游标稳定性隔离级别只对游标当前所在那一行加锁,游标所在行的前一行和下一行都不加锁。下面我们举一个游标稳定性的例子,输出结果如下:
8Jm X
H�p^OVl&T:aG0
IBM DB2 数据库用户社区}_"e2W3@
IBM DB2 数据库用户社区%i
g#m)T2M&lar/j!w?
我们通过“ get snapshot for locks on sample ”命令来监控表加锁情况,输出结果如下:
-b�|_*D_(U0
#[/@&_*[)sv2P%Oj Go0
IBM DB2 数据库用户社区0c^�m3Gy
如
果使用这种隔离级,那么在一个事务中只有两个锁:结果集中只有正在被读取的那一行 ( 游标指向的行 ) 被加上 NS 锁,在表上加 IS
锁。其他未被处理的行上不加锁。这种隔离级别只能保证正在处理的行的值不会被其他并发的程序所改变。该隔离级别是 DB2 默认的隔离级别。
IBM DB2 数据库用户社区#E,W*FJD5F-J#SZ
IBM DB2 数据库用户社区[Z HKd6A9D,OZ
游标稳定性 (CS) 当在行上定位游标时会锁定任何由应用程序的事务所访问的行。此锁定在读取下一行或终止事务之前有效。但是,如果更改了某一行上的任何数据,那么在对数据库提交更改之前必须挂起该锁定。
8sw5jw*tYj6[0
Q)Z,?^Rl0m{ @;R0
对于具有“游标稳定性”的应用程序已检索的行,当该行上有任何可更新的游标时,任何其他应用程序都不能更新或删除该行。“游标稳定性”应用程序不能查看其他应用程序的未提交操作。
q@N H@(N4r0
IsnO ~0
使用“游标稳定性”,可能会出现不可重复读和幻像读现象。“游标稳定性”是默认隔离级别,应在需要最大并行性,但只看到其他应用程序中的已提交行的情况下才使用。
IBM DB2 数据库用户社区wdQ!fVI6Qm*V@
IBM DB2 数据库用户社区5z4CWF*E9J
6.1.4 未提交读
,Fe:T1wsy.y0
IBM DB2 数据库用户社区4d.k!?
D1tXlr0TJ U
未
提交读隔离级别是最不严格的隔离级别。实际上,在使用这个隔离级别时,仅当另一个事务试图删除或更改被检索的行所在的表时,才会锁定一个事务检索的行。因
为在使用这种隔离级别时,行通常保持未锁定状态,所以脏读、不可重复读和幻像读都可能会发生。因此,未提交读隔离级别通常用于那些访问只读表和视图的事
务,以及某些执行 SELECT 语句的事务 ( 只要其他事务的未提交数据对这些语句没有负面效果 ) 。
IBM DB2 数据库用户社区k.}J6zB%J%m
IBM DB2 数据库用户社区] s"I4Y:[%G
顾
名思义,其他事务对行所做的更改在提交之前对于使用未提交读隔离级别的事务是可见的。但是,此类事务不能看见或访问其他事务
DDL(CREATE、ALTER 和 DROP)
语句所创建的表、视图或索引,直到那些事务被提交为止。类似地,如果其他事务删除了现有的表、视图或索引,那么仅当进行删除操作的事务终止时,使用未提交
读隔离级别的事务才能知道这些对象不再存在了。
6HkWY2u$zz0
IBM DB2 数据库用户社区mJyfGQY H;R
一定要注意一点:当运行在未提交读隔离级别下的事务使用可更新游标时,该事务的行为和在游标稳定性隔离级别下运行一样,并应用游标稳定性隔离级别的约束。下面我们举一个例子。
IBM DB2 数据库用户社区%z*c%K
bk*_
v6QEZz8Y*t3}/Y0
我们编写一个 SQL 存储过程,在存储过程中我们显式地在 SELECT 语句中使用 UR 隔离级别。
IBM DB2 数据库用户社区&MB
|}0V5_cr^
D
0`y
~~m0
创建一个存储过程,保存为 LOCKS.SQL,输出结果如下:
IBM DB2 数据库用户社区.t.b/b*qC5wFP
dyr3?$M2U;k0
g9[L,M.l%P0}0
为了方便抓住锁信息,我们在这个存储过程的结尾处使用了一个死循环。利用一个命令窗口运行存储过程,输出结果如下:
;c6W v(g
U"k0
Pcsq n&B+H0
,i wbb0O~)s0
再打开一个新的窗口,得到在 STAFF 表上的当前锁信息,输出结果如下:
IBM DB2 数据库用户社区2s _^!z3a.q
IBM DB2 数据库用户社区TIm/jf|zc
1a-e�C(Rh0
但是会发现此时
在 STAFF 表上出现的是 IS 锁,而不是 IN 锁。是什么原因呢?这是因为 UR
隔离级别允许应用程序存取其他事务的未落实的更改,但是对于只读和可更新这两种不同的游标类型,UR
的工作方式有所不同。对于可更新的游标,当它使用隔离级别 UR 运行程序时,应用程序会自动使用隔离级别 CS 。
IBM DB2 数据库用户社区]8ww8G*r(k�q{
IBM DB2 数据库用户社区BQ(e2v
NS9_9C2~0x
在
上面的例子当中,虽然显式地指定了 SQL 语句的隔离级别是 UR,但是,由于在存储过程中使用的游标是模糊游标 (
也就是没有显式地声明游标是只读的还是可更新的 ),因而系统会默认地将这个模糊游标当成可更新游标处理,存储过程的隔离级别自动从 UR 升级为
CS 。要防止此升级,可以采用以下办法:
&[ @+a
DY/!Vy%|~0
修改应用程序中的游标,以使这些游标是非模糊游标。将 SELECT 语句更改为包括 FOR READ ONLY 子句。
将模糊游标保留在应用程序中,但是预编译程序或使用 BLOCKING ALL 和 STATIC READONLY YES 选项绑定它以允许在运行该程序时将任何模糊游标视为只读游标。
Y5jr*N;W~4W_�t0
我们还是使用上面的例子,显式地将该游标声明成只读游标,输出结果如下:
8_
~r%P0|nt8N6R0
&F$|P5H*u:zd4fd x9K0
'G~5rO~0
此时我们再运行这个存储过程,并利用 DB2PD 获取锁的情况,输出结果如下:
Bc#_:w*^,F
t#me0
b(?)@ECd#@8e0
IBM DB2 数据库用户社区`_e/c^
注:可以看到
表上出现的锁是
锁。
IBM DB2 数据库用户社区A`'Rwq"b+XAi
IBM DB2 数据库用户社区?PX#bO
从
上面的例子中我们可以看到:“未提交读 (UR)
”隔离级别允许应用程序访问其他事务的未提交的更改。除非其他应用程序尝试删除或改变该表,否则该应用程序也不会锁定正读取的行而使其他应用程序不能访问
该行。对于只读和可更新的游标,“未提交的读”的工作方式有所不同。
IBM DB2 数据库用户社区4I/7C7X#G/A*B
IBM DB2 数据库用户社区Qmc$QD}^#_o
如果使用这种隔离级别,那么对于只读操作不加行锁。典型的只读操作包括: SELECT 语句的结果集只读 ( 比如语句中包括 ORDER BY 子句 ) ;定义游标时指明起为 FOR FETCH ONLY 或 FOR READ ONLY 。
m}j@}#pi7d0
aG*Eek/#MvR0
该隔离级别可以改善应用程序的性能,同时可以达到最大程度的并发性。但是,应用程序的数据完整性将受到威胁。如果需要读取未提交的数据,该隔离级是唯一选择。
-vd4{,n'T0
9]6^2x^y:b_!u.k0
使用“未提交的读”,可能出现不可重复读行为和幻像读现象。“未提交读”隔离级别最常用于只读表上的查询,或者在仅执行选择语句且不关心是否可从其他应用程序中看到未提交的数据时也最常用。
'E.d5C-l/h3a1q0
3G&x8mh'[H0
以上我们所讲的隔离级别的加锁范围和持续时间都是针对读操作而言的。对于更改操作,被修改的行上会被加上 X 锁,无论使用何种隔离级别,X 锁都直到提交或回滚之后才会被释放。
IBM DB2 数据库用户社区'b'G!m8iv:]m
IBM DB2 数据库用户社区'U/kTB0A[Pn
i
6.1.5 隔离级别加锁示例讲解
IBM DB2 数据库用户社区q'M2c9bLd
9O%U7BZ9VBY0Z0
假设有一张表 EMP1,表中有 42 条记录,我们使用 FOR READ ONLY 分别在 UR、CS、RS 和 RR 隔离级别下加锁。
Z5R-X%u9{2m6c0
,B^8I"D}h]0
EMP1 表在本章后续的内容中也会使用到,其创建过程如下:
/FF"qrTg @a S0
D[,tI
_M/`
Bu0
gR;/La)mP6P0
我们使用 EMP1 表中 JOB 字段内容为 'CLERK' 的数据,输出结果如下:
!J
q$NF8jU`0
ve4gB0/,Z y0
+U-r3{0}&RT'iXi0
在上面的 SQL 语句中,我们从表的 42 条记录中返回 8 条记录。下面我们分别看看这条语句在不同的隔离级别下加锁的情况:
IBM DB2 数据库用户社区+Q$OZf'{
+`:W]:jL+b&c$Ug0
UR 隔离级别,输出结果如下:
IBM DB2 数据库用户社区/p
},T["r,O
r3Y
IBM DB2 数据库用户社区 `}.XoK(X0[A(x4k
IBM DB2 数据库用户社区(d^�W/y
x8Ix|
在另外一个窗口中使用“ db2 get snapshot for locks on sample ”命令监控,发现在 UR 隔离级别下,在表上有一个 IN 锁,没有加任何行锁。
IBM DB2 数据库用户社区af};S8Xy/G7c
+V*rH*OBrX0
CS 隔离级别,输出结果如下:
] qx�C+T$b^;n0
LXS)~0/g
T/R'~0
f+T/G.K7B4^k0
在另外一个窗口中使用“ db2 get snapshot for locks on sample ”命令监控,发现在 CS 隔离级别下,共有两个锁:在表上有一个 IS 锁,在行上有一个 NS 锁。
IBM DB2 数据库用户社区wO
PDok
8g�|uzL!j,E8@+d4Y#z0
RS 隔离级别,输出结果如下:
Y$U%Mv0e1v!C7y0
IBM DB2 数据库用户社区e�y2}7W+D(n.Xg
_
t2eDx4L(Q
YX0
在另外一个窗口中使用“ db2 get snapshot for locks on sample ”命令监控,发现在 RS 隔离级别下,共有 9 个锁:在表上有一个 IS 锁,在读取的 8 行上分别有 1 个 NS 锁。
,VO^:AnG0
IBM DB2 数据库用户社区Z},W
P[:QaK
RR 隔离级别,输出结果如下:
IBM DB2 数据库用户社区Ed+k:}uC5J_c+]q
"D
{ ]5xy4h0
K;`(UX!i(D0
在另外一个窗口中使用“ db2 get snapshot for locks on sample ”命令监控,发现在 RR 隔离级别下,分为两种情况:
IBM DB2 数据库用户社区Z8^ M2d9I6P
IBM DB2 数据库用户社区o*f;P%J-~+C9I
如果该 SQL 语句使用全表扫描,那么即使只读取了 8 行,也会在整个表上加一个 S 锁,输出结果如下:
w+//9bV"R&K
/�c^;B0
IBM DB2 数据库用户社区%L
QfZP
@
IBM DB2 数据库用户社区r
q5x1B.z}
如果创建索引,并进行索引扫描,那么表上加 IS 锁,读取的每行上加 S 锁。所以对于 RR 隔离级别来说,为了保证并发,尽可能创建合理的索引以减少加锁的范围,输出结果如下:
IBM DB2 数据库用户社区*zX*AG(d1^-F8Y6e
IBM DB2 数据库用户社区N.R] G*yJoT
IBM DB2 数据库用户社区^}e(`6f!B
6.1.6 隔离级别摘要
p-/4tRJq&GHyF0m0
L4Z([+^sp5_lg0
表 6-1 按不期望的结果概述了几个不同的隔离级别。
/O.Z/X
q&e,A3eh0
/V|ck^Q@0
表 6-1 隔离级别摘要
IBM DB2 数据库用户社区vu)Jy9`/wVfEjd
0uxqE�LpQ0
IBM DB2 数据库用户社区'T-rKC })BOy
表 6-2 提供了简单的试探方法,以帮助您为应用程序选择初始隔离级别。首先考虑表中列示的方法,并参阅先前对影响各隔离级别因素的讨论,可能会找到另一个更适合的隔离级别。
IBM DB2 数据库用户社区z#j&LQssXKk0w)T
4ev.e
j:u/Z0
表 6-2 选择隔离级别的准则
H�oj6De0
IBM DB2 数据库用户社区:DW9w(rpF�Y}:W
IBM DB2 数据库用户社区4^&t+{i!v&L-K]
为
避免应用程序出现用户无法容忍的现象,必须为其选择适当的隔离级别。在不同隔离级别下,应用程序锁定或释放资源需要不同的 CPU
和内存资源,所以隔离级别不但影响应用程序之间的隔离程度,还可能影响应用程序的个别性能特征。潜在的锁等待情况也会随隔离级别的不同而不同。
C9q?4Cer0
IBM DB2 数据库用户社区lGsVQn5S
因为隔离级别确定访问数据时如何锁定数据并使数据不受其他进程影响,所以您在选择隔离级别时应该平衡并行性和数据完整性需求。您指定的隔离级别在工作单元运行期间生效。
Uf7~�rFD&e,@0
选择正确的隔离级别
8C*pzZ#M0
IBM DB2 数据库用户社区-W&KXlx�`.t
使
用的隔离级别不仅影响数据库的并发性,而且影响并发应用程序的性能。通常,使用的隔离级别越严格,并发性就越小,某些应用程序的性能可能会随之越低,因为
它们要等待资源上的锁被释放。那么,如何决定要使用哪种隔离级别呢?最好的方法是先确定哪些现象是不可接受的,然后选择能够防止这些现象发生的隔离级别。
以下列举了各种隔离级别的适用情况:
IBM DB2 数据库用户社区TC
px%o&Gf
如果正在执行大型查询,而且不希望并发事务所做的修改导致查询的多次运行返回不同的结果,则使用可重复读隔离级别。
如果希望在应用程序之间获得一定的并发性,还希望限定的行在事务执行期间保持稳定,则使用读稳定性隔离级别。
如果希望获得最大的并发性,同时不希望查询看到未提交的数据,则使用游标稳定性隔离级别。
如果正在只读的表 / 视图 / 数据库上执行查询,或者并不介意查询是否返回未提交的数据,则使用未提交读隔离级别。
设置隔离级别
Z
O7|tk&s"]G(aj3n0
&LG3E!H!V+r$_-{v^!ac0
尽管隔离级别控制事务级上的行为,但实际上它们是在应用程序级被指定的:
IBM DB2 数据库用户社区0z|�O2o�?/L
对于嵌入式 SQL 应用程序,在预编译时或在将应用程序绑定到数据库 ( 如果使用延迟绑定 ) 时指定隔离级别。在这种情况下,使用 PRECOMPILE 或 BIND 命令的 ISOLATION 选项来设置隔离级别。
对于
开放数据库连接 (ODBC) 和调用级接口 (Call Level Interface,CLI)
应用程序,隔离级别是在应用程序运行时通过调用指定了 SQL_ATTR_TXN_ISOLATION 连接属性的
SQLSetConnectAttr() 函数进行设置的。另外,也可以通过指定 DB2CLI.INI 配置文件中的 TXNISOLATION
关键字的值来设置 ODBC/CLI 应用程序的隔离级别;但是,这种方法不够灵活,不能像第一种方法那样为一个应用程序中的不同事务修改隔离级别。
对于 Java 数据库连接 (JDBC) 和 SQLJ 应用程序,隔离级别是在应用程序运行时通过调用 DB2 的 JAVA.SQL 连接接口中的“ setTransactionIsolation() ”方法设置的。
IBM DB2 数据库用户社区4e#Bc,ru DdXK
当
没有使用这些方法显式指定应用程序的隔离级别时,默认使用游标稳定性 (CS) 隔离级别。这个默认设置被应用于从命令行处理程序 (CLP) 执行的
DB2 命令、SQL 语句和脚本,以及嵌入式 SQL、ODBC/CLI、JDBC 和 SQLJ 应用程序。因此,也可以为从 CLP 执行的操作
( 以及传递给 DB2 CLP 进行处理的脚本 ) 指定隔离级别。在这种情况下,隔离级别是通过在建立数据库连接之前在 CLP 中执行
CHANGE ISOLATION 命令设置的,输出结果如下:
+NBn_GtI2K0
Sbv3Ex+}.E0
IBM DB2 数据库用户社区]Z&T%@m�Q4w)f"M
在 DB2 V7.1 及更高版本中,能够指定特定查询所用的隔离级别,方法是在 SELECT SQL 语句中加上 WITH [RR | RS | CS | UR] 子句。大家可以看到,本章前面的示例均使用这种方法举例。
IBM DB2 数据库用户社区4e
Y
MiF!P0YX
IBM DB2 数据库用户社区5zE'q;}:BG?
IBM DB2 数据库用户社区(OzI'F`
IBM DB2 数据库用户社区!e
^&E
R8b%kQ
m�~0ry$Fve"u0
6.2 加锁总结
IBM DB2 数据库用户社区
| D(m%WlK4F!S8O
eVYP
qz0
6.2.1 如何获取锁
(|]*?Z0~/sl%V0
IBM DB2 数据库用户社区Q frs{4Om&o/p
在
大多数情况下,DB2 数据库管理程序在需要锁时隐式地获取它们,因此这些锁在 DB2
数据库管理程序的控制之下。除了使用未提交读隔离级别的情况外,事务从不需要显式地请求锁。实际上,唯一有可能被事务显式锁定的数据库对象是表
(LOCK TABLE) 。图 6-1 说明了用何种逻辑确定为所引用的对象获取什么类型的锁。
IBM DB2 数据库用户社区H!E(Bn5~.d0S-?s]W
IBM DB2 数据库用户社区*l+Pn6]+S,o,kD
图 6-1 如何获取锁
IBM DB2 数据库用户社区E�o ]Pp^#LP

,Q
^C ] TD0
IBM DB2 数据库用户社区q6[q![R
从
图 6-1 中我们可以看到,数据库首先判断该 SQL
语句是采用全表扫描还是索引扫描。如果是全表扫描,那么会在整个表上加表级别的锁;如果是读操作,那么获取表级 S 锁;如果是
DML(INSERT、UPDATE 和 DELETE) 操作,那么获取表级 X 锁。假设 SQL
语句采用的是索引扫描,如果是读操作,在读取的行上加 NS 锁,同时在表上加 IS 锁;如果是 DML 操作,那么在操作的行上加 X
锁,同时在表上加 IX 锁。
`R*uLH+w0C4j/l0
-NccJ rnZ3AKM;}*o0
注意:
IBM DB2 数据库用户社区8I3I;XF!Sxe
L~Tt6lud0
假设一个表中有 1000 行数据,某个 SQL 语句访问该表中的两行数据。如果该表没有索引,那么这条 SQL 只能进行全表扫描,这种情况下即使你只访问两行数据,但是由于没有索引也必须进行全表扫描,这时整个表都被加锁。
%m`b:{0zFdA"n0
IBM DB2 数据库用户社区s3?^5__1r/f
DB2 数据库管理程序默认总是尝试获取行级锁。但是,可以通过执行特殊形式的 ALTER TABLE 语句来修改这种行为,输出结果如下:
IBM DB2 数据库用户社区N!bfD'i6V,RCZ
IBM DB2 数据库用户社区,CCUC;[/~#Vb,n1V
Ctu6v Gz7B0
其中的
TableName 标识一个现有表的名称,所有事务在访问它时都要获取表级锁。 ALTER TABLE 语句的 LOCKSIZE
子句指定行级别或表级别的锁定作用域 ( 详细程度 ) 。默认情况下,使用行锁定。这些已定义的表锁定仅请求 S( 共享 ) 和 X( 互斥 )
锁定。 ALTER TABLE 语句的 LOCKSIZE ROW 子句不会阻止正常的锁定升级。
+xr(i1KKlM*H0
zf Br%O8Z0
也可以在应用程序中通过执行 LOCK TABLE 语句,强制 DB2 数据库管理程序为特定事务在表上获取表级锁,输出结果如下:
[4|$N&K-f$p0
!sQW]$KK+T0
IBM DB2 数据库用户社区k&f MR)y3n|
其
中的 TableName 标识一个现有表的名称,对于这个表应该获取表级锁 ( 假定其他事务在该表上没有不兼容的锁 )
。如果在执行这个语句时指定了共享 (SHARE) 模式,就会获得一个允许其他事务读取 ( 但不能更改 )
表中数据的表级锁;如果执行时指定了互斥 (EXCLUSIVE) 模式,就会获得一个不允许其他事务读取或修改表中数据的表级锁。
,L^1FZ6Z`0
6q8@2Skt)[ B)_0x4y0
在下列情况下,由 ALTER TABLE 语句定义的永久表锁定可能比使用 LOCK TABLE 语句获得的单个事务表锁定更可取,原因如下:
IBM DB2 数据库用户社区N;IZ~W$~
表是只读的,且将始终只需要 S 锁定,其他用户也可以获取表的 S 锁定。
表通常由只读应用程序访问,但有时由单个用户访问可以进行简要维护,而该用户需要 X 锁定。当维护程序运行时,将只读应用程序锁定在外,但在其他情况下,只读应用程序可以使用最小的锁定开销同时访问表。
IBM DB2 数据库用户社区@8]P}~}
总结一下:ALTER TABLE
语句全局指定锁定,它影响访问该表的所有应用程序和用户。单个应用程序可以使用LOCK TABLE
语句来指定应用程序级别的表锁定。
IBM DB2 数据库用户社区B/] q(h;m[3t7os$qZ
5z)ey k2l%M*n0
6.2.2 意图锁和非意图锁
x9Yd8X-J!iL0
IBM DB2 数据库用户社区8ItQE/z+i
对
于 IN、IX、IS 和 SIX 这些意图 (INTENT)
锁,读者可以这样理解:严格来说它们并不是一种锁,而是用来存放表中行锁的信息。举个通俗的例子,我们去住一个酒店。我们把整个酒店比喻成一张表,每个房
间是一行。那么当我们预订一个房间时,就对该行 ( 房间 ) 加 X 锁,但是同时会在酒店的前台对该行 ( 房间 ) 做一个信息登记 (
旅客姓名、身份证、住多长时间等 )
。大家可以把意图锁当成是这个酒店前台的登记信息,它并不是真正意义上的锁,而是维护表中每行的加锁情况,所有访问这个表的应用程序共用这个意图锁。后续
的旅客来时通过酒店前台来看哪个房间是可住的。那么如果没有意图锁,会出现什么情况呢?假设我要预订房间,那么每次我都需要到每一个房间查看确认这个房间
有没有住旅客,这样的效率显然是很低下的。其实最早的 DB2 版本是没有意图锁的,但是这对并发影响非常大,后来就增加了意图锁。所有的数据库
(Oracle、Informix 和 Sybase)
都有意图锁的实现机制。在一个表上只有一个意图锁,所有应用程序共用这个意图锁,但是可能经常会更改。
IBM DB2 数据库用户社区)}7bN#WFjW5NQLb
)K9X.C_T`0
6.2.3 读锁和写锁
#rPW&{,Ai0
IBM DB2 数据库用户社区-U9CHG#eS7B
在 DB2 数据库中有两种主要类型的锁:读锁 (S) 和写 (X) 锁。
IBM DB2 数据库用户社区+A7DYUU:`S5eT
o:k^*i,H*bp@_u0
一般来说读锁是在如下情况下加的:
2a7Oc:U`Ljb8b b0
IBM DB2 数据库用户社区+y(I'l;F*r@cx
v
NS
是在 RS 和 CS 隔离级别下对读取到的行加的锁。而 S 锁是在 RR 隔离级别下对读取到的表 ( 使用全表扫描 ) 或行 ( 使用索引扫描
) 加的锁。 U 锁是在“ select * from t1 for update ”情况下加的锁。这些锁都是在读取 (SELECT)
期间加的锁。
IBM DB2 数据库用户社区"_z
SJ&g:K$}M
.MZ7z1Va0
一般来说写锁是在如下情况下加的:
+qvC$ru4U$up0
/r5DZUiRYW1f5^:q0
Z
锁是超级排它锁,它不允许任何隔离级别的读取,一般是在数据物理结构发生改变的情况下加的锁。例如:CREATE、ALTER、DROP、离线
REORG 和离线 LOAD 期间会加 Z 锁。 X 锁是在做 INSERT、UPDATE 和 DELETE 期间加的锁,它允许使用 UR
隔离级别进行未提交读取。 NW 锁表示当一行被插入到索引中的时候,该行的下一行会被加上该锁。锁的拥有者可以读但不能更改锁定行。该锁与 X
锁类似,只是与 NS 锁兼容。
IBM DB2 数据库用户社区'](H.Q"Zll6F5hb
3f{9a+Osp4_2Q.w1p@0
6.2.4 LRB(Lock Resource Block)
o/ZM m&A/{5sS0
i7?%~5NtPK0
每个数
据库都有一个锁列表,该列表包含所有同时连接到数据库的应用程序所持有的锁。在 32 位平台上,一个对象上的第一个锁要求占 72
字节,而其他锁要求占 36 字节。在 64 位平台上,第一个锁要求占 128 字节 (HP 平台为 80 字节 ),而其他锁要求占 64
字节。 关于锁占用资源块 (LRB:Lock Resource Block),在各个版本还不一样,表 6-3 是 DB2 V9 中 LRB
占用资源的情况。
IBM DB2 数据库用户社区6sYgR/H
IBM DB2 数据库用户社区|K4|Y"w/X2od'q6Ui�W
表 6-3 DB2 V9 中 LRB 占用资源的情况
2/�q8bB|qQV0
Wn.^Fz K2o0
{2{p~+^*h0
注意:
0Q0FRm
gTz/Z0
q;iY IF&t0
关
于 LRB,在 DB2 的各个版本很不一样。在 DB2 V8 之前,在 32 位平台上,在一个没有持有其他锁定的对象上持有一个锁定需要 72
字节,在一个持有了现存锁定的对象上记录一个锁定需要 36 字节;在 DB2 V8 的后期版本中,在一个没有持有其他锁定的对象上持有一个锁定需要
64 字节,在一个持有了现存锁定的对象上记录一个锁定需要 32 字节;在 64 位平台上,要对没有其他锁定的对象上保留锁定需要 112
字节,要对具有现有锁定的对象上保留锁定需要 56 字节。 DB2 V9 中的 LRB 情况如表 6-3 所示。
!o.Js"`+@0
IBM DB2 数据库用户社区8hw5]G7_|3jSS
}
6.2.5 USE AND KEEP LOCKS
(TB"O(D5cL
|0
6g2~'P)y:J:R0
在 DB2 中,默认情况下锁都是由 DB2 数据库管理器根据应用程序的隔离级别自动设置锁类型。 DB2 提供了一种方式允许用户明确地向 DB2 数据库管理器请求锁类型:
@-CPV4T-d0
USE AND KEEP EXCLUSIVE LOCKS:向 DB2 数据库管理器明确请求在数据上加排它锁。
USE AND KEEP UPDATE LOCKS:向 DB2 数据库管理器明确请求在数据上加更新锁。
USE AND KEEP SHARE LOCKS:向 DB2 数据库管理器明确请求在数据上加共享锁。
3FT5Ej%h
NP1M0
例如:
4u0m
A3Pc$C0
Xy?DOqY
j0
IBM DB2 数据库用户社区.g8`WL�o
n3N
在上面的语句中,如果没有带 USE AND KEEP EXCLUSIVE LOCKS 子句,默认情况下 DB2 会向行加更新锁 (U 锁 ),使用了该子句后将会变为排它锁 (X 锁 ) 。
ou{O-X
]?.j:{0
IBM DB2 数据库用户社区9V3?:p%AJ3c5v;XX9D#}
再如:
fH
k5i#uPyb`0
IBM DB2 数据库用户社区m7x#@Uj-b7Y
IBM DB2 数据库用户社区:E"] Y1m,nt�`
在上面的语句中,如果没有带 USE AND KEEP EXCLUSIVE LOCKS 子句,默认情况下 DB2 会向行加下一键共享锁 (NS 锁 ),使用了该子句后将会变为更新锁 (U 锁 ) 。
IBM DB2 数据库用户社区L!~fa$|R2Rl
"teq-w UZ&y+Gh0
再如:
IBM DB2 数据库用户社区nv:{.G1Q:m&x
IBM DB2 数据库用户社区 Z9uLQ!kHO`
IBM DB2 数据库用户社区gT4K:H;?K1N+i
在上面的语句中,如果没有带 USE AND KEEP EXCLUSIVE LOCKS 子句,默认情况下 DB2 会向行加更新锁 (U 锁 ),使用了该子句后将会变为下一键共享锁 (NS 锁 ) 。
kh
QS"g)m9Yd0
$voG6DvX#]0
为什么
要这样显式请求锁类型呢?这是因为 USE AND KEEP LOCKS
显式请求锁类型有助于避免多个存取数据库的独立进程的应用程序可能产生的死锁。例如,在一个应用程序中的数个进程存取同一个表,对该表并行进行读取及写入
操作。如果这些进程执行读 SQL 查询,然后再对同一表执行 SQL
更新,那么各个进程间对同一数据潜在的争用会使得死锁的几率增大。例如,如果两个进程读该表,然后更新该表,那么 A 进程先获得 S 锁,同时 B
进程也获得 S 锁。当 A 进程发出更新语句时试图获得对行的 X 锁定,而 B 进程对该行具有 S 锁定。此时 A 进程进入锁等待 (Lock
Wait) 状态,等待 B 进程释放 S 锁。当后来进程发出更新语句时试图获得对行的 X 锁定,而进程 A 对该行具有 S 锁定。此时 B
进程进入锁等待 (LOCK WAIT) 状态,等待 A 进程释放 S
锁。这样就产生了死锁,为了避免发生这种死锁,存取具有修改意向的数据的应用程序应该执行下列其中一项操作:
IBM DB2 数据库用户社区+b0F.j;e~4Hc
执行选择操作时使用 FOR UPDATE OF 子句。此子句确保当 A 进程试图读取该数据时进行 U 锁定,禁用行分块 (BLOCKING) 。
执行查询时使用 WITH RR USE AND KEEP UPDATE LOCKS 子句或
WITH RS USE AND KEEP UPDATE LOCKS 子句。任一子句都确保当 A 进程试图读取该数据时进行 U
锁定,并且允许行分块 (BLOCKING) 。
IBM DB2 数据库用户社区{*`mgJ^^O
6.2.6 索引类型和下一键锁
'fY g1cu
jlv0
IBM DB2 数据库用户社区h-a;[fg
DB2 中有两种索引类型:type-1 索引和 type-2 索引。这两种索引加锁的情况是不一样的,下面我们来分别介绍这两种索引的加锁算法。
kS+S&Y+/
M*zo6d0
lT5F6o oc'HJ4~0
type-1 索引加锁算法
7Ps
Pm2vrJP{f'S0
4t5^ _mY,J3F1m0
在 DB2 V8 之前,DB2 只有一种索引类型,也就是我们今天称之为的 TYPE-1 索引,这种索引在删除和插入的时候特别容易引起死锁从而影响并发。是什么原因呢,下面我们举一个使用 TYPE-1 索引的例子:
IBM DB2 数据库用户社区Q$[+j-Zi4j {
2~%k2c4E*z4N I0
假设一个索引的叶子 (LEAF) 中包含 1、5、6、7、8、12 6 个 KEY 。
1KCmX+RWb1dx-Q%pb0
假如现在交易 1 删除 KEY VALUE 8 对应的行,在删除期间,KEY VALUE 8
对应的行上会加 X 锁。当 KEY VALUE 8 被删除以后,就会在索引的下一键也就是 8 的下一个键 12 上加 NX 锁,相应地会在
KEY VALUE 12 对应的行上加 X 锁。
如果另外一个交易 2 删除 KEY VALUE 5 对应的行,在删除期间,KEY VALUE
5 对应的行上会加 X 锁。当 KEY VALUE 5 被删除以后,就会在索引的下一键也就是 5 的下一个键 6 上加 NX 锁,相应地会在
KEY VALUE 6 对应的行上加 X 锁。
假设现在交易 1 插入一行 KEY VALUE 4 相应的行,这一行会加 W 锁,当插入新的 KEY 到索引的时候,KEY VALUE 6 对应的行会加 NW 锁。因为此时交易 2 对应的行上持有 X 锁,这时它不得不等待交易 2 释放掉该锁。
同样假设现在交易 2 插入一行 KEY VALUE 9 相应的行,这一行会加 W 锁,当插入新的 KEY 到索引的时候,KEY VALUE 12 对应的行会加 NW 锁。因为此时交易 1 对应的行上持有 X 锁,这时它不得不等待交易 1 释放掉该锁。
M!d5zbk(zPn0
所以在 type-1 索引时,因为更改期间加锁的方式是 W 和 NW,所以很容易造成死锁,这会极大影响数据库的并发。
vPd7N!e:_%X(i0
IBM DB2 数据库用户社区`JC-c(v#A1NV
type-2 索引加锁算法
IBM DB2 数据库用户社区%]G0ra�G$|DQ
y
sb/W8j9^fd0
DB2
从 V8 以后所有新创建的索引都是 type-2 类型的索引。 type-2 索引可以大大減少 NEXT KEY
锁从而改进了性能,因为各项是标记为删除的而不是从页面中物理删除的 ( 伪删除 ) 。 type-2 索引同时允许索引列大于默认的 255
字节,同时还可以在线运行 REORG 和 RUNSTATS,并且支持新的多维群集 (MDC) 功能。在 DB2 V8 中,所有新的索引都是以
type-2 类型创建的,只有已经在表上定义了 ( 迁移前 ) type-1 索引的時候除外。可以使用 REORG INDEXES 将
type-1 索引转换为 type-2 索引。 Type-2 索引采用的是伪删除算法,图 6-2 是两种索引类型加锁的比较。
IBM DB2 数据库用户社区$U%_0F*_@n2H;s
IBM DB2 数据库用户社区Fyt ^W
M [$l QP)K
图 6-2 type-1 和 type-2 索引加锁比较
GW9Al;F!YGG8ri0

IBM DB2 数据库用户社区pN3E
p�p8A1C
6Q;a"`E0Ee0
在 DB2 V8 之前的版本中,插入过程中可能使用 W 或 NW 锁,但是在 DB2 V8 以后只有在使用了隔离级别为 RR 的情况下才会出现这两种锁。因此,应尽可能避免这种情况。
IBM DB2 数据库用户社区!y`/X.^H
./5pG"z5Z2CZ0
6.2.7 扫描方式加锁情况
(rW)P{4/b,TCm0
'gb0A%}(rywX!m?0
在
DB2 数据库中,不同的扫描方式在不同的隔离级别下加锁的情况也是不一样的,在 DB2 中主要有以下几种扫描方式:全表扫描、索引扫描和 RID
扫描 ( 注:如果使用了 MDC,那么还会有其他扫描方式,但此处我们不讨论 ) 。其中 RID 扫描也算是索引扫描。表 6-5 和表 6-6
总结了在全表和索引扫描方式下加锁的情况。
c$lZ0p(p0
GU/t%[FW0
表 6-4 表扫描时在表 / 行上加锁情况
IBM DB2 数据库用户社区,J5dNZ3Dh?T

//A"U//
Gk)iH7B?0
IBM DB2 数据库用户社区�I3O+A!h0zC;i,H.U2o.b"C
表 6-5 索引扫描时在表 / 行上的加锁情况
D"uZ
qQx0


%[X/x�P#N;k6/(W&]0
IBM DB2 数据库用户社区vR�I[3Z(`
关于在不同隔离级别和扫描方式下表 / 行上的加锁情况,我们可以在 DB2 解释工具的输出中查看加锁情况。下面我们分别对全表扫描和索引扫描的加锁情况举例:
%j5HDh8]aP/A0
IBM DB2 数据库用户社区r(l�Y7c A:jc/_!Y�n
全表扫描加锁情况如下:
IBM DB2 数据库用户社区�r"z-`9T*u/�l1^/W
IBM DB2 数据库用户社区6j'E.y0H.Hf
@1n
cdZ
IBM DB2 数据库用户社区�{F-N pV
l,U
索引扫描加锁情况如下:
5I1oc
/.V]�uP l4{0
IBM DB2 数据库用户社区qT.zZo1H
IBM DB2 数据库用户社区}�PB3Cn!IA
通过上面两个例子,我们希望读者能够明白关于扫描方式和隔离级别的加锁情况,关键是如何分析加锁情况。
我们在进行客户支持时遇到最多的话题之一就是锁。“为什么 DB2
锁住了这个表、行或者对象?”,“这个锁会阻塞多长时间及为什么?”;“为什么出现了死锁?”,“我的锁请求在等待什么?”,诸如此类问题等等。更仔细地
分析一些常见的锁示例可以说明 DB2 锁定策略背后的原则。在国内很多 DB2
用户都会碰到有关锁等待、死锁和锁升级等锁相关的问题,本章将会对这些问题以及解决方法做详细的讲解。
aMj+xXh{0
IBM DB2 数据库用户社区KW:~�t-lV�ZV
本章主要讲解如下内容:IBM DB2 数据库用户社区W[R%Ge/oq*l
隔离级别和锁
加锁总结
乐观锁
内部锁
设置锁相关的注册变量
8U&{a C
ZJp0
6.1 隔离级别和锁
IBM DB2 数据库用户社区5Y7m;j[4{5YT/
IBM DB2 数据库用户社区DX3f!v;y4pC
要
维护数据库的一致性和数据完整性,同时又允许多个应用程序同时访问一个数据库,将这样的特性称为并发性。 DB2
数据库尝试强制实施并发性的方法之一是使用隔离级别,它决定在第一个事务访问数据时,如何对其他事务锁定或隔离该事务所使用的数据。 DB2
使用下列隔离级别来强制实施并发性:
IBM DB2 数据库用户社区"a*Y{VRo!j'b.T
可重复读 (Reapeatable Read,RR)
读稳定性 (Read Stability,RS)
游标稳定性 (Cursor Stability,CS)
未提交的读 (Uncommitted Read,UR)
U+^8iAUp6m0
隔离级别是根据称为现象 (Phenomena) 的三个禁止操作序列来声明的:
$K6a9Ws#[:Sm0
脏读 (Dirty Read):在事务 A 提交修改结果之前,其他事务即可看到事务A的修改结果。
不可重复读 (Non-Repeatable Read):在事务A提交之前,允许其他事务修改和删除事务A涉及的数据,导致事务A中执行同样操作的结果集变小。
幻像读 (Phantom Read):事务A在提交查询结果之前,其他事务可以插入或者更改事务 A 涉及的数据,导致事务 A 中执行同样操作的结果集增大。
.P
@$G W:pk7lM0
数
据库并发性 ( 可以同时访问同一资源的事务数量 )
因隔离级别不同而有所差异,可重复读隔离级别可以防止所有现象,但是会大大降低并发性。未提交读隔离级别提供了最大的并发性,但可能会造成“脏读”、“幻
像读”或“不可重复读”现象。 DB2 默认的隔离级别是 CS 。
S,x}m
Q0
IBM DB2 数据库用户社区6jhb-Z4c{}kK
6.1.1 可重复读
IBM DB2 数据库用户社区5L.~f$hl^A"k&Aj}?
;kkd(AsFQ-tJ6h0
可
重复读隔离级别是最严格的隔离级别。在使用它时,一个事务的操作结果完全与其他并发事务隔离,脏读、不可重复读、幻像读都不会发生。当使用可重复读隔离级
别时,在事务执行期间会共享 (S) 锁定该事务以任何方式引用的所有行,在该事务中多次执行同一条 SELECT
语句,得到的结果数据集总是相同的。因此,使用可重复读隔离级别的事务可以多次检索同一行集,并可以对它们执行任意操作,直到提交或回滚操作终止事务。但
是,在事务提交前,不允许其他事务执行会影响该事务正在访问的任何行的插入、更新或删除操作。为了确保这种行为,需要锁定该事务所引用的每一行——
而不是仅锁定被实际检索或修改的那些行。因此,如果一个表中有 1000 行,但只检索两行,则整个表 (1000 行,而不仅是被检索的两行 )
都会被锁定。输出结果如下:
+q.l
q|Fu9W%[0
C:/>db2 +c select empno,firstnme,salary from employee where empno between '000010' and '000020' withrr EMPNO FIRSTNME SALARY ------ ------------ ----------- 000010 CHRISTINE 152750.00 000020 MICHAEL 94250.00 2 条记录已选择。 |
8fc{�pPQ&~0
我们通过“ get snapshot for locks on sample ”命令来监控表加锁情况,输出结果如下:
{}X&|P!Y0
C:/>db2 update monitor switches using lock on DB20000I UPDATE MONITOR SWITCHES 命令成功完成。 C:/>db2 get snapshot for locks on sample | more -------------- 略 ------------------ 锁定列表 锁定名称 = 0x020006000E0040010000000052 锁定属性 = 0x00000010 发行版标志 = 0x00000004 锁定计数 = 1 挂起计数 = 0 锁定对象名 = 20971534 对象类型= 表 表空间名= USERSPACE1 表模式= DB2ADMIN 表名= EMPLOYEE 方式= S -- 注:虽然读取了两行,但是整个表加S 锁 |
zMB?
j.eu0
如果
使用这种隔离级别,不管你从表中读多少数据,整个表上都加 S
锁,直到该事务被提交或回滚,表上的锁才会被释放。这样可以保证在一个事务中即使多次读取同一行,都会得到相同结果集。另外,在同一事务中如果以同样的搜
索标准重新打开已被处理过的游标,那么得到的结果集不会改变。可重复读相对于读稳定性而言,加锁的范围更大:对于读稳定性,应用程序只对符合要求的所有行
加锁;而对于重复读,应用程序将对整个表都加 S 锁。
'_uI/@�c[0
IBM DB2 数据库用户社区uOq0?g2`0@'Z
可重复读会锁定应用程序在工作单元中引用的整个表。利用可重复读,一个应用程序在打开游标的相同工作单元内发出一个 SELECT 语句两次,每次都返回相同的结果。利用可重复读隔离级别,不可能出现丢失更新、脏读和幻像读的情况。
-D}9tdpA0
IBM DB2 数据库用户社区�R�pSC,cXg]
在该工作单元完成之前,“可重复读”应用程序可以多次检索和操作这些行。但是,在该工作单元完成之前其他应用程序均不能更新、删除或插入可能会影响结果表的行。“可重复读”应用程序不能查看其他应用程序未提交的更改。
#Z5eJ
c9e?:f o0
t1u@ /Op0
6.1.2 读稳定性
m {.MfTet0
K4t%P�{i7ju%U0
读
稳定性隔离级别没有可重复读隔离级别那么严格;因此,它没有将事务与其他并发事务的效果完全隔离。读稳定性隔离级别可以防止脏读和不可重复读,但是可能出
现幻像读。在使用这个隔离级别时,只锁定事务实际检索和修改的行。因此,如果一个表中有 1000 行,但只检索两行 ( 通过索引扫描
),则只锁定被检索的两行 ( 而不是所扫描的 1000 行 ) 。因此,如果在同一个事务中发出同一个 SELECT
语句两次或更多次,那么每次产生的结果数据集可能不同。
~c2l!kB-o/9X%L_'y0
wf/D!y'V7qZX0
与
可重复读隔离级别一样,在读稳定性隔离级别下运行的事务可以检索一个行集 (ROWS
SET),并可以对它们执行任意操作,直到事务终止。在这个事务存在期间,其他事务不能执行那些会影响这个事务检索到的行集的更新或删除操作,但是可以执
行插入操作。如果插入的行与第一个事务的查询的选择条件匹配,那么这些行可能作为幻像出现在后续产生的结果数据集中。其他事务对其他行所作的更改,在提交
之前是不可见的。下面我们还用上面的那个例子锁定读稳定性,输出结果如下:
IBM DB2 数据库用户社区o_d5A9OiV"bw4i
M
C:/>db2 +c select empno,firstnme,salary from employee where empno between '000010' and '000020' withrs EMPNO FIRSTNME SALARY ------ ------------ ----------- 000010 CHRISTINE 152750.00 000020 MICHAEL 94250.00 2 条记录已选择。 |
H C&b(tn5_%ECgB-|d
IBM DB2 数据库用户社区g3F
Zo:X
I$]
我们通过“ get snapshot for locks on sample ”命令来监控表加锁情况,输出结果如下:
`Hp.er
qW/v0
C:/>db2 update monitor switches using lock on DB20000I UPDATE MONITOR SWITCHES 命令成功完成。 C:/>db2 get snapshot for locks on sample | more -------------- 略 ------------------ 锁定列表 锁定名称 = 0x02000600050040010000000052 锁定属性 = 0x00000010 发行版标志 = 0x00000001 锁定计数 = 1 挂起计数 = 0 锁定对象名 = 20971525 对象类型 = 行 表名 =EMPLOYEE 方式 =S -- 注:只在读取的行上加S 锁 锁定名称 = 0x02000600040040010000000052 锁定属性 = 0x00000010 发行版标志 = 0x00000001 锁定计数 = 1 挂起计数 = 0 锁定对象名 = 20971524 对象类型 = 行 表名 =EMPLOYEE 方式= S -- 注:只在读取的行上加S 锁 锁定名称 = 0x02000600000000000000000053 锁定属性 = 0x00000010 发行版标志 = 0x00000001 锁定计数 = 1 挂起计数 = 0 锁定对象名 = 6 对象类型= 表 表名 = EMPLOYEE 方式= IS -- 注:表上加IS 锁 |
kY,o9|%gg0
如果使用这
种隔离级,那么在一个事务中将有 N+1 个锁,其中 N 是所有被读取 ( 通过索引扫描 ) 过的行的数目,这些行上都会被加上 NS
锁,在表上加上 1 个 IS
锁。这些锁直到该事务被提交或回滚才会被释放。这样可以保证在一个事务中即使多次读取同一行,得到的值也不会改变。但是使用这种隔离级别,在一个事务中,
如果使用同样的搜索标准重新打开已被处理过的游标,则结果集可能改变 ( 可能会增加某些行,这些行被称为幻影行 (Phantom)) 。这是因为
RS 隔离级别不能阻止通过插入或更新操作在结果集中加入新行。
GW/^/7d y)ws2R;W0
IBM DB2 数据库用户社区dU7DW%A&M&U
注意:
-vbXB:bLKYf0
IBM DB2 数据库用户社区4t%IPjnlTCi
NS 是下一键共享锁,此时锁拥有者和所有并发的事务都可以读 ( 但不能更改 ) 被锁定行中的数据。这种锁用来在使用读稳定性或游标稳定性事务隔离级别读取的数据上代替共享锁。
BVg,ifZ!b.W0
m8u3C#~,Q0D-I0
读稳定
性 (RS)
只锁定应用程序在工作单元中检索的那些行。它确保在某个工作单元完成之前,在该工作单元运行期间的任何限定行读取不被其他应用程序进程更改,且确保不会读
取由另一个应用程序进程所更改的任何行,直至该进程提交了这些更改。也就是说,不可能出现“不可重复读”情形。
IBM DB2 数据库用户社区'GD�piVb-y
RPD
_
IBM DB2 数据库用户社区"Es0mD
fT
E(O'[tpb&E
“读稳定性”隔离级别的其中一个目标是提供较高并行性以及数据的稳定视图,为了有助于达到此目标,优化器确保在发生锁定升级前不获取表级锁定。
)}SH5M%U/E3/3N/Uf:})f$~0
IBM DB2 数据库用户社区~u:^^+K
Fug
“读稳定性”隔离级别最适用于包括下列所有特征的应用程序:
W5H AB]0
在并发环境下运行。
需要限定某些行在工作单元运行期间保持稳定。
在工作单元中不会多次发出相同的查询,或者在同一工作单元中发出多次查询时并不要求该查询获得相同的回答。
Tw2j6^%Cz:d"Lwx$Y*a0
6.1.3 游标稳定性
(i%d"eW{rs/U
{.l0
IBM DB2 数据库用户社区eL#p`5EB;[K9MQ
游标稳定性隔离级别在隔离事务效果方面非常宽松。它可以防止脏读;但有可能出现不可重复读和幻像读。这是因为在大多数情况下,游标稳定性隔离级别只锁定事务声明并打开的游标当前引用的行。
A[%h7?LMa0
IBM DB2 数据库用户社区jdd/gXVd
当
使用游标稳定性隔离级别的事务通过游标从表中检索行时,其他事务不能更新或删除游标所引用的行。但是,如果被锁定的行本身不是用索引访问的,那么其他事务
可以将新的行添加到表中,以及对被游标锁定行前后的行进行更新或删除操作。所获取的锁一直有效,直到游标重定位或事务终止为止 (
如果游标重定位,原来行上的锁就被释放,并获得游标现在引用的行上的锁 )
。此外,如果事务修改了它检索到的任何行,那么在事务终止之前,其他事务不能更新或删除该行,即使在游标不再位于被修改的行时。与可重复读和读稳定性隔离
级别一样,其他事务在其他行上进行的更改,在这些更改提交之前对于使用游标稳定性隔离级别的事务 ( 这是默认的隔离级别 )
是不可见的。我们还用上面那个例子,一个表中有 1000
行数据,我们只检索其中两行数据。那么对于可重复读隔离级别会锁住整个表,对于读稳定性隔离级别会对读到的数据 ( 两行 )
加锁,而对于游标稳定性隔离级别只对游标当前所在那一行加锁,游标所在行的前一行和下一行都不加锁。下面我们举一个游标稳定性的例子,输出结果如下:
8Jm X
H�p^OVl&T:aG0
C:/>db2 +c declare c1 cursor for select empno,firstnme,salary from employee where empno between '000010' and '000020' with cs C:/>db2 +c open c1 C:/>db2 +c fetch c1 EMPNO FIRSTNME SALARY ------ ------------ ----------- 000010 CHRISTINE 152750.00-- 注:游标当前所在行,DB2 只对这一行加锁。游标的 上一行和下一行都不加锁。当游标移动到下一行时,锁自动释放。 1 条记录已选择。 |
IBM DB2 数据库用户社区%i
g#m)T2M&lar/j!w?
我们通过“ get snapshot for locks on sample ”命令来监控表加锁情况,输出结果如下:
-b�|_*D_(U0
C:/>db2 update monitor switches using lock on DB20000I UPDATE MONITOR SWITCHES 命令成功完成。 C:/>db2 get snapshot for locks on sample | more -------------- 略 ------------------ 锁定名称 = 0x02000600040040010000000052 锁定属性 = 0x00000010 发行版标志 = 0x00000001 锁定计数 = 1 挂起计数 = 0 锁定对象名 = 20971524 对象类型= 行 表名= EMPLOYEE 方式= S -- 注:只在游标所在行上加S 锁 锁定名称 = 0x02000600000000000000000053 锁定属性 = 0x00000010 发行版标志 = 0x00000001 锁定计数 = 1 挂起计数 = 0 锁定对象名 = 6 对象类型= 表 表名= EMPLOYEE 方式= IS -- 注:表上加IS 锁 |
IBM DB2 数据库用户社区0c^�m3Gy
如
果使用这种隔离级,那么在一个事务中只有两个锁:结果集中只有正在被读取的那一行 ( 游标指向的行 ) 被加上 NS 锁,在表上加 IS
锁。其他未被处理的行上不加锁。这种隔离级别只能保证正在处理的行的值不会被其他并发的程序所改变。该隔离级别是 DB2 默认的隔离级别。
IBM DB2 数据库用户社区#E,W*FJD5F-J#SZ
IBM DB2 数据库用户社区[Z HKd6A9D,OZ
游标稳定性 (CS) 当在行上定位游标时会锁定任何由应用程序的事务所访问的行。此锁定在读取下一行或终止事务之前有效。但是,如果更改了某一行上的任何数据,那么在对数据库提交更改之前必须挂起该锁定。
8sw5jw*tYj6[0
Q)Z,?^Rl0m{ @;R0
对于具有“游标稳定性”的应用程序已检索的行,当该行上有任何可更新的游标时,任何其他应用程序都不能更新或删除该行。“游标稳定性”应用程序不能查看其他应用程序的未提交操作。
q@N H@(N4r0
IsnO ~0
使用“游标稳定性”,可能会出现不可重复读和幻像读现象。“游标稳定性”是默认隔离级别,应在需要最大并行性,但只看到其他应用程序中的已提交行的情况下才使用。
IBM DB2 数据库用户社区wdQ!fVI6Qm*V@
IBM DB2 数据库用户社区5z4CWF*E9J
6.1.4 未提交读
,Fe:T1wsy.y0
IBM DB2 数据库用户社区4d.k!?
D1tXlr0TJ U
未
提交读隔离级别是最不严格的隔离级别。实际上,在使用这个隔离级别时,仅当另一个事务试图删除或更改被检索的行所在的表时,才会锁定一个事务检索的行。因
为在使用这种隔离级别时,行通常保持未锁定状态,所以脏读、不可重复读和幻像读都可能会发生。因此,未提交读隔离级别通常用于那些访问只读表和视图的事
务,以及某些执行 SELECT 语句的事务 ( 只要其他事务的未提交数据对这些语句没有负面效果 ) 。
IBM DB2 数据库用户社区k.}J6zB%J%m
IBM DB2 数据库用户社区] s"I4Y:[%G
顾
名思义,其他事务对行所做的更改在提交之前对于使用未提交读隔离级别的事务是可见的。但是,此类事务不能看见或访问其他事务
DDL(CREATE、ALTER 和 DROP)
语句所创建的表、视图或索引,直到那些事务被提交为止。类似地,如果其他事务删除了现有的表、视图或索引,那么仅当进行删除操作的事务终止时,使用未提交
读隔离级别的事务才能知道这些对象不再存在了。
6HkWY2u$zz0
IBM DB2 数据库用户社区mJyfGQY H;R
一定要注意一点:当运行在未提交读隔离级别下的事务使用可更新游标时,该事务的行为和在游标稳定性隔离级别下运行一样,并应用游标稳定性隔离级别的约束。下面我们举一个例子。
IBM DB2 数据库用户社区%z*c%K
bk*_
v6QEZz8Y*t3}/Y0
我们编写一个 SQL 存储过程,在存储过程中我们显式地在 SELECT 语句中使用 UR 隔离级别。
IBM DB2 数据库用户社区&MB
|}0V5_cr^
D
0`y
~~m0
创建一个存储过程,保存为 LOCKS.SQL,输出结果如下:
IBM DB2 数据库用户社区.t.b/b*qC5wFP
CREATE PROCEDURE locks() LANGUAGE SQL BEGIN declare c1 cursor for select * from staff with UR; open c1; while 1=1 do ——注:死循环 end while; END @ |
g9[L,M.l%P0}0
为了方便抓住锁信息,我们在这个存储过程的结尾处使用了一个死循环。利用一个命令窗口运行存储过程,输出结果如下:
;c6W v(g
U"k0
C:/ >db2 – td@ -vf locks.sql C:/ >db2 "call locks()" |
,i wbb0O~)s0
再打开一个新的窗口,得到在 STAFF 表上的当前锁信息,输出结果如下:
IBM DB2 数据库用户社区2s _^!z3a.q
C:/>db2pd -db sample -locks show detail Locks: Address TranHdl Lockname Type Mode Sts Owner Dur HldCnt Att ReleaseFlg 0x408E0290 2 00020003000000000000000054 Table .IS G 2 1 0 0x0000 0x00000001 TbspaceID 2 TableID 3 |
1a-e�C(Rh0
但是会发现此时
在 STAFF 表上出现的是 IS 锁,而不是 IN 锁。是什么原因呢?这是因为 UR
隔离级别允许应用程序存取其他事务的未落实的更改,但是对于只读和可更新这两种不同的游标类型,UR
的工作方式有所不同。对于可更新的游标,当它使用隔离级别 UR 运行程序时,应用程序会自动使用隔离级别 CS 。
IBM DB2 数据库用户社区]8ww8G*r(k�q{
IBM DB2 数据库用户社区BQ(e2v
NS9_9C2~0x
在
上面的例子当中,虽然显式地指定了 SQL 语句的隔离级别是 UR,但是,由于在存储过程中使用的游标是模糊游标 (
也就是没有显式地声明游标是只读的还是可更新的 ),因而系统会默认地将这个模糊游标当成可更新游标处理,存储过程的隔离级别自动从 UR 升级为
CS 。要防止此升级,可以采用以下办法:
&[ @+a
DY/!Vy%|~0
修改应用程序中的游标,以使这些游标是非模糊游标。将 SELECT 语句更改为包括 FOR READ ONLY 子句。
将模糊游标保留在应用程序中,但是预编译程序或使用 BLOCKING ALL 和 STATIC READONLY YES 选项绑定它以允许在运行该程序时将任何模糊游标视为只读游标。
Y5jr*N;W~4W_�t0
我们还是使用上面的例子,显式地将该游标声明成只读游标,输出结果如下:
8_
~r%P0|nt8N6R0
declare c1 cursor for select * from stafffor read only with UR; |
'G~5rO~0
此时我们再运行这个存储过程,并利用 DB2PD 获取锁的情况,输出结果如下:
Bc#_:w*^,F
t#me0
c:/> db2pd -db sample -locks show locks Locks: Address TranHdl Lockname Type Mode Sts Owner Dur HldCnt Att ReleaseFlg 0x408E07E0 2 00020003000000000000000054 Table.IN G 2 1 0 0x0000 0x00000001 TbspaceID 2 TableID 3 |
IBM DB2 数据库用户社区`_e/c^
-
注:可以看到
STAFF
表上出现的锁是
IN
锁。
IBM DB2 数据库用户社区A`'Rwq"b+XAi
IBM DB2 数据库用户社区?PX#bO
从
上面的例子中我们可以看到:“未提交读 (UR)
”隔离级别允许应用程序访问其他事务的未提交的更改。除非其他应用程序尝试删除或改变该表,否则该应用程序也不会锁定正读取的行而使其他应用程序不能访问
该行。对于只读和可更新的游标,“未提交的读”的工作方式有所不同。
IBM DB2 数据库用户社区4I/7C7X#G/A*B
IBM DB2 数据库用户社区Qmc$QD}^#_o
如果使用这种隔离级别,那么对于只读操作不加行锁。典型的只读操作包括: SELECT 语句的结果集只读 ( 比如语句中包括 ORDER BY 子句 ) ;定义游标时指明起为 FOR FETCH ONLY 或 FOR READ ONLY 。
m}j@}#pi7d0
aG*Eek/#MvR0
该隔离级别可以改善应用程序的性能,同时可以达到最大程度的并发性。但是,应用程序的数据完整性将受到威胁。如果需要读取未提交的数据,该隔离级是唯一选择。
-vd4{,n'T0
9]6^2x^y:b_!u.k0
使用“未提交的读”,可能出现不可重复读行为和幻像读现象。“未提交读”隔离级别最常用于只读表上的查询,或者在仅执行选择语句且不关心是否可从其他应用程序中看到未提交的数据时也最常用。
'E.d5C-l/h3a1q0
3G&x8mh'[H0
以上我们所讲的隔离级别的加锁范围和持续时间都是针对读操作而言的。对于更改操作,被修改的行上会被加上 X 锁,无论使用何种隔离级别,X 锁都直到提交或回滚之后才会被释放。
IBM DB2 数据库用户社区'b'G!m8iv:]m
IBM DB2 数据库用户社区'U/kTB0A[Pn
i
6.1.5 隔离级别加锁示例讲解
IBM DB2 数据库用户社区q'M2c9bLd
9O%U7BZ9VBY0Z0
假设有一张表 EMP1,表中有 42 条记录,我们使用 FOR READ ONLY 分别在 UR、CS、RS 和 RR 隔离级别下加锁。
Z5R-X%u9{2m6c0
,B^8I"D}h]0
EMP1 表在本章后续的内容中也会使用到,其创建过程如下:
/FF"qrTg @a S0
C:/> db2 "create table emp1 like employee" C:/> db2 "insert into emp1 select * from employee" |
_M/`
Bu0
gR;/La)mP6P0
我们使用 EMP1 表中 JOB 字段内容为 'CLERK' 的数据,输出结果如下:
!J
q$NF8jU`0
C:/>db2 +c select empno,job,salary from emp1 where job='CLERK' for read only EMPNO JOB SALARY ------ -------- ----------- 000120 CLERK 49250.00 000230 CLERK 42180.00 000240 CLERK 48760.00 000250 CLERK 49180.00 000260 CLERK 47250.00 000270 CLERK 37380.00 200120 CLERK 39250.00 200240 CLERK 37760.00 8 条记录已选择。 |
+U-r3{0}&RT'iXi0
在上面的 SQL 语句中,我们从表的 42 条记录中返回 8 条记录。下面我们分别看看这条语句在不同的隔离级别下加锁的情况:
IBM DB2 数据库用户社区+Q$OZf'{
+`:W]:jL+b&c$Ug0
UR 隔离级别,输出结果如下:
IBM DB2 数据库用户社区/p
},T["r,O
r3Y
C:/>db2 +c select empno,job,salary from emp1 where job='CLERK' for read onlywith ur EMPNO JOB SALARY ------ -------- ----------- 000120 CLERK 49250.00 000230 CLERK 42180.00 000240 CLERK 48760.00 000250 CLERK 49180.00 000260 CLERK 47250.00 000270 CLERK 37380.00 200120 CLERK 39250.00 200240 CLERK 37760.00 8 条记录已选择。 |
IBM DB2 数据库用户社区(d^�W/y
x8Ix|
在另外一个窗口中使用“ db2 get snapshot for locks on sample ”命令监控,发现在 UR 隔离级别下,在表上有一个 IN 锁,没有加任何行锁。
IBM DB2 数据库用户社区af};S8Xy/G7c
+V*rH*OBrX0
CS 隔离级别,输出结果如下:
] qx�C+T$b^;n0
C:/>db2 +c declare c1 cursor for select empno,job,salary from emp1 where job='CLERK' for read onlywith CS C: />db2 +c open c1C: />db2 +c fetch c1 EMPNO JOB SALARY ------ -------- ----------- 000120 CLERK 49250.00 1 条记录已选择。 |
T/R'~0
f+T/G.K7B4^k0
在另外一个窗口中使用“ db2 get snapshot for locks on sample ”命令监控,发现在 CS 隔离级别下,共有两个锁:在表上有一个 IS 锁,在行上有一个 NS 锁。
IBM DB2 数据库用户社区wO
PDok
8g�|uzL!j,E8@+d4Y#z0
RS 隔离级别,输出结果如下:
Y$U%Mv0e1v!C7y0
C:/>db2 +c select empno,job,salary from emp1 where job='CLERK' for read onlywith RS EMPNO JOB SALARY ------ -------- ----------- 000120 CLERK 49250.00 000230 CLERK 42180.00 000240 CLERK 48760.00 000250 CLERK 49180.00 000260 CLERK 47250.00 000270 CLERK 37380.00 200120 CLERK 39250.00 200240 CLERK 37760.00 8 条记录已选择。 |
_
t2eDx4L(Q
YX0
在另外一个窗口中使用“ db2 get snapshot for locks on sample ”命令监控,发现在 RS 隔离级别下,共有 9 个锁:在表上有一个 IS 锁,在读取的 8 行上分别有 1 个 NS 锁。
,VO^:AnG0
IBM DB2 数据库用户社区Z},W
P[:QaK
RR 隔离级别,输出结果如下:
IBM DB2 数据库用户社区Ed+k:}uC5J_c+]q
C:/>db2 +c select empno,job,salary from emp1 where job='CLERK' for read onlywith RR EMPNO JOB SALARY ------ -------- ----------- 000120 CLERK 49250.00 000230 CLERK 42180.00 000240 CLERK 48760.00 000250 CLERK 49180.00 000260 CLERK 47250.00 000270 CLERK 37380.00 200120 CLERK 39250.00 200240 CLERK 37760.00 8 条记录已选择。 |
{ ]5xy4h0
K;`(UX!i(D0
在另外一个窗口中使用“ db2 get snapshot for locks on sample ”命令监控,发现在 RR 隔离级别下,分为两种情况:
IBM DB2 数据库用户社区Z8^ M2d9I6P
IBM DB2 数据库用户社区o*f;P%J-~+C9I
如果该 SQL 语句使用全表扫描,那么即使只读取了 8 行,也会在整个表上加一个 S 锁,输出结果如下:
w+//9bV"R&K
/�c^;B0
C:/>dynexpln -d sample -q "select empno,job,salary from emp1 where job='CLERK' for read only with rr" – t Access Table Name = DB2ADMIN.EMP1 ID = 3,12 | #Columns = 2 | Relation Scan -- 注:全表扫描 | | Prefetch: Eligible | Isolation Level: Repeatable Read -- 注:RR 隔离级别 | Lock Intents| | Table: Share -- 注:整个表上加S 锁 | | Row : None | Sargable Predicate(s) | | #Predicates = 1 | | Return Data to Application | | | #Columns = 3 Return Data Completion End of section |
QfZP
@
IBM DB2 数据库用户社区r
q5x1B.z}
如果创建索引,并进行索引扫描,那么表上加 IS 锁,读取的每行上加 S 锁。所以对于 RR 隔离级别来说,为了保证并发,尽可能创建合理的索引以减少加锁的范围,输出结果如下:
IBM DB2 数据库用户社区*zX*AG(d1^-F8Y6e
C:/>db2 create index job on DB2ADMIN.emp1(job) DB20000I SQL 命令成功完成。 C:/>db2 runstats on table DB2ADMIN.emp1 and indexes all DB20000I RUNSTATS 命令成功完成。 C:/>dynexpln -d sample -q "select empno,job,salary from emp1 where job='CLERK' for read only with rr" -t Access Table Name = DB2ADMIN.EMP1 ID = 3,12 | Index Scan: Name = DB2ADMIN.JOB ID = 1 -- 注:索引扫描 | | Regular Index (Not Clustered) | | Index Columns: | | | 1: JOB (Ascending) | #Columns = 2 | #Key Columns = 1 | | Start Key: Inclusive Value | | | | 1: 'CLERK ' | | Stop Key: Inclusive Value | | | | 1: 'CLERK ' | Data Prefetch: Eligible 0 | Index Prefetch: None| Isolation Level: Repeatable Read -- 注:RR 隔离级别 | Lock Intents| | Table: Intent Share -- 注:表上加IS 锁| | Row : Share -- 注:行上加S 锁 | Sargable Predicate(s) | | Return Data to Application | | | #Columns = 3 Return Data Completion End of section |
IBM DB2 数据库用户社区^}e(`6f!B
6.1.6 隔离级别摘要
p-/4tRJq&GHyF0m0
L4Z([+^sp5_lg0
表 6-1 按不期望的结果概述了几个不同的隔离级别。
/O.Z/X
q&e,A3eh0
/V|ck^Q@0
表 6-1 隔离级别摘要
IBM DB2 数据库用户社区vu)Jy9`/wVfEjd
隔离级别 | 访问未提交的数据 | 不可重复读 | 幻像读现象 |
---|---|---|---|
可重复读 (RR) | 不可能 | 不可能 | 不可能 |
读稳定性 (RS) | 不可能 | 不可能 | 可能 |
游标稳定性 (CS) | 不可能 | 可能 | 可能 |
未提交读 (UR) | 可能 | 可能 | 可能 |
IBM DB2 数据库用户社区'T-rKC })BOy
表 6-2 提供了简单的试探方法,以帮助您为应用程序选择初始隔离级别。首先考虑表中列示的方法,并参阅先前对影响各隔离级别因素的讨论,可能会找到另一个更适合的隔离级别。
IBM DB2 数据库用户社区z#j&LQssXKk0w)T
4ev.e
j:u/Z0
表 6-2 选择隔离级别的准则
H�oj6De0
应用程序类型 | 需要高数据稳定性 | 不 需要高数据稳定性 |
---|---|---|
读写事务 | RS | CS |
只读事务 | RR 或 RS | UR |
IBM DB2 数据库用户社区4^&t+{i!v&L-K]
为
避免应用程序出现用户无法容忍的现象,必须为其选择适当的隔离级别。在不同隔离级别下,应用程序锁定或释放资源需要不同的 CPU
和内存资源,所以隔离级别不但影响应用程序之间的隔离程度,还可能影响应用程序的个别性能特征。潜在的锁等待情况也会随隔离级别的不同而不同。
C9q?4Cer0
IBM DB2 数据库用户社区lGsVQn5S
因为隔离级别确定访问数据时如何锁定数据并使数据不受其他进程影响,所以您在选择隔离级别时应该平衡并行性和数据完整性需求。您指定的隔离级别在工作单元运行期间生效。
Uf7~�rFD&e,@0
选择正确的隔离级别
8C*pzZ#M0
IBM DB2 数据库用户社区-W&KXlx�`.t
使
用的隔离级别不仅影响数据库的并发性,而且影响并发应用程序的性能。通常,使用的隔离级别越严格,并发性就越小,某些应用程序的性能可能会随之越低,因为
它们要等待资源上的锁被释放。那么,如何决定要使用哪种隔离级别呢?最好的方法是先确定哪些现象是不可接受的,然后选择能够防止这些现象发生的隔离级别。
以下列举了各种隔离级别的适用情况:
IBM DB2 数据库用户社区TC
px%o&Gf
如果正在执行大型查询,而且不希望并发事务所做的修改导致查询的多次运行返回不同的结果,则使用可重复读隔离级别。
如果希望在应用程序之间获得一定的并发性,还希望限定的行在事务执行期间保持稳定,则使用读稳定性隔离级别。
如果希望获得最大的并发性,同时不希望查询看到未提交的数据,则使用游标稳定性隔离级别。
如果正在只读的表 / 视图 / 数据库上执行查询,或者并不介意查询是否返回未提交的数据,则使用未提交读隔离级别。
设置隔离级别
Z
O7|tk&s"]G(aj3n0
&LG3E!H!V+r$_-{v^!ac0
尽管隔离级别控制事务级上的行为,但实际上它们是在应用程序级被指定的:
IBM DB2 数据库用户社区0z|�O2o�?/L
对于嵌入式 SQL 应用程序,在预编译时或在将应用程序绑定到数据库 ( 如果使用延迟绑定 ) 时指定隔离级别。在这种情况下,使用 PRECOMPILE 或 BIND 命令的 ISOLATION 选项来设置隔离级别。
对于
开放数据库连接 (ODBC) 和调用级接口 (Call Level Interface,CLI)
应用程序,隔离级别是在应用程序运行时通过调用指定了 SQL_ATTR_TXN_ISOLATION 连接属性的
SQLSetConnectAttr() 函数进行设置的。另外,也可以通过指定 DB2CLI.INI 配置文件中的 TXNISOLATION
关键字的值来设置 ODBC/CLI 应用程序的隔离级别;但是,这种方法不够灵活,不能像第一种方法那样为一个应用程序中的不同事务修改隔离级别。
对于 Java 数据库连接 (JDBC) 和 SQLJ 应用程序,隔离级别是在应用程序运行时通过调用 DB2 的 JAVA.SQL 连接接口中的“ setTransactionIsolation() ”方法设置的。
IBM DB2 数据库用户社区4e#Bc,ru DdXK
当
没有使用这些方法显式指定应用程序的隔离级别时,默认使用游标稳定性 (CS) 隔离级别。这个默认设置被应用于从命令行处理程序 (CLP) 执行的
DB2 命令、SQL 语句和脚本,以及嵌入式 SQL、ODBC/CLI、JDBC 和 SQLJ 应用程序。因此,也可以为从 CLP 执行的操作
( 以及传递给 DB2 CLP 进行处理的脚本 ) 指定隔离级别。在这种情况下,隔离级别是通过在建立数据库连接之前在 CLP 中执行
CHANGE ISOLATION 命令设置的,输出结果如下:
+NBn_GtI2K0
C:/pp>db2 change isolation to ur DB21027E 当连接至数据库时未能更改隔离级别。 C:/pp>db2 connect reset DB20000I SQL 命令成功完成。 C:/pp>db2 change isolation to ur DB21053W 当连接至不支持 UR 的数据库时,会发生自动升级。 DB20000I CHANGE ISOLATION 命令成功完成。 |
IBM DB2 数据库用户社区]Z&T%@m�Q4w)f"M
在 DB2 V7.1 及更高版本中,能够指定特定查询所用的隔离级别,方法是在 SELECT SQL 语句中加上 WITH [RR | RS | CS | UR] 子句。大家可以看到,本章前面的示例均使用这种方法举例。
IBM DB2 数据库用户社区4e
Y
MiF!P0YX
IBM DB2 数据库用户社区5zE'q;}:BG?
![]() ~$V7L2I AR9R0 ![]() |
![]() IBM DB2 数据库用户社区"l]&//G;f CnX5p
|
IBM DB2 数据库用户社区!e
^&E
R8b%kQ
m�~0ry$Fve"u0
6.2 加锁总结
IBM DB2 数据库用户社区
| D(m%WlK4F!S8O
eVYP
qz0
6.2.1 如何获取锁
(|]*?Z0~/sl%V0
IBM DB2 数据库用户社区Q frs{4Om&o/p
在
大多数情况下,DB2 数据库管理程序在需要锁时隐式地获取它们,因此这些锁在 DB2
数据库管理程序的控制之下。除了使用未提交读隔离级别的情况外,事务从不需要显式地请求锁。实际上,唯一有可能被事务显式锁定的数据库对象是表
(LOCK TABLE) 。图 6-1 说明了用何种逻辑确定为所引用的对象获取什么类型的锁。
IBM DB2 数据库用户社区H!E(Bn5~.d0S-?s]W
IBM DB2 数据库用户社区*l+Pn6]+S,o,kD
图 6-1 如何获取锁
IBM DB2 数据库用户社区E�o ]Pp^#LP

,Q
^C ] TD0
IBM DB2 数据库用户社区q6[q![R
从
图 6-1 中我们可以看到,数据库首先判断该 SQL
语句是采用全表扫描还是索引扫描。如果是全表扫描,那么会在整个表上加表级别的锁;如果是读操作,那么获取表级 S 锁;如果是
DML(INSERT、UPDATE 和 DELETE) 操作,那么获取表级 X 锁。假设 SQL
语句采用的是索引扫描,如果是读操作,在读取的行上加 NS 锁,同时在表上加 IS 锁;如果是 DML 操作,那么在操作的行上加 X
锁,同时在表上加 IX 锁。
`R*uLH+w0C4j/l0
-NccJ rnZ3AKM;}*o0
注意:
IBM DB2 数据库用户社区8I3I;XF!Sxe
L~Tt6lud0
假设一个表中有 1000 行数据,某个 SQL 语句访问该表中的两行数据。如果该表没有索引,那么这条 SQL 只能进行全表扫描,这种情况下即使你只访问两行数据,但是由于没有索引也必须进行全表扫描,这时整个表都被加锁。
%m`b:{0zFdA"n0
IBM DB2 数据库用户社区s3?^5__1r/f
DB2 数据库管理程序默认总是尝试获取行级锁。但是,可以通过执行特殊形式的 ALTER TABLE 语句来修改这种行为,输出结果如下:
IBM DB2 数据库用户社区N!bfD'i6V,RCZ
ALTER TABLE [TableName ] LOCKSIZE TABLE |
Ctu6v Gz7B0
其中的
TableName 标识一个现有表的名称,所有事务在访问它时都要获取表级锁。 ALTER TABLE 语句的 LOCKSIZE
子句指定行级别或表级别的锁定作用域 ( 详细程度 ) 。默认情况下,使用行锁定。这些已定义的表锁定仅请求 S( 共享 ) 和 X( 互斥 )
锁定。 ALTER TABLE 语句的 LOCKSIZE ROW 子句不会阻止正常的锁定升级。
+xr(i1KKlM*H0
zf Br%O8Z0
也可以在应用程序中通过执行 LOCK TABLE 语句,强制 DB2 数据库管理程序为特定事务在表上获取表级锁,输出结果如下:
[4|$N&K-f$p0
LOCK TABLE [TableName ] IN [SHARE | EXCLUSIVE] MODE |
IBM DB2 数据库用户社区k&f MR)y3n|
其
中的 TableName 标识一个现有表的名称,对于这个表应该获取表级锁 ( 假定其他事务在该表上没有不兼容的锁 )
。如果在执行这个语句时指定了共享 (SHARE) 模式,就会获得一个允许其他事务读取 ( 但不能更改 )
表中数据的表级锁;如果执行时指定了互斥 (EXCLUSIVE) 模式,就会获得一个不允许其他事务读取或修改表中数据的表级锁。
,L^1FZ6Z`0
6q8@2Skt)[ B)_0x4y0
在下列情况下,由 ALTER TABLE 语句定义的永久表锁定可能比使用 LOCK TABLE 语句获得的单个事务表锁定更可取,原因如下:
IBM DB2 数据库用户社区N;IZ~W$~
表是只读的,且将始终只需要 S 锁定,其他用户也可以获取表的 S 锁定。
表通常由只读应用程序访问,但有时由单个用户访问可以进行简要维护,而该用户需要 X 锁定。当维护程序运行时,将只读应用程序锁定在外,但在其他情况下,只读应用程序可以使用最小的锁定开销同时访问表。
IBM DB2 数据库用户社区@8]P}~}
总结一下:ALTER TABLE
语句全局指定锁定,它影响访问该表的所有应用程序和用户。单个应用程序可以使用LOCK TABLE
语句来指定应用程序级别的表锁定。
IBM DB2 数据库用户社区B/] q(h;m[3t7os$qZ
5z)ey k2l%M*n0
6.2.2 意图锁和非意图锁
x9Yd8X-J!iL0
IBM DB2 数据库用户社区8ItQE/z+i
对
于 IN、IX、IS 和 SIX 这些意图 (INTENT)
锁,读者可以这样理解:严格来说它们并不是一种锁,而是用来存放表中行锁的信息。举个通俗的例子,我们去住一个酒店。我们把整个酒店比喻成一张表,每个房
间是一行。那么当我们预订一个房间时,就对该行 ( 房间 ) 加 X 锁,但是同时会在酒店的前台对该行 ( 房间 ) 做一个信息登记 (
旅客姓名、身份证、住多长时间等 )
。大家可以把意图锁当成是这个酒店前台的登记信息,它并不是真正意义上的锁,而是维护表中每行的加锁情况,所有访问这个表的应用程序共用这个意图锁。后续
的旅客来时通过酒店前台来看哪个房间是可住的。那么如果没有意图锁,会出现什么情况呢?假设我要预订房间,那么每次我都需要到每一个房间查看确认这个房间
有没有住旅客,这样的效率显然是很低下的。其实最早的 DB2 版本是没有意图锁的,但是这对并发影响非常大,后来就增加了意图锁。所有的数据库
(Oracle、Informix 和 Sybase)
都有意图锁的实现机制。在一个表上只有一个意图锁,所有应用程序共用这个意图锁,但是可能经常会更改。
IBM DB2 数据库用户社区)}7bN#WFjW5NQLb
)K9X.C_T`0
6.2.3 读锁和写锁
#rPW&{,Ai0
IBM DB2 数据库用户社区-U9CHG#eS7B
在 DB2 数据库中有两种主要类型的锁:读锁 (S) 和写 (X) 锁。
IBM DB2 数据库用户社区+A7DYUU:`S5eT
o:k^*i,H*bp@_u0
一般来说读锁是在如下情况下加的:
2a7Oc:U`Ljb8b b0
IBM DB2 数据库用户社区+y(I'l;F*r@cx
v
NS
是在 RS 和 CS 隔离级别下对读取到的行加的锁。而 S 锁是在 RR 隔离级别下对读取到的表 ( 使用全表扫描 ) 或行 ( 使用索引扫描
) 加的锁。 U 锁是在“ select * from t1 for update ”情况下加的锁。这些锁都是在读取 (SELECT)
期间加的锁。
IBM DB2 数据库用户社区"_z
SJ&g:K$}M
.MZ7z1Va0
一般来说写锁是在如下情况下加的:
+qvC$ru4U$up0
/r5DZUiRYW1f5^:q0
Z
锁是超级排它锁,它不允许任何隔离级别的读取,一般是在数据物理结构发生改变的情况下加的锁。例如:CREATE、ALTER、DROP、离线
REORG 和离线 LOAD 期间会加 Z 锁。 X 锁是在做 INSERT、UPDATE 和 DELETE 期间加的锁,它允许使用 UR
隔离级别进行未提交读取。 NW 锁表示当一行被插入到索引中的时候,该行的下一行会被加上该锁。锁的拥有者可以读但不能更改锁定行。该锁与 X
锁类似,只是与 NS 锁兼容。
IBM DB2 数据库用户社区'](H.Q"Zll6F5hb
3f{9a+Osp4_2Q.w1p@0
6.2.4 LRB(Lock Resource Block)
o/ZM m&A/{5sS0
i7?%~5NtPK0
每个数
据库都有一个锁列表,该列表包含所有同时连接到数据库的应用程序所持有的锁。在 32 位平台上,一个对象上的第一个锁要求占 72
字节,而其他锁要求占 36 字节。在 64 位平台上,第一个锁要求占 128 字节 (HP 平台为 80 字节 ),而其他锁要求占 64
字节。 关于锁占用资源块 (LRB:Lock Resource Block),在各个版本还不一样,表 6-3 是 DB2 V9 中 LRB
占用资源的情况。
IBM DB2 数据库用户社区6sYgR/H
IBM DB2 数据库用户社区|K4|Y"w/X2od'q6Ui�W
表 6-3 DB2 V9 中 LRB 占用资源的情况
2/�q8bB|qQV0
Architecture | LRB Size | First Transaction to Lock | Subsequent Locks |
32-bit | 48 bytes | 96 bytes | 48 bytes |
64-bit | 64 bytes | 128 bytes | 64 bytes |
64-bit HP_UX | 80 bytes | 160 bytes | 80 bytes |
{2{p~+^*h0
注意:
0Q0FRm
gTz/Z0
q;iY IF&t0
关
于 LRB,在 DB2 的各个版本很不一样。在 DB2 V8 之前,在 32 位平台上,在一个没有持有其他锁定的对象上持有一个锁定需要 72
字节,在一个持有了现存锁定的对象上记录一个锁定需要 36 字节;在 DB2 V8 的后期版本中,在一个没有持有其他锁定的对象上持有一个锁定需要
64 字节,在一个持有了现存锁定的对象上记录一个锁定需要 32 字节;在 64 位平台上,要对没有其他锁定的对象上保留锁定需要 112
字节,要对具有现有锁定的对象上保留锁定需要 56 字节。 DB2 V9 中的 LRB 情况如表 6-3 所示。
!o.Js"`+@0
IBM DB2 数据库用户社区8hw5]G7_|3jSS
}
6.2.5 USE AND KEEP LOCKS
(TB"O(D5cL
|0
6g2~'P)y:J:R0
在 DB2 中,默认情况下锁都是由 DB2 数据库管理器根据应用程序的隔离级别自动设置锁类型。 DB2 提供了一种方式允许用户明确地向 DB2 数据库管理器请求锁类型:
@-CPV4T-d0
USE AND KEEP EXCLUSIVE LOCKS:向 DB2 数据库管理器明确请求在数据上加排它锁。
USE AND KEEP UPDATE LOCKS:向 DB2 数据库管理器明确请求在数据上加更新锁。
USE AND KEEP SHARE LOCKS:向 DB2 数据库管理器明确请求在数据上加共享锁。
3FT5Ej%h
NP1M0
例如:
4u0m
A3Pc$C0
DECLARE c1 CURSOR FOR select empno,job,salary from emp where job='CLERK' FOR UPDATE WITH RS USE AND KEEP EXCLUSIVE LOCKS |
j0
IBM DB2 数据库用户社区.g8`WL�o
n3N
在上面的语句中,如果没有带 USE AND KEEP EXCLUSIVE LOCKS 子句,默认情况下 DB2 会向行加更新锁 (U 锁 ),使用了该子句后将会变为排它锁 (X 锁 ) 。
ou{O-X
]?.j:{0
IBM DB2 数据库用户社区9V3?:p%AJ3c5v;XX9D#}
再如:
fH
k5i#uPyb`0
DECLARE c1 CURSOR FOR select empno,job,salary from emp where job='CLERK' FOR FETCH ONLY WITH RR USE AND KEEP UPDATE LOCKS |
IBM DB2 数据库用户社区:E"] Y1m,nt�`
在上面的语句中,如果没有带 USE AND KEEP EXCLUSIVE LOCKS 子句,默认情况下 DB2 会向行加下一键共享锁 (NS 锁 ),使用了该子句后将会变为更新锁 (U 锁 ) 。
IBM DB2 数据库用户社区L!~fa$|R2Rl
"teq-w UZ&y+Gh0
再如:
IBM DB2 数据库用户社区nv:{.G1Q:m&x
DECLARE c1 CURSOR FOR select empno,job,salary from emp where job='CLERK' FOR UPDATE WITH RS USE AND KEEP SHARE LOCKS |
IBM DB2 数据库用户社区gT4K:H;?K1N+i
在上面的语句中,如果没有带 USE AND KEEP EXCLUSIVE LOCKS 子句,默认情况下 DB2 会向行加更新锁 (U 锁 ),使用了该子句后将会变为下一键共享锁 (NS 锁 ) 。
kh
QS"g)m9Yd0
$voG6DvX#]0
为什么
要这样显式请求锁类型呢?这是因为 USE AND KEEP LOCKS
显式请求锁类型有助于避免多个存取数据库的独立进程的应用程序可能产生的死锁。例如,在一个应用程序中的数个进程存取同一个表,对该表并行进行读取及写入
操作。如果这些进程执行读 SQL 查询,然后再对同一表执行 SQL
更新,那么各个进程间对同一数据潜在的争用会使得死锁的几率增大。例如,如果两个进程读该表,然后更新该表,那么 A 进程先获得 S 锁,同时 B
进程也获得 S 锁。当 A 进程发出更新语句时试图获得对行的 X 锁定,而 B 进程对该行具有 S 锁定。此时 A 进程进入锁等待 (Lock
Wait) 状态,等待 B 进程释放 S 锁。当后来进程发出更新语句时试图获得对行的 X 锁定,而进程 A 对该行具有 S 锁定。此时 B
进程进入锁等待 (LOCK WAIT) 状态,等待 A 进程释放 S
锁。这样就产生了死锁,为了避免发生这种死锁,存取具有修改意向的数据的应用程序应该执行下列其中一项操作:
IBM DB2 数据库用户社区+b0F.j;e~4Hc
执行选择操作时使用 FOR UPDATE OF 子句。此子句确保当 A 进程试图读取该数据时进行 U 锁定,禁用行分块 (BLOCKING) 。
执行查询时使用 WITH RR USE AND KEEP UPDATE LOCKS 子句或
WITH RS USE AND KEEP UPDATE LOCKS 子句。任一子句都确保当 A 进程试图读取该数据时进行 U
锁定,并且允许行分块 (BLOCKING) 。
IBM DB2 数据库用户社区{*`mgJ^^O
6.2.6 索引类型和下一键锁
'fY g1cu
jlv0
IBM DB2 数据库用户社区h-a;[fg
DB2 中有两种索引类型:type-1 索引和 type-2 索引。这两种索引加锁的情况是不一样的,下面我们来分别介绍这两种索引的加锁算法。
kS+S&Y+/
M*zo6d0
lT5F6o oc'HJ4~0
type-1 索引加锁算法
7Ps
Pm2vrJP{f'S0
4t5^ _mY,J3F1m0
在 DB2 V8 之前,DB2 只有一种索引类型,也就是我们今天称之为的 TYPE-1 索引,这种索引在删除和插入的时候特别容易引起死锁从而影响并发。是什么原因呢,下面我们举一个使用 TYPE-1 索引的例子:
IBM DB2 数据库用户社区Q$[+j-Zi4j {
2~%k2c4E*z4N I0
假设一个索引的叶子 (LEAF) 中包含 1、5、6、7、8、12 6 个 KEY 。
1KCmX+RWb1dx-Q%pb0
假如现在交易 1 删除 KEY VALUE 8 对应的行,在删除期间,KEY VALUE 8
对应的行上会加 X 锁。当 KEY VALUE 8 被删除以后,就会在索引的下一键也就是 8 的下一个键 12 上加 NX 锁,相应地会在
KEY VALUE 12 对应的行上加 X 锁。
如果另外一个交易 2 删除 KEY VALUE 5 对应的行,在删除期间,KEY VALUE
5 对应的行上会加 X 锁。当 KEY VALUE 5 被删除以后,就会在索引的下一键也就是 5 的下一个键 6 上加 NX 锁,相应地会在
KEY VALUE 6 对应的行上加 X 锁。
假设现在交易 1 插入一行 KEY VALUE 4 相应的行,这一行会加 W 锁,当插入新的 KEY 到索引的时候,KEY VALUE 6 对应的行会加 NW 锁。因为此时交易 2 对应的行上持有 X 锁,这时它不得不等待交易 2 释放掉该锁。
同样假设现在交易 2 插入一行 KEY VALUE 9 相应的行,这一行会加 W 锁,当插入新的 KEY 到索引的时候,KEY VALUE 12 对应的行会加 NW 锁。因为此时交易 1 对应的行上持有 X 锁,这时它不得不等待交易 1 释放掉该锁。
M!d5zbk(zPn0
所以在 type-1 索引时,因为更改期间加锁的方式是 W 和 NW,所以很容易造成死锁,这会极大影响数据库的并发。
vPd7N!e:_%X(i0
IBM DB2 数据库用户社区`JC-c(v#A1NV
type-2 索引加锁算法
IBM DB2 数据库用户社区%]G0ra�G$|DQ
y
sb/W8j9^fd0
DB2
从 V8 以后所有新创建的索引都是 type-2 类型的索引。 type-2 索引可以大大減少 NEXT KEY
锁从而改进了性能,因为各项是标记为删除的而不是从页面中物理删除的 ( 伪删除 ) 。 type-2 索引同时允许索引列大于默认的 255
字节,同时还可以在线运行 REORG 和 RUNSTATS,并且支持新的多维群集 (MDC) 功能。在 DB2 V8 中,所有新的索引都是以
type-2 类型创建的,只有已经在表上定义了 ( 迁移前 ) type-1 索引的時候除外。可以使用 REORG INDEXES 将
type-1 索引转换为 type-2 索引。 Type-2 索引采用的是伪删除算法,图 6-2 是两种索引类型加锁的比较。
IBM DB2 数据库用户社区$U%_0F*_@n2H;s
IBM DB2 数据库用户社区Fyt ^W
M [$l QP)K
图 6-2 type-1 和 type-2 索引加锁比较
GW9Al;F!YGG8ri0

IBM DB2 数据库用户社区pN3E
p�p8A1C
6Q;a"`E0Ee0
在 DB2 V8 之前的版本中,插入过程中可能使用 W 或 NW 锁,但是在 DB2 V8 以后只有在使用了隔离级别为 RR 的情况下才会出现这两种锁。因此,应尽可能避免这种情况。
IBM DB2 数据库用户社区!y`/X.^H
./5pG"z5Z2CZ0
6.2.7 扫描方式加锁情况
(rW)P{4/b,TCm0
'gb0A%}(rywX!m?0
在
DB2 数据库中,不同的扫描方式在不同的隔离级别下加锁的情况也是不一样的,在 DB2 中主要有以下几种扫描方式:全表扫描、索引扫描和 RID
扫描 ( 注:如果使用了 MDC,那么还会有其他扫描方式,但此处我们不讨论 ) 。其中 RID 扫描也算是索引扫描。表 6-5 和表 6-6
总结了在全表和索引扫描方式下加锁的情况。
c$lZ0p(p0
GU/t%[FW0
表 6-4 表扫描时在表 / 行上加锁情况
IBM DB2 数据库用户社区,J5dNZ3Dh?T

//A"U//
Gk)iH7B?0
IBM DB2 数据库用户社区�I3O+A!h0zC;i,H.U2o.b"C
表 6-5 索引扫描时在表 / 行上的加锁情况
D"uZ
qQx0


%[X/x�P#N;k6/(W&]0
IBM DB2 数据库用户社区vR�I[3Z(`
关于在不同隔离级别和扫描方式下表 / 行上的加锁情况,我们可以在 DB2 解释工具的输出中查看加锁情况。下面我们分别对全表扫描和索引扫描的加锁情况举例:
%j5HDh8]aP/A0
IBM DB2 数据库用户社区r(l�Y7c A:jc/_!Y�n
全表扫描加锁情况如下:
IBM DB2 数据库用户社区�r"z-`9T*u/�l1^/W
dynexpln – d sample – q "select * from employee with rr" -t ---------------------------- 略 -------------------------- | Isolation Level: Repeatable Read | Lock Intents | | Table: Share | | Row : None | Sargable Predicate(s) ---------------------------- 略 ------------------------------ dynexpln – d sample – q "select * from employee with rs" -t| Relation Scan -- 注:全表扫描 | | Prefetch: Eligible| Isolation Level: Read Stability -- 注:隔离级别为RS | Lock Intents| | Table: Intent Share -- 注:表上加IS 锁 | | Row : Next Key Share-- 注:行上加 NS 锁 | Sargable Predicate(s) |
@1n
cdZ
IBM DB2 数据库用户社区�{F-N pV
l,U
索引扫描加锁情况如下:
5I1oc
/.V]�uP l4{0
dynexpln – d sample – q "select * from employee where empno='000100' with rs" -t ---------------------------- 略 -------------------------- | Index Scan: Name = ORACLE.PK_EMPLOYEE ID = 1 | | Regular Index (Not Clustered) | | Index Columns: | | | 1: EMPNO (Ascending) | #Columns = 13 | Volatile Cardinality | Single Record | Fully Qualified Unique Key | #Key Columns = 1 | | Start Key: Inclusive Value | | | | 1: '000100' | | Stop Key: Inclusive Value | | | | 1: '000100' | Data Prefetch: None | Index Prefetch: None| Isolation Level: Read Stability -- 注:隔离级别为RS | Lock Intents| | Table: Intent Share -- 注:表上加IS 锁 | | Row : Next Key Share -- 注:行上加NS 锁 |
IBM DB2 数据库用户社区}�PB3Cn!IA
通过上面两个例子,我们希望读者能够明白关于扫描方式和隔离级别的加锁情况,关键是如何分析加锁情况。
相关文章推荐
- 深入解析DB2 - 高级管理、内部体系结构与诊断案例
- 高级进阶DB2(第2版)——内部结构、高级管理与问题诊断
- 高级进阶DB2(第2版)——内部结构、高级管理与问题诊断
- 高级进阶DB2(第2版)——内部结构、高级管理与问题诊断
- 《高级进阶DB2(第2版)——内部结构、高级管理与问题诊断》之我见
- 深入解析Oracle.DBA入门进阶与诊断案例
- 基于mysql体系结构的深入解析
- 基于mysql体系结构的深入解析
- 基于mysql体系结构的深入解析
- 【《深入解析Java Web技术内幕》学习思维导图】第7章 JVM体系结构与工作方式
- 让DB2跑得更快——DB2内部解析与性能优化
- 深入理解JVM1 ---JVM的基本体系结构
- 深入 Linux PAM 体系结构
- 【性能诊断】六、并发场景的性能分析(windbg案例,大量的内部异常造成CPU飙升)
- /LGC窗口管理/MiniGUI 体系结构
- 实例解析linux内核I2C体系结构
- Backbone js 结构分析&案例解析
- Exchange2003部署与管理体系结构
- Android 属性动画 源码解析 深入了解其内部实现