阻塞是DBA經常碰到的情形,尤其是不良的應用程序設計的阻塞將導致性能嚴重下降直至數據庫崩潰。對DBA而言,有必要知道如何定位到當前系統有哪些阻塞,到底誰是阻塞者,誰是被阻塞者。本文對此給出了描述并做了相關演示。
1、阻塞及其類型
a、什么是阻塞
一個會話持有某個資源的鎖,而另一個會話在請求這個資源,就會出現阻塞(blocking)。也就是說新的會話會被掛起,直到持有鎖的會話放棄鎖定的資源。大多數情況下,在一個交互式應用中被嚴重阻塞,即可表明應用邏輯有問題,這才是阻塞的根源。
b、阻塞得類型
數據庫中有5條常見的DML語句可能會阻塞,即:INSERT、UPDATE、DELETE、MERGE 和SELECT FOR UPDATE。
2、幾種不同類型阻塞的處理辦法
a、INSERT阻塞主要是由于有一個帶主鍵的表,或者表上有惟一的約束,在兩個會話試圖用同樣的值插入一行時引發阻塞。多表通過引用完整性約束相互鏈接時,在其依賴的父表正在創建或刪除期間,對子表的插入可能會阻塞。對于該類情形建議使用序列來生成主鍵/惟一列值。
b、對于UPDATE、DELETE、MERGE 和SELECT FOR UPDATE阻塞,只要有任一session使用這些操作已經鎖定行,其余的必須處于等待狀態。直到當前鎖定行上的鎖(排他鎖)釋放。對于該類情形,建議盡可能快速提交事務,或采用批量SQL方式提交。
c、對于一個阻塞的SELECT FOR UPDATE,解決方案很簡單:只需增加NOWAIT 子句,它就不會阻塞了。
3、演示阻塞
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
--更新表,注,提示符scott@CNMMBO表明用戶為scott的session,用戶名不同,session不同。 scott@CNMMBO> update emp set sal=sal*1.1 where empno=7788; 1 row updated. scott@CNMMBO> @my_env SPID SID SERIAL # USERNAME PROGRAM ------------ ---------- ---------- --------------- ------------------------------------------------ 11205 1073 4642 robin oracle@SZDB (TNS V1-V3) --另起兩個session更新同樣的行,這兩個session都會處于等待,直到第一個session提交或回滾 leshami@CNMMBO> update scott.emp set sal=sal+100 where empno=7788; goex_admin@CNMMBO> update scott.emp set sal=sal-50 where empno=7788; --下面在第一個session 查詢阻塞情況 scott@CNMMBO> @blocker BLOCK_MSG BLOCK -------------------------------------------------- ---------- pts /5 ( '1073,4642' ) is blocking 1067,10438 1 pts /5 ( '1073,4642' ) is blocking 1065,4464 1 --上面的結果表明session 1073,4642 阻塞了后面的2個 --即session 1073,4642是阻塞者,后面2個session是被阻塞者 --Author : Leshami --Blog : http: //blog .csdn.net /leshami --下面查詢正在阻塞的session id ,SQL語句以及被阻塞的時間 scott@CNMMBO> @blocking_session_detail.sql 'SID=' ||A.SID|| 'WAITCLASS=' ||A.WAIT_CLASS|| 'TIME=' ||A.SECONDS_IN_WAIT||CHR(10)|| 'QUERY=' ||B.SQL_TEXT ------------------------------------------------------------------------------------------------------- sid=1067 Wait Class=Application Time=5995 Query=update scott.emp set sal=sal+100 where empno=7788 sid=1065 Wait Class=Application Time=225 Query=update scott.emp set sal=sal-50 where empno=7788 --下面的查詢阻塞時鎖的持有情況 scott@CNMMBO> @request_lock_type USERNAME SID TY LMODE REQUEST ID1 ID2 ------------------------------ ---------- -- ----------- ----------- ---------- ---------- SCOTT 1073 TX Exclusive None 524319 27412 LESHAMI 1067 TX None Exclusive 524319 27412 GOEX_ADMIN 1065 TX None Exclusive 524319 27412 --可以看到LESHAMI,GOEX_ADMIN 2個用戶都在請求524319 /27412 上的Exclusive鎖,而此時已經被SCOTT加了Exclusive鎖 --查詢阻塞時鎖的持有詳細信息 scott@CNMMBO> @request_lock_detail SID USERNAME OSUSER TERMINAL OBJECT_NAME TY Lock Mode Req_Mode ---------- -------------------- --------------- ------------------------- -------------------- -- ----------- -------------------- 1065 GOEX_ADMIN robin pts /1 EMP TM Row Excl 1065 GOEX_ADMIN robin pts /1 Trans-524319 TX --Waiting-- Exclusive 1067 LESHAMI robin pts /0 EMP TM Row Excl 1067 LESHAMI robin pts /0 Trans-524319 TX --Waiting-- Exclusive 1073 SCOTT robin pts /5 EMP TM Row Excl 1073 SCOTT robin pts /5 Trans-524319 TX Exclusive |
文中涉及到的相關腳本如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
robin@SZDB:~ /dba_scripts/custom/sql > more my_env.sql SELECT spid, s.sid, s.serial #, p.username, p.program FROM v $process p, v $session s WHERE p.addr = s.paddr AND s.sid = (SELECT sid FROM v $mystat WHERE rownum = 1); robin@SZDB:~ /dba_scripts/custom/sql > more blocker.sql col block_msg format a50; select c.terminal|| ' (' '' ||a.sid|| ',' ||c.serial #||''') is blocking '||b.sid||','||d.serial# block_msg, a.block from v $lock a, v $lock b, v $session c, v $session d where a.id1=b.id1 and a.id2=b.id2 and a.block>0 and a.sid <>b.sid and a.sid=c.sid and b.sid=d.SID; robin@SZDB:~ /dba_scripts/custom/sql > more blocking_session_detail.sql --To find the query for blocking session --Access Privileges: SELECT on v $session, v $sqlarea SELECT 'sid=' || a.SID || ' Wait Class=' || a.wait_class || ' Time=' || a.seconds_in_wait || CHR (10) || ' Query=' || b.sql_text FROM v $session a, v $sqlarea b WHERE a.blocking_session IS NOT NULL AND a.sql_address = b.address ORDER BY a.blocking_session / robin@SZDB:~ /dba_scripts/custom/sql > more request_lock_type.sql --This script generates a report of users waiting for locks. --Access Privileges: SELECT on v $session, v $lock SELECT sn.username, m.sid, m. type , DECODE(m.lmode, 0, 'None' , 1, 'Null' , 2, 'Row Share' , 3, 'Row Excl.' , 4, 'Share' , 5, 'S/Row Excl.' , 6, 'Exclusive' , lmode, ltrim(to_char(lmode, '990' ))) lmode, DECODE(m.request,0, 'None' , 1, 'Null' , 2, 'Row Share' , 3, 'Row Excl.' , 4, 'Share' , 5, 'S/Row Excl.' , 6, 'Exclusive' , request, ltrim(to_char(m.request, '990' ))) request, m.id1, m.id2 FROM v $session sn, v $lock m WHERE (sn.sid = m.sid AND m.request != 0) OR (sn.sid = m.sid AND m.request = 0 AND lmode != 4 AND (id1, id2) IN (SELECT s.id1, s.id2 FROM v $lock s WHERE request != 0 AND s.id1 = m.id1 AND s.id2 = m.id2) ) ORDER BY id1, id2, m.request; robin@SZDB:~ /dba_scripts/custom/sql > more request_lock_detail.sql set linesize 190 col osuser format a15 col username format a20 wrap col object_name format a20 wrap col terminal format a25 wrap col Req_Mode format a20 select B.SID, C.USERNAME, C.OSUSER, C.TERMINAL, DECODE(B.ID2, 0, A.OBJECT_NAME, 'Trans-' ||to_char(B.ID1)) OBJECT_NAME, B.TYPE, DECODE(B.LMODE,0, '--Waiting--' , 1, 'Null' , 2, 'Row Share' , 3, 'Row Excl' , 4, 'Share' , 5, 'Sha Row Exc' , 6, 'Exclusive' , 'Other' ) "Lock Mode" , DECODE(B.REQUEST,0, ' ' , 1, 'Null' , 2, 'Row Share' , 3, 'Row Excl' , 4, 'Share' , 5, 'Sha Row Exc' , 6, 'Exclusive' , 'Other' ) "Req_Mode" from DBA_OBJECTS A, V$LOCK B, V$SESSION C where A.OBJECT_ID(+) = B.ID1 and B.SID = C.SID and C.USERNAME is not null order by B.SID, B.ID2; |