使用游标的要小心了,游标在OPEN时会加锁:
实例:
表t1,数据如下:
ID MEMO
- -- ----
1 a1
2 b1
3 hhh
4 d1
5 e1
DB2默认锁定超时-1(不限制),方便测试修改为20(秒)
db2 update db cfg for sample using LOCKTIMEOUT 20
在第一个DB2 CLP窗口执行更新
连接到数据库:db2 connect to sample
执行更新:db2 +c "update t1 set memo = 'hhh1' where id =3"
查看锁:
C:\>db2pd -db sample -locks
Database Partition 0 -- Database SAMPLE -- Active -- Up 0 days 00:40:56
Locks:
Address TranHdl Lockname Type Mode Sts Owner
Dur HoldCount Att ReleaseFlg rrIID
0x7FC70480 2 53514C43324832307F4760B841 Internal P ..S G 2
1 0 0x00000000 0x40000000 0
0x7FC70700 2 02001300060000000000000052 Row ..X G 2
1 0 0x00000000 0x40000000 0
0x7FC70400 2 02001300000000000000000054 Table .IX G 2
1 0 0x00002000 0x40000000 0
表加意向互斥锁,更新的行(id = 3)加互斥锁
在第二个DB2 CLP窗口定义游标并打开:
连接到数据库:db2 connect to sample
定义游标:db2 "declare c1 cursor for select * from t1 for read only" (v8)
db2 "declare c1 cursor for select * from t1 with rs"(v9.7)
注:v8的游标定义,在版本8上open时锁等待,在版本9上不会出现锁等待,本实例主要说v9.7
db2 "declare c1 cursor for select * from t1 with rs"
db2 +c "open c1"
光标停止不动,在另外的DB2 CLP窗口查看锁及锁等待,信息如下:
锁:
C:\>db2pd -db sample -locks
Database Partition 0 -- Database SAMPLE -- Active -- Up 0 days 00:43:34
Locks:
Address TranHdl Lockname Type Mode Sts Owner
Dur HoldCount Att ReleaseFlg rrIID
0x7FC70480 2 53514C43324832307F4760B841 Internal P ..S G 2
1 0 0x00000000 0x40000000 0
0x7FC72280 9 53514C43324832307F4760B841 Internal P ..S G 9
1 0 0x00000000 0x40000000 0
0x7FC70700 2 02001300060000000000000052 Row ..X G 2
1 0 0x00000000 0x40000000 0
0x7FC7C580 9 02001300060000000000000052 Row .NS W 2
1 0 0x00000000 0x00000001 0
0x7FC7C700 9 01000000010000000100001356 Internal V ..S G 9
1 0 0x00000000 0x40000000 0
0x7FC7C300 9 02001300040000000000000052 Row .NS G 9
1 0 0x00000000 0x00000001 0
0x7FC7C780 9 02001300050000000000000052 Row .NS G 9
1 0 0x00000000 0x00000001 0
0x7FC70400 2 02001300000000000000000054 Table .IX G 2
1 0 0x00002000 0x40000000 0
0x7FC71F80 9 02001300000000000000000054 Table .IS G 9
1 0 0x00003000 0x00000001 0
锁等待:
C:\>db2pd -db sample -locks show wait
Database Partition 0 -- Database SAMPLE -- Active -- Up 0 days 00:02:33
Locks:
Address TranHdl Lockname Type Mode Sts Owner
Dur HoldCount Att ReleaseFlg rrIID
0x7FC7C500 9 02001300060000000000000052 Row .NS W 0
1 0 0x00000000 0x00000001 0 TbspaceID 2 TableID 19 P
artitionID 0 Page 0 Slot 6
0x7FC70480 2 02001300060000000000000052 Row ..X G 2
1 0 0x00000000 0x40000000 0 TbspaceID 2 TableID 19 P
artitionID 0 Page 0 Slot 6
等待20秒后,提示:
C:\>db2 +c "open c1"
DB20000I SQL 命令成功完成。
再查看锁:
C:\>db2pd -db sample -locks
Database Partition 0 -- Database SAMPLE -- Active -- Up 0 days 00:44:22
Locks:
Address TranHdl Lockname Type Mode Sts Owner
Dur HoldCount Att ReleaseFlg rrIID
0x7FC70480 2 53514C43324832307F4760B841 Internal P ..S G 2
1 0 0x00000000 0x40000000 0
0x7FC70700 2 02001300060000000000000052 Row ..X G 2
1 0 0x00000000 0x40000000 0
0x7FC70400 2 02001300000000000000000054 Table .IX G 2
1 0 0x00002000 0x40000000 0
锁又恢复到执行更新语句后的状况,看来是游标在open的时候锁超时,回滚了!
奇怪的是但居然提示成功了?那如果我超时设置为600秒,那岂不是要等10分钟才告诉成功!,真成功了吗?继续看
C:\>db2 +c "fetch c1"
ID MEMO
----------- ----------
1 a1
1 条记录已选择。
C:\>db2 +c "fetch c1"
ID MEMO
----------- ----------
2 b1
1 条记录已选择。
C:\>db2 +c "fetch c1"
ID MEMO
----------- ----------
SQL0911N 因为死锁或超时,所以当前事务已回滚。原因码为 "68"。 SQLSTATE=40001
看来是成功了,只是DB2把超时放在OPEN上,提示放在了FETCH上。那就引发下一个疑问:既然在OPEN的时候已经回滚了,那怎么还能再FETCH出来前两条记录?
现在又两个问题需要验证:
1、OPEN的时候是不是真的回滚了?
因为使用RS隔离级别,那么如果游标在OPEN之后,继续拥有表上的意向共享锁,和查询行上的下一键共享锁,
那么上面实例中在OPOEN之后“锁又恢复到执行更新语句后的状况”就表明发生了回滚(后面FETCH第三条记录时的提示,表明不可能是提交)。
验证疑问一:
在DB2 CLP窗口定义游标并打开:
C:\>db2 "declare c1 cursor for select * from t1 with rs"
DB20000I SQL 命令成功完成。
C:\>db2 +c "open c1"
DB20000I SQL 命令成功完成。
查看锁:
C:\>db2pd -db sample -locks
Database Partition 0 -- Database SAMPLE -- Active -- Up 0 days 00:36:02
Locks:
Address TranHdl Lockname Type Mode Sts Owner
Dur HoldCount Att ReleaseFlg rrIID
0x7FC7C680 9 02001300080000000000000052 Row .NS G 9
1 0 0x00000000 0x00000001 0
0x7FC72900 9 53514C43324832307F4760B841 Internal P ..S G 9
1 0 0x00000000 0x40000000 0
0x7FC7C380 9 02001300060000000000000052 Row .NS G 9
1 0 0x00000000 0x00000001 0
0x7FC7C480 9 01000000010000000100001356 Internal V ..S G 9
1 0 0x00000000 0x40000000 0
0x7FC7C780 9 02001300040000000000000052 Row .NS G 9
1 0 0x00000000 0x00000001 0
0x7FC7C600 9 02001300070000000000000052 Row .NS G 9
1 0 0x00000000 0x00000001 0
0x7FC72880 9 02001300050000000000000052 Row .NS G 9
1 0 0x00000000 0x00000001 0
0x7FC7C280 9 02001300000000000000000054 Table .IS G 9
1 0 0x00002000 0x00000001 0
在游标OPEN之后,锁依然存在,表明确实发生回滚。
2、如果真的回滚了,为什么还能继续FETCH?
推测存在两种情况:其一、FETCH时数据直接来自OPEN的结果集,其二、FETCH时数据来自于重新查询。
通过验证疑问一,已确认在游标OPEN时已发生回滚,那么第一种情况FETCH时的数据来自OPEN的结果集不成立。
那么如何验证FETCH时的数据来自重新查询呢,假设:如果在FETCH一行时,同时对该行加锁,那么肯定是重新查询了。
验证疑问二:
继续以前的测试,在第一次FETCH的时候,查看下锁情况:
C:\>db2 +c "fetch c1"
ID MEMO
----------- ----------
1 a1
1 条记录已选择。
查看锁:
C:\>db2pd -db sample -locks
Database Partition 0 -- Database SAMPLE -- Active -- Up 0 days 01:25:43
Locks:
Address TranHdl Lockname Type Mode Sts Owner
Dur HoldCount Att ReleaseFlg rrIID
0x7FC70480 2 53514C43324832307F4760B841 Internal P ..S G 2
1 0 0x00000000 0x40000000 0
0x7FC70700 2 02001300060000000000000052 Row ..X G 2
1 0 0x00000000 0x40000000 0
0x7FC70400 2 02001300000000000000000054 Table .IX G 2
1 0 0x00002000 0x40000000 0
锁没有变化,是没有查询还是持有后又释放了?下步狠点,FETCH前,把二条记录先锁上:
执行:db2 +c "update db2admin.t1 set memo = 'b2' where id = 2"
查看锁:
C:\>db2pd -db sample -locks
Database Partition 0 -- Database SAMPLE -- Active -- Up 0 days 01:29:21
Locks:
Address TranHdl Lockname Type Mode Sts Owner
Dur HoldCount Att ReleaseFlg rrIID
0x7FC70480 2 53514C43324832307F4760B841 Internal P ..S G 2
1 0 0x00000000 0x40000000 0
0x7FC70700 2 02001300060000000000000052 Row ..X G 2
1 0 0x00000000 0x40000000 0
0x7FC70380 2 02001300050000000000000052 Row ..X G 2
1 0 0x00000000 0x40000000 0
0x7FC70400 2 02001300000000000000000054 Table .IX G 2
2 0 0x00002000 0x40000000 0
多了一个行锁。锁定的正好是下一个FETCH的行。
继续执行FETCH:
C:\>db2 +c "fetch c1"
ID MEMO
----------- ----------
2 b1
1 条记录已选择。
神了,居然出来了,MEMO原来是b1,虽然在其他窗口被更新为b2,但fetch出来任然是b1,这就是当前已落实更新?
这时候对于自己推测的两种情况有点怀疑了。再想会不会是这样:
1、OPEN隔离级别为RS的游标时,由于其他事物持有X锁,发生超时回滚;
2、接着FETCH时使用CS隔离级别查询所需的行,由于DB2v9.7启用当前已落实的更新,所以可以返回记录
(OPEN完成后,在其他事物中对要FETCH的行加锁,没有锁等待,所以推测是CS隔离级别的查询)。
3、当FETCH到OPEN时发生超时的行时,提示出错(为什么现在才提示?)。
谁出来解释下,到底是杂了?
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/7690668/viewspace-614929/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/7690668/viewspace-614929/