DB2V9.7中使用游标的注意了

使用游标的要小心了,游标在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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值