如果发生死锁或锁等待等现象我们该怎么办
一旦我们在上述的2,3项发现了死锁或锁等待或锁升级,或者其他途径报告锁的问题,我们将如何解决呢?
首先我们要考虑应用系统的变化
考察最近是否有新的程序加入或者是否对现系统做了改动,比如表结构变了,程序变了,是否有了大量数据的变动。
如果有,可以重新编译与变动相关的程序(比如存储过程等),查看与变动相关的SQL(比如运行效率低)。
其次我们可以使用DB2提供工具来解决问题
如果上述方法还无法解决问题,就要采用DB2提供的工具来尝试解决问题了。
1. 查看db2diag.log文件,查找sqlcode是 -911或 -952
2006-11-08-16.29.11.398155+480 E36235682A521 LEVEL: Error PID : 12979 TID : 1 PROC : db2agent (TESTDB) 0 INSTANCE: db2inst1 NODE : 000 DB : TESTDB APPHDL : 0-288 APPID: 198.132.3.100.57177.061108070923 AUTHID : TESTDB FUNCTION: DB2 UDB, data management, sqldEscalateLocks, probe:4 MESSAGE : ADM5503E The escalation of "2" locks on table“TESTDB.TEST12" to lock intent "X" has failed.The SQLCODE is "-911". 2006-11-08-16.24.39.672914+480 E36100838A502 LEVEL: Error PID : 20866 TID : 1 PROC : db2agent (TESTDB) 0 INSTANCE: db2inst1 NODE : 000 DB : TESTDB APPHDL : 0-1394 APPID: 198.132.3.110.58426.061108075556 AUTHID : TESTDB FUNCTION: DB2 UDB, data management, sqldEscalateLocks, probe:4 MESSAGE : ADM5503E The escalation of "1" locks on table “TESTDB.TEST11"to lock intent "X" has failed.The SQLCODE is "-952".
我们可以看到红字标识出的SQLCODE is "-911" 或 SQLCODE is "-952"的信息里,也分别提供了的表名字(TESTDB.TEST12,TESTDB.TEST11),从这里我们可以看出,锁的问题与这些表相关,有了这些表的描述,对我们定位问题就前进了一步,下面我们需要定位是哪个程序或哪个SQL影响的,
2. 锁的快照信息(snapshot)
当使用list applications命令还能看到运行状态还是锁定等待的应用时,可以立即使用get snapshot for locks 命令来查找到底锁定是被哪个程序挂起的
查看应用程序的状态是否有锁定等待(Lock-wait)状态出现
执行命令 list applications for db sample show detail 得到如下结果
。。。。。。。。
应用程序句柄 = 54
应用程序标识 = *LOCAL.DB2.071129094306
序号 = 00001
应用程序名 = db2bp.exe CONNECT
授权标识 = DB2ADMIN
应用程序状态 = 锁定等待
状态更改时间 = 2007-11-29 17:50:16.124739
应用程序代码页 = 1386
挂起的锁定 = 4
总计等待时间(毫秒) = 237867
挂起锁定的代理程序标识 = 45
保留锁定的应用程序标识 = *LOCAL.DB2.071129094146
锁定名称 = 0x030006000500C0020000000052
锁定属性 = 0x00000000
发行版标志 = 0x00000001
锁定对象类型 = 行
锁定方式 = 互斥锁定(X)
请求的锁定方式 = 下一个键共享(NS)
挂起锁定的表空间名 = IBMDB2SAMPLEREL
挂起锁定的表模式 = DB2ADMIN
挂起锁定的表名 = TEST1
挂起锁定的表的数据分区标识 = 0
锁定等待启动时间戳记 = 2007-11-29 17:50:16.124744 。。。。。。。。
这里我们可以看到应用程序(54)正在等待应用程序(45)的锁的释放,被锁的的表名是 DB2ADMIN.TEST1。
3. 应用程序和动态SQL的快照信息(snapshot)
当使用list applications命令已经不能看到运行状态是锁定等待的应用时,就无法立即定位是哪个应用程序锁定引起的,这时我们使用get snapshot for applications和 get snapshot for dynamic sql 命令来查找锁定可能是被哪个SQL或哪个程序引起的,比如得到如下内容:
应用程序快照
应用程序句柄 = 45
。。。。。。。。。。。。。。
已用的 UOW 日志空间(以字节计) = 174
先前的 UOW 完成时间戳记 = 2007-11-29 17:41:46.288856
上次完成 uow 耗用时间(秒.毫秒) = 0.000000
UOW 启动时间戳记 = 2007-11-29 17:41:58.733755
UOW 停止时间戳记 =
UOW 完成状态 = 。。。。。。。。。。。。。
这里我们可以看到红字标识部分的应用程序(45),已经执行很长时间但一直没有结束,说明有可能就是这个程序造成的。
动态 SQL 快照结果
数据库名称 = SAMPLE
数据库路径 = C:\DB2\NODE0000\SQL00001\
执行数 = 17
编译数 = 1
最差预编译时间(毫秒) = 9
非常好的预编译时间(毫秒) = 9
已删除的内部行 = 0
已插入的内部行 = 0
已读取的行 = 0
已更新的内部行 = 0
已写入的行 = 0
语句排序 = 17
语句排序溢出 = 0
总计排序时间 = 3
缓冲池数据逻辑读取 = 0
缓冲池数据物理读取 = 0
缓冲池临时数据逻辑读取 = 0
缓冲池临时数据物理读取 = 0
缓冲池索引逻辑读取 = 0
缓冲池索引物理读取 = 0
缓冲池临时索引逻辑读取 = 0
缓冲池临时索引物理读取 = 0
缓冲池 xda 逻辑读取 = 0
缓冲池 xda 物理读取 = 0
缓冲池临时 xda 逻辑读取 = 0
缓冲池临时 xda 物理读取 = 0
总计执行时间(秒.毫秒) = 0.057497
总计用户 CPU 时间(秒.毫秒) = 0.000000
总计系统 CPU 时间(秒.毫秒) = 0.000000
语句文本 = SELECT TABLE_CAT, TABLE_SCHEM, TABLE_NAME, CASE TABLE_TYPE WHEN 'NICKNAME' THEN 'SYNONYM' ELSE TABLE_TYPE END as TABLE_TYPE, REMARKS FROM DB2ADMIN.TEST11 WHERE TABLE_SCHEM = 'DB2ADMIN' AND TABLE_TYPE IN ('TABLE') ORDER BY 4,1,2,3
这里我们假设在db2diag.log里报出的是表DB2ADMIN.TEST11发生锁问题,那么就找根此表相关的SQL语句,比如红字标识出的SQL语句,通过这些SQL来定位最终是由于哪个应用程序或SQL造成的锁定。
4. DB2PD程序快速定位锁定SQL语句
当使用list applications命令还能看到运行状态还是锁定等待的应用时,可以立即使用db2pd 命令来查找到底锁定是被哪个程序哪个SQL语句挂起的。从版本 8.2.2开始DB2也可以使用 db2pd 命令来获取死锁信息。
我们用如下命令生成锁定信息,生成的信息存入locklog 文件内
db2pd -db sample -locks -transactions –applications -dynamic -file locklog
我们会在文件中看到锁
Locks: Address TranHdl Lockname Type Mode Sts Owner Dur HoldCount Att ReleaseFlg 0x7F890990 2 03000500040080020000000052 Row ..X G 2 1 0 0x08 0x40000000 0x7F890D80 7 03000500040080020000000052 Row .NS W 0 1 0 0x00 0x00000001
从中会发现 TranHdl 7 正在等待 TranHdl 2 挂起的锁定,他们共同要求锁定名称相同(03000500040080020000000052)。
下面我们找跟TranHdl 相关的AppHandl
Transactions: Address AppHandl [nod-index] TranHdl Locks State Tflag Tflag2 Firstlsn Lastlsn LogSpace SpaceReserved TID AxRegCnt GXID 0x7FC21A80 20 [000-00020] 2 4 WRITE 0x00000000 0x00000000 0x000003A9A957 0x000003ACD1FD 154 282 0x000000001345 1 0 0x7FC25B80 25 [000-00025] 7 4 READ 0x00000000 0x00000000 0x000000000000 0x000000000000 0 0 0x000000001672 1 0
从中会发现TranHdl 2,7对应的AppHandl 是 20,25
然后我们再查找AppHandl20,25对应的动态 SQL 语句的当前和最后一个锚点标识和语句唯一标识(C-AnchID C-StmtUID L-AnchID L-StmtUID)。
这允许直接从应用程序映射至动态 SQL 语句。
Applications: Address AppHandl [nod-index] NumAgents CoorEDUID Status C-AnchID C-StmtUID L-AnchID L-StmtUID Appid WorkloadID WorkloadOccID 0x7AEFBF30 25 [000-00025] 1 396 Lock-wait 116 1 116 1 *LOCAL.DB2.071127074823 1 2 0x7AED8080 20 [000-00020] 1 420 UOW-Waiting 0 0 13 1 *LOCAL.DB2.071127074818 1 1
其中AppHandl 20对应的C-AnchID C-StmtUID L-AnchID L-StmtUID 是 0, 0, 13 , 1
AppHandl 25对应的C-AnchID C-StmtUID L-AnchID L-StmtUID 是 116, 1, 116 , 1
这里我只需要关注AppHandl 20对应的 ID
最后我们再查找C-AnchID C-StmtUID L-AnchID L-StmtUID 对应的sql语句
Dynamic SQL Statements: Address AnchID StmtUID NumEnv NumVar NumRef NumExe Text0x7EA75AE0 13 1 1 1 1 1 insert into test1 values(2,'test1')
完成上面过程我们发现了是insert into test1 values(1,'test1') sql导致了死锁或锁超时,因为test1表就是锁定超时报告文件中检测出来的锁定表。
但需要注意的是,当前发现的sql语句 insert into test1 values(2,'test1') 是在其应用程序没有后续的其他sql语句发生时找到的结果,在实际的程序中在insert into test1 values(2,'test1') 语句后面很有可能会有其他sql语句执行,而在db2pd跟踪中Applications所能反应的只能是当前执行的sql语句和上一个执行的sql语句,这两个sql语句,不一定涉及到锁定超时报告文件当中的test1表,所以需要你在db2pd的检测文件仔细查找跟test1表相关的sql语句。