enqueue等待事件的处理

有些时候,操作执行非常慢,当你从session_wait会话里查看在等待什么时,如果发现有很多的enqueue事件,那么可以看以下步骤处理

select * from gv$session_wait where event not like ‘SQL%’ AND EVENT=’enqueue’

首先查询会话都在等待什么资源

SQL> select event,sid,p1,p2,p3 from v$session_wait where event not like ‘SQL*%’ and event not like ‘rdbms%’;
EVENT                                                                   SID         P1         P2         P3
—————————————————————- ———- ———- ———- ———-
pmon timer                                                                1        300          0          0
ges remote message                                                        4         32          0          0
gcs remote message                                                        5         64          0          0
gcs remote message                                                        7         64          0          0
smon timer                                                               12        300          0          0
enqueue                                                                  23 1415053318     655363       1673
global cache s to x                                                      24          2          3 1.5094E+10

7 rows selected.

发现enqueue等待非常严重,查询enqueue等待事件的详细信息

select * from v$enqueue_stat;
INST_ID EQ TOTAL_REQ# TOTAL_WAIT#  SUCC_REQ# FAILED_REQ# CUM_WAIT_TIME
———- — ———- ———– ———- ———– ————-
         1 CF    1825643          14    1825640           3             0
         1 CU        257           0        257           0             0
         1 DL         41          19         41           0             0
         1 DM          5           4          5           0             0
         1 DP          1           0          1           0             0
         1 DR        649         649        649           0             0
         1 DS          2           0          2           0             0
         1 DT          1           0          1           0             0
         1 HW       2219          14       2219           0             0
         1 IA          1           1          1           0             0
         1 IR        524           0        524           0             0
         1 IS         11           0         11           0             0
         1 MD          5           5          5           0             0
         1 MR        365           7        365           0             0
         1 PE      50122           0      50122           0             0
         1 PI          1           0          1           0             0
         1 PR          1           0          1           0             0
         1 RO         17          14         17           0             0
         1 RT          4           1          3           1             0
         1 SQ        251           0        251           0             0
         1 SR          1           0          1           0             0
         1 SS          1           0          1           0             0
         1 ST        209         209        209           0             0
         1 SW          2           0          2           0             0
         1 TA       7633         167       7633           0             0
         1 TD       3887        3887       3887           0             0
         1 TM     178395          59     178395           0             0
         1 TS         25           9         25           0             0
         1 TT     169372           0     169372           0             0
         1 TX     167088           9     167086           0             0
         1 US        667           0        667           0             0
         1 WL          3           3          3           0             0
         1 XR          4           2          4           0             0

33 rows selected.
发现TX,TT,TM这三种锁等待时间都比较长,
TX是事务锁:
主要发生在以下情况:

1 你请求的资源备其他会话锁住
2 当两个会话同时插入一个相同的VALUE,但是两个会话都没有提交,因此,最后 一个会话等待第一个会话提交或回滚
3 没有可能用的事务槽。
TT:DDL操作表空间的一些操作
TM DML,TM是伴随着TX出现的,TM代表有DML(INSERT UPDATE,DELETE)操作引起的锁

那么现在找出现在持有 和等待enqueue事件的锁
SQL> set line 160
SQL> col machine format a10
SQL> col username format a15
SQL> select b.sid,b.serial#,b.username,machine,event,wait_time,chr(bitand(p1,-16777216)/16777215)||chr(bitand(p1, 16711680)/65535) “Enqueue Type” from v$session_wait a,v$session b
  2  where a.event not like ‘SQL*N%’ and a.event not like ‘rdbms%’ and a.sid=b.sid
  3  and b.sid>8 and a.event=’enqueue’
  4  order by username;

       SID    SERIAL# USERNAME        MACHINE    EVENT                           WAIT_TIME En
———- ———- ————— ———- —————————— ———- –
        23      44840 SYS             tsp5a      enqueue                                 0 TX

发现23号会话在等待enqueue事件。

SQL> SELECT DECODE(request,0,’Holder: ‘,’Waiter: ‘)|| sid sess, id1, id2, lmode,
  2  request, type FROM V$LOCK WHERE (id1, id2, type) IN (SELECT id1, id2, type FROM V$LOCK WHERE request>0) ORDER BY id1, request;

SESS                                                    ID1        ID2      LMODE    REQUEST TY
———————————————— ———- ———- ———- ———- –
Holder: 24                                           655363       1673          6          0 TX
Waiter: 23                                           655363       1673          0          6 TX

24号会话持有23号会话需要的锁。

继续分析,查询具体导致的SQL语句

23号
SQL> SELECT /*+ ORDERED */ sql_text FROM v$sqltext a WHERE (a.hash_value, a.address) IN (SELECT DECODE(sql_hash_value,0,prev_hash_value,sql_hash_value),DECODE(sql_hash_value,0,prev_sql_addr, sql_address) FROM v$session b WHERE b.sid = &sid) ORDER BY piece ASC;
Enter value for sid: 23
old   1: SELECT /*+ ORDERED */ sql_text FROM v$sqltext a WHERE (a.hash_value, a.address) IN (SELECT DECODE(sql_hash_value,0,prev_hash_value,sql_hash_value),DECODE(sql_hash_value,0,prev_sql_addr, sql_address) FROM v$session b WHERE b.sid = &sid) ORDER BY piece ASC
new   1: SELECT /*+ ORDERED */ sql_text FROM v$sqltext a WHERE (a.hash_value, a.address) IN (SELECT DECODE(sql_hash_value,0,prev_hash_value,sql_hash_value),DECODE(sql_hash_value,0,prev_sql_addr, sql_address) FROM v$session b WHERE b.sid = 23) ORDER BY piece ASC

SQL_TEXT
—————————————————————-

delete from test where rowid in(select rid from
(select rowid rid,row_number() over(partition by id,name order by id desc) rn from test)
where rn > 1)

查询会话24执行的SQL语句内容:
SQL> SELECT /*+ ORDERED */ sql_text FROM v$sqltext a WHERE (a.hash_value, a.address) IN (SELECT DECODE(sql_hash_value,0,prev_hash_value,sql_hash_value),DECODE(sql_hash_value,0,prev_sql_addr, sql_address) FROM v$session b WHERE b.sid = &sid) ORDER BY piece ASC;
Enter value for sid: 24
old   1: SELECT /*+ ORDERED */ sql_text FROM v$sqltext a WHERE (a.hash_value, a.address) IN (SELECT DECODE(sql_hash_value,0,prev_hash_value,sql_hash_value),DECODE(sql_hash_value,0,prev_sql_addr, sql_address) FROM v$session b WHERE b.sid = &sid) ORDER BY piece ASC
new   1: SELECT /*+ ORDERED */ sql_text FROM v$sqltext a WHERE (a.hash_value, a.address) IN (SELECT DECODE(sql_hash_value,0,prev_hash_value,sql_hash_value),DECODE(sql_hash_value,0,prev_sql_addr, sql_address) FROM v$session b WHERE b.sid = 24) ORDER BY piece ASC

SQL_TEXT
—————————————————————-
delete FROM test WHERE ROWID!=(SELECT MAX(ROWID) FROM test D W
HERE test.name=D.name AND test.id=D.id)

这条语句我之前也执行过,但由于执行时间太长,还没等结果返回我就按ctrl+c强制结束了它,语句非正常退出.

查询被锁的对象

SQL> SELECT /*+ rule */ lpad(’ ‘,decode(l.xidusn ,0,3,0))||l.oracle_username User_name,o.owner,o.object_name,o.object_type,s.sid,s.serial#
  2  FROM v$locked_object l,dba_objects o,v$session s
  3  WHERE l.object_id=o.object_id AND l.session_id=s.sid ORDER BY o.object_id,xidusn DESC;

USER_NAME
————————————————————————————————————————
OWNER
——————————
OBJECT_NAME
————————————————————————————————————————
OBJECT_TYPE               SID    SERIAL#
—————— ———- ———-
SYS
SYS
TEST
TABLE                      24      36906

   SYS
SYS
TEST
TABLE                      23      44840

根据以上信息我们可以断定是由于会话24锁住了会话23请求的数据行,造成了enqueue等待,从而使删除语句执行很慢,并且一直没有结果返回.

解决方法:

杀死相应的会话并释放锁资源:
SQL> alter system kill session ‘24,36906′;

System altered.

再次查询会话等待事件及锁信息:

SQL> select event,sid,p1,p2,p3 from v$session_wait where event not like ‘SQL*%’ and event not like ‘rdbms%’;

EVENT                                 SID         P1         P2         P3
—————————— ———- ———- ———- ———-
pmon timer                              1        300          0          0
ges remote message                      4         32          0          0
gcs remote message                      5         64          0          0
gcs remote message                      7         64          0          0
smon timer                             12        300          0          0

SQL> SELECT DECODE(request,0,’Holder: ‘,’Waiter: ‘)|| sid sess, id1, id2, lmode, request, type FROM V$LOCK WHERE (id1, id2, type) IN (SELECT id1, id2, type FROM V$LOCK WHERE request>0) ORDER BY id1, request;

no rows selected

SQL> select * from v$locked_object;

no rows selected

enqueue等待事件消失,数据库恢复了正常,删除语句很快返回了结果:
SQL> delete from test where rowid in (select rid from
  2  (select rowid rid,row_number() over(partition by id,name order by id desc) rn from test)
  3  where rn > 1);
40000 rows deleted.

但此时通过查询v$session,发现会话24依然存在,并属于僵死状态(KILLED):
SQL>  select sid,serial#,username,status from v$session;

       SID    SERIAL# USERNAME                       STATUS
———- ———- —————————— ——–
         1          1                                ACTIVE
         2          1                                ACTIVE
         3          1                                ACTIVE
         4          1                                ACTIVE
         5          1                                ACTIVE
         7          1                                ACTIVE
         9          1                                ACTIVE
        10          1                                ACTIVE
        11          1                                ACTIVE
        12          1                                ACTIVE
        13          1                                ACTIVE
        14          1                                ACTIVE
        15          1                                ACTIVE
        18          1                                ACTIVE
        20         30 RTP_USER_99            INACTIVE
        21         20 RTP_USER_99            INACTIVE
        22      10984 RTP_USER_99          INACTIVE
        23      44840 SYS                        INACTIVE
        24      36906 SYS                        KILLED
        25        330 SYS                          ACTIVE

20 rows selected.

对于僵死会话,需要杀掉对应的OS进程才能完全清除.

找出进程在OS中的位置,并kill掉:

SQL> select pro.spid from v$session ses,v$process pro where ses.sid=&sid and ses.paddr=pro.addr;
Enter value for sid: 24
old   1: select pro.spid from v$session ses,v$process pro where ses.sid=&sid and ses.paddr=pro.addr
new   1: select pro.spid from v$session ses,v$process pro where ses.sid=24 and ses.paddr=pro.addr

SPID
————
17676

oracle@tsp5a> ps -ef |grep 17676 | grep -v grep
  oracle 17676 17675  0 04:25:14 ?        0:00 oracleT001 (DESCRIPTION=(LOCAL=YES)(ADDRESS=(PROTOCOL=beq)))

oracle@tsp5a> kill -9 17676

至此,问题全部解决完毕.

总结: 同样的enqueue可能在不同的场景中发生。比如TX, 就可能在下列情况下发生: ITL分配,其他事务占用,数据行被锁定,索引块分裂等,本篇文章就是数据行被锁定引发enqueue等待的其中一个案例,所以在处理enqueue等待时,除了要清楚enqueue的类型,还要清楚是什么原因导致该enqueue的,才好对症下药。

EYGLE写了一篇非常好的文章描述如何处理ST enqueue的一个案例,具体可以参考: http://www.eygle.com/statspack/statspack13.htm

附: 关于Enqueue 等待事件的整理:
(1). ST enqueue,用于空间管理和字典管理的表空间(DMT)的区间分配,在DMT中典型的是对于uet$和fet$数据字典表的 争用。对于支持LMT的版本,应该尽量使用本地管理表空间. 或者考虑手工预分配一定数量的区(Extent),减少动态扩展时发生的严重队列竞争。
(2). HW enqueue指和段的高水位标记相关等待; 手动分配适当区可以避免这一等待。
(3). TX是最常见的enqueue等待, TX enqueue等待通常是以下三个问题之一产生的结果。
第一个问题是唯一索引中的重复索引,你需要执行提交(commit)/回滚(rollback)操作来释放enqueue。
第二个问题是对同一位图索引段的多次更新。因为单个位图段可能包含多个行地址(rowid),所以当多个用户试图更新同一段时,可能一个用户会锁定其他用户请求的记录,这时等待出现。直到获得锁定的用户提交或回滚, enqueue才释放。
第三个问题,也是最可能发生的问题是多个用户同时更新同一个块。如果没有足够的ITL槽,就会发生块级锁定。通过增大initrans和/或maxtrans以允许使用多个ITL槽(对于频繁并发进行DML操作的数据表,在建表之初就应该考虑为相应参数设置合理的数值,避免系统运行以后在线的更改,在8i之前, freelists等参数不能在线更改,设计时的考虑就尤为重要),或者增大表上的pctfree值,就可以很容易的避免这种情况。
(4). TM enqueue队列锁在进行DML操作前获得,以阻止对正在操作的数据表进行任何DDL操作(在DML操作一个数据表时,其结构不能被更改)。
 



评论暂缺

(Required)
(Required, will not be published)