想关注我吗?请点击图片上方蓝字小麦苗关注即可,关注后您将可以每日获得最实用的数据库技术。请将小麦苗公众号置顶,小麦苗不喜欢被压着,~O(∩_∩)O~
在烦恼的时候,让心歇歇脚。
给自己一个空间,让自己的心灵有一份纯净的湖泊。
今天给大家分享的是【故障处理】队列等待之TX - allocate ITL entry引起的死锁处理(下)。
等待事件历史文章~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
● 【故障处理】序列cache值过小导致CPU利用率过高
● 【故障处理】队列等待之enq: TX - row lock contention
● Oracle一次缩小表空间的处理过程
● 【故障处理】队列等待之TX - allocate ITL entry案例
● 【故障处理】队列等待之enq: US - contention案例
● 【故障处理】队列等待之TX - allocate ITL entry引起的死锁处理(上)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
●【等待事件】等待事件概述(1)--等待事件的源起和分类
●【等待事件】User I/O类 等待事件(2.1)--db file sequential read(数据文件顺序读)
●【等待事件】User I/O类 等待事件(2.2)--db file scattered read(数据文件离散读)
●【等待事件】User I/O类 等待事件(2.3)--db file parallel read
● 【等待事件】User I/O类 等待事件(2.4)--db file single write
● 【等待事件】User I/O类 等待事件(2.5)--direct path read(直接路径读、DPR)
● 【等待事件】User I/O类 等待事件(2.6)--direct path write(直接路径写、DRW)
● 【等待事件】User I/O类 等待事件(2.7)--direct path read/write temp
● 【等待事件】User I/O类 等待事件(2.8)--read by other session
● 【等待事件】User I/O类 等待事件(2.9)--local write wait
● 【等待事件】User I/O类 等待事件(2.10)--所有User I/O类 等待事件总结
● 【等待事件】System I/O类 等待事件(3.1)--db file parallel write
● 【等待事件】System I/O类 等待事件(3.2)--control file parallel write
● 【等待事件】System I/O类 等待事件(3.3)--control file sequential read
● 【等待事件】System I/O类 等待事件(3.4)--control file single write
【故障处理】队列等待之TX - allocate ITL entry引起的死锁处理(下)--ITL死锁模拟
有人的地方就有江湖,有资源阻塞的地方就可能有死锁。所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。其最常见的死锁的类型分为:行级锁(row-level locks)和块级锁(block-level locks),这里的行级锁其实就是指的ITL死锁。有关死锁的问题,有许多需要介绍的,这篇blog主要是故障处理,所以这里我们模拟一个ITL死锁产生的过程即可,后边我会系统的发一次有关死锁的内容,还有ITL的内容,希望大家持续关注小麦苗的微信公众号(xiaomaimiaolhr)。
实验部分:
实验的设计过程来源于网络!
我们首先创建一张表T_ITL_LHR,这里指定PCTFREE为0,INITRANS为1,就是为了观察到ITL的真实等待情况,然后我们给这些块内插入数据,把块填满,让它不能有空间分配。
SYS@lhrdb21> SELECT * FROM V$VERSION;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
CORE 11.2.0.4.0 Production
TNS for IBM/AIX RISC System/6000: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production
SYS@lhrdb21> SHOW PARAMETER CLUSTER
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
cluster_database boolean TRUE
cluster_database_instances integer 2
cluster_interconnects string
SYS@lhrdb21> CREATE TABLE T_ITL_LHR(A INT) PCTFREE 0 INITRANS 1;
Table created.
SYS@lhrdb21> BEGIN
2 FOR I IN 1 .. 2000 LOOP
3 INSERT INTO T_ITL_LHR VALUES (I);
4 END LOOP;
5 END;
6 /
PL/SQL procedure successfully completed.
SYS@lhrdb21> COMMIT;
Commit complete.
我们检查数据填充的情况:
SYS@lhrdb21> SELECT F, B, COUNT(*)
2 FROM (SELECT DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) F,
3 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) B
4 FROM T_ITL_LHR)
5 GROUP BY F, B
6 ORDER BY F,B;
F B COUNT(*)
---------- ---------- ----------
1 94953 734
1 94954 734
1 94955 532
可以发现,这2000条数据分布在3个块内部,其中有2个块(94953和94954)填满了,一个块(94955)是半满的。因为有2个ITL槽位,我们需要拿2个满的数据块,4个进程来模拟ITL死锁:
实验步骤
会话
SID
要更新的块号
要更新的行号
是否有阻塞
步骤一
1
19
94953
94953
1
N
2
79
2
N
3
78
94954
94954
1
N
4
139
2
N
会话1:
SYS@lhrdb21> SELECT USERENV('SID') FROM DUAL;
USERENV('SID')
--------------
19
SYS@lhrdb21> UPDATE T_ITL_LHR SET A=A
2 WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94953
3 AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=1;
1 row updated.
会话2:
SYS@lhrdb21> SELECT USERENV('SID') FROM DUAL;
USERENV('SID')
--------------
79
SYS@lhrdb21> UPDATE T_ITL_LHR SET A=A
2 WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94953
3 AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=2;
1 row updated.
会话3:
SYS@lhrdb21> SELECT USERENV('SID') FROM DUAL;
USERENV('SID')
--------------
78
SYS@lhrdb21> UPDATE T_ITL_LHR SET A=A
2 WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94954
3 AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=1;
1 row updated.
会话4:
SYS@lhrdb21> SELECT USERENV('SID') FROM DUAL;
USERENV('SID')
--------------
139
SYS@lhrdb21> UPDATE T_ITL_LHR SET A=A
2 WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94954
3 AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=2;
1 row updated.
这个时候系统不存在阻塞,
SELECT NVL(A.SQL_ID, A.PREV_SQL_ID) SQL_ID,
A.BLOCKING_SESSION,
A.SID,
A.SERIAL#,
A.LOGON_TIME,
A.EVENT
FROM GV$SESSION A
WHERE A.SID IN (19, 79,78,139)
ORDER BY A.LOGON_TIME;
以上4个进程把2个不同块的4个ITL槽位给消耗光了,现在的情况,就是让他们互相锁住,达成死锁条件,回到会话1,更新块94954,注意,以上4个操作,包括以下的操作,更新的根本不是同一行数据,主要是为了防止出现的是行锁等待。
实验步骤
会话
SID
要更新的块号
要更新的行号
是否有阻塞
步骤一
1
19
94953
94953
1
N
2
79
2
N
3
78
94954
94954
1
N
4
139
2
N
步骤二
1
19
94954
3
Y
3
78
94953
3
Y
会话1:
UPDATE T_ITL_LHR SET A=A
WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94954
AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=3;
会话1出现了等待。
会话3:
UPDATE T_ITL_LHR SET A=A
WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94953
AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=3;
会话3发现出现了等待。
我们查询阻塞的具体情况:
SELECT NVL(A.SQL_ID, A.PREV_SQL_ID) SQL_ID,
A.BLOCKING_SESSION,
A.SID,
A.SERIAL#,
A.LOGON_TIME,
A.EVENT
FROM GV$SESSION A
WHERE A.SID IN (19, 79,78,139)
ORDER BY A.LOGON_TIME;
可以看到,会话1被会话4阻塞了,会话3被会话2阻塞了。
注意,如果是9i,在这里就报死锁了,但是在10g里面,这个时候,死锁是不会发生的,因为这里的会话1还可以等待会话4释放资源,会话3还可以等待会话2释放资源,只要会话2与会话4释放了资源,整个环境又活了,那么我们需要把这两个进程也塞住。
实验步骤
会话
SID
要更新的块号
要更新的行号
是否有阻塞
步骤一
1
19
94953
94953
1
N
2
79
2
N
3
78
94954
94954
1
N
4
139
2
N
步骤二
1
19
94954
3
Y
3
78
94953
3
Y
步骤三
2
79
94954
4
Y
4
139
94953
4
Y
会话2,注意,我们也不是更新的同一行数据:
UPDATE T_ITL_LHR SET A=A
WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94954
AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=4;
会话2出现了等待,具体阻塞情况:
我做了几次实验,会话2执行完SQL后,会话3到这里就报出了死锁,但有的时候并没有产生死锁,应该跟系统的阻塞顺序有关,若没有产生死锁,我们可以继续会话4的操作。
SYS@lhrdb21> UPDATE T_ITL_LHR SET A=A
2 WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94953
3 AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=3;
UPDATE T_ITL_LHR SET A=A
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
会话4,注意,我们也不是更新的同一行数据:
UPDATE T_ITL_LHR SET A=A
WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94953
AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=4;
会话4发现出现了等待。
虽然,以上的每个更新语句,更新的都不是同一个数据行,但是,的确,所有的进程都被阻塞住了,那么,死锁的条件也达到了,等待一会(这个时间有个隐含参数来控制的:_lm_dd_interval),我们可以看到,会话2出现提示,死锁:
SYS@lhrdb21> UPDATE T_ITL_LHR SET A=A
2 WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94954
3 AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=4;
UPDATE T_ITL_LHR SET A=A
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
报出死锁之后的阻塞情况:
我们可以在会话2上继续执行步骤三中的SQL,依然会产生死锁。生成死锁后,在告警日志中有下边的语句:
Fri Sep 09 17:56:55 2016
Global Enqueue Services Deadlock detected. More info in file
/oracle/app/oracle/diag��ͷ,�ɾ�/rdbms/lhrdb2/lhrdb21/trace/lhrdb21_lmd0_17039368.trc.
其中的内容有非常经典的一段Global Wait-For-Graph(WFG):
*** 2016-09-09 17:48:22.216
Submitting asynchronized dump request [1c]. summary=[ges process stack dump (kjdglblkrdm1)].
Global blockers dump end:-----------------------------------
Global Wait-For-Graph(WFG) at ddTS[0.395] :
BLOCKED 0x700010063d59b90 3 wq 2 cvtops x1001 TX 0x7000b.0xa67(ext 0x2,0x0)[1002-0029-00008387] inst 1
BLOCKER 0x700010063c6d268 3 wq 1 cvtops x28 TX 0x7000b.0xa67(ext 0x2,0x0)[1002-002D-00003742] inst 1
BLOCKED 0x700010063d5adc8 3 wq 2 cvtops x1 TX 0x30021.0x848(ext 0x2,0x0)[1002-002D-00003742] inst 1
BLOCKER 0x700010063d5a4b8 3 wq 1 cvtops x28 TX 0x30021.0x848(ext 0x2,0x0)[1002-0029-00008387] inst 1
至于每个参数到底是什么意思,目前还没有去研究,等待大神可以无偿解释一下。至于如何解决ITL产生的死锁,无非就是增大表和索引的initrans和PCT_FREE的值,可以参考本BLOG中的ITL死锁问题解决。
该实验过程可能有点复杂,小麦苗画了个图来说明整个实验过程:
【推荐】 update修改为merge(max+decode)
http://blog.itpub.net/26736162/viewspace-1244055/
【推荐】 采用merge语句的非关联形式再次显神能
http://blog.itpub.net/26736162/viewspace-1222423/
【推荐】 采用MERGE语句的非关联形式提升性能 ---后传
http://blog.itpub.net/26736162/viewspace-1222417/
【推荐】 采用MERGE 语句的非关联形式提升性能
http://blog.itpub.net/26736162/viewspace-1218671/
自相矛盾:一个进程可以自成死锁么
http://blog.itpub.net/26736162/viewspace-2080712/
oracle死锁类型和原因分析
http://blog.itpub.net/26736162/viewspace-1744719/
【DEADLOCK】Oracle“死锁”模拟
http://blog.itpub.net/26736162/viewspace-1744705/
[转]:深入研究ITL阻塞与ITL死锁
http://blog.itpub.net/26736162/viewspace-2124539/
● 本文作者:小麦苗,只专注于数据库的技术,更注重技术的运用
● 本文在itpub(http://blog.itpub.net/26736162)、博客园(http://www.cnblogs.com/lhrbest)和个人微信公众号(xiaomaimiaolhr)上有同步更新,推荐pdf文件阅读
● QQ群:230161599 微信群:私聊
● 本文itpub地址:http://blog.itpub.net/26736162/viewspace-2124771/ 博客园地址:http://www.cnblogs.com/lhrbest/p/5859095.html
● 本文pdf版:http://yunpan.cn/cdEQedhCs2kFz (提取码:ed9b)
● 小麦苗云盘地址:http://blog.itpub.net/26736162/viewspace-1624453/
● 联系我请加QQ好友(642808185),注明添加缘由
● 于 2016-09-05 09:00~ 2016-09-10 19:00 在中行完成
● 文章内容来源于小麦苗整理的笔记,若有侵权或不当之处还请谅解!
●【版权所有,文章允许转载,但须以链接方式注明源地址,否则追究法律责任】
长按识别二维码或微信客户端扫描下边的二维码来关注小麦苗的微信公众号:xiaomaimiaolhr,学习最实用的数据库技术。
本文分享自微信公众号 - DB宝(lhrdba)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。