标签:mutex latch library cache qhd2004 reference count
看吕大的书,其中对mutex的介绍让人心动,因此我做一次搬运工。
mutex与latch区别:
mutex | latch |
没有等待队列,没有持有队列,抢占机制 | 使用队列 |
spin255次,在spin 期间无法获得,转入睡眠,自己醒来 | spin 2000次,在spin 期间无法获得,转入睡眠,等待唤醒 |
使用引用计数器 (reference count),在64 位中占用 8字节,其实前4为 SID,后4 为引用计数器,如果引用计数器为 0,说明mutex 为独占模式 | 在latch池中,每一个 latch一块内存,所有 latch组成 latch池 |
植入对象内部 | 与对象分离 |
v$mutex_sleep_history.mutex_value查看mutex value
select sid, event, p1raw, p2raw, state, wait_class
from v$session
where wait_class <> ‘Idle‘
order by event;
如果event为library cache:mutex X,那么p2raw就是阻塞进程的mutex的当前mutex_value,其中前端是SID,可以找到阻塞的会话。
mutex类型:
常见类型有:cursor parent、library cache、hash table、cursor pin、cursor stats
在v$mutex_sleep和v$mutex_sleep_history中的mutex_type列对应mutex类型
不同mutex type对应不同的等待事件,通常如下:
hash table、cursor parent、cursor stats类型的mutex对应 cursor:mutex
library cache类型的mutex对应 library cache:mutex
cursor pin类型的mutex对应 cursor:pin
硬解析所有mutex都会出现,软软解析只会cursor pin类型的mutex
library cache:mutex X
11g前使用library cache latch保护hash bucket和其后的链表,11g开始不再用,而是换成library cache:mutex
无论软解析还是硬解析,进程都要独占方式获得library cache:mutex ,然后才能访问hash链,如果遇到竞争,产生等待事件,那这里的等待事件是library cache:mutex X
在v$mutex_sleep或是v$mutex_sleep_history的location列可以看到mutex miss是kglhdgn1 62 或是在awr报告中大mutex sleep summary部分看到location为kglhdgn1 62竞争多,说明在搜索hash bucket后的链表时遇到竞争
搜索hash链的目的是找到父游标句柄,找到父游标句柄后,要再次申请以独占方式持有library cache:mutex,成功后才能访问父游标句柄内的信息。如果在申请时遇到竞争,产生等待事件,这里的等待事件也是library cache:mutex X 。此处理的 mutex miss通常是kgldhgn2 106 。在此处mutex保护下,进程获得父游标句柄上的library cache lock,成功后,mutex被释放。也就是说,此处的mutex是代替以前版本的library cache lock latch(11g前是使用library cache lock latch,11g后换成library cache:mutex)
在子游标句柄,还有其他对象的句柄上都会有同样的mutex和library cache lock
hash table:mutex
找到父游标句柄,也加上了library cache lock,接下来,从父游标句柄中取出父游标堆0的地址,并访问父游标堆0。父游标堆0中包含了子游标句柄地址,这些子游标句柄地址构成了了游标列表,如下图:
访问父游标堆0的目的是在子游标列表中查找子游标句柄,这里当然要持有mutex。此处会持有两种类型的mutex:一种是cursor parent,另一种是hash table
oracle先持有cursor parent类型的mutex,访问父游标堆0中的其他信息,然后释放。再持有hash table类型的mutex,搜索了子游标列表,查找子游标句柄,找到后释放。这两种类型的mutex对应等待事件都是cursor:mutex S
搜索子游标列表,从mutex类型上推测应为hash table:mutex。它只保护子游标列表,如果此类型mutex遇到竞争,说明,某sql语句版本太多。其实hash table:mutex承担了之前版本中的library cache pin和library cache pin latch的作用。
cursor pin
整个解析过程会先访问hash链表,然后访问父游标句柄,父游标堆0,再访问子游标句柄,最后是子游标堆0和包含执行计划的堆6
对子游标堆0和堆6所加的mutex的类型是cursor pin,对应的等待事件是cursor:pin S或cursor:pin S wait on X
堆6的cursor pin类型的mutex比较特殊,这个mutex并不在子游标堆6中,它在父游标堆0中,因为子游标堆6的DS在父游标堆0中。
硬解析时需要独占、共享模式多次持有父游标堆0与子游标堆6上的mutex
软解析时通常不需要访问子游标堆0,可以从父游标堆0中找到子游标堆6,直接访问子游标堆6中的执行计划
软软解析时子游标堆6的DS地址保存在PGA中
通过mutex判断解析问题
硬解析:
需要所有mutex,申请多次shared pool latch,软解析只需要少量的shared pool latch,软软解析不需要shared pool latch
因此:如果shared pool latch竞争激烈,一定是硬解析过多,还有一种情况,版本过多。
同一父游标下多个子游标同时硬解析,会造成library cache lock竞争和保护子游标列表的mutex也会竞争,为hash table型的mutex
因此:如果library cache lock和hash table型的mutex同时出现,硬解析过多
如果只有hash table型的mutex,说明版本过多
在awr中mutex sleep summary可以查看,并做相应判断。
软解析:
搜索hash bucket后链表时,需要加library cache型的mutex
访问父游标句柄时,也需要加library cache型的mutex
访问父游标堆0、搜索子游标句柄列表时,需要hash table型的mutex
访问子游标句柄时,还是用需要加library cache型的mutex
访问子游标堆6,读取执行计划时,需要cursor pin型的mutex
补充:动态游标,在解析后,如果使用绑定变量,将变量值传入共享池,这一步要独占library cache型mutex
静态游标,绑定变量不传入共享池,也就不需要library cache型mutex
软软解析:
在PGA cache cursor列表中搜索子游标堆6DS地址,不需要任何mutex、latch
根据得到的子游标堆6的DS地址,访问共享池中子游标堆6,读取执行计划,需要共享模式的cursor pin型mutex,此处可能遇到的等待事件为cursor:pin S
如果使用绑定变量,将绑定变量值传入共享池,这一步要独占library cache型mutex。同样,如果是静态游标,绑定变量不传入共享池,也就不需要library cache型mutex
执行、抓取结束后,需要释放共享cursor pin型mutex,此处也可能遇到等待事件cursor:pin S
总结:
如果只有cursor pin型和library cache型的mutex竞争,那是软软解析
如果还有其他mutex等待,那是软解析
如果还有shared pool latch等待,那是硬解析
解决解析阶段竞争:
硬解析:一般有如下3种原因
没有使用绑定变量
父游标版本过高
共享池小
第一种,没有绑定变量,查询v$sqlarea.sql_text,看相似语句是否过多。修改应用是上上策,cursor_sharing参数不是好方法
第二种,父游标版本过高,查询v$sql_shared_cursor。11g的adaptive cursor sharing(ACS),可能会造成版本过多问题,oltp环境,可以考虑关闭此特性。
第三种,共享池小,可能使用瞬时LRU与周期LRU比值判断,如小,则加大
软解析:两种解决方法
调整应用,减少软解析
调整session_cached_cursors,化软解析为软软解析
总结:
硬解析过多,可以使用绑定变量、加大共享池,化硬解析为软解析
软解析过多,可以调大session_cached_cursors,化软解析为软软解析
软软解析一个小测试:
建立两个连接
SQL> select sid from v$mystat where rownum=1;
SID
----------
226
SQL> select sid from v$mystat where rownum=1;
SID
----------
242
在每个会话中执行如下:
declare
mcur number;
mstat number;
v_name varchar2(40);
begin
mcur:=dbms_sql.open_cursor;
for i in 1 .. 10000000 loop
dbms_sql.parse(mcur,‘select * from moe where id=1‘,dbms_sql.native);
mstat:=dbms_sql.execute(mcur);
end loop;
dbms_sql.close_cursor(mcur);
end;
/
查看上面两个会话的等待事件:
select sid,event,p1raw,p2raw,p3raw from v$session where sid in (‘226‘,‘242‘);
SID EVENT P1RAW P2RAW P3RAW
---------- --------------- ---------------- ---------------- ----------------
226 cursor: pin S 00000000D756DDA2 0000000000000001 0000000400000000
242 cursor: pin S 00000000D756DDA2 0000000000000002 0000000900000000
解决如下:
在226中执行如下:
declare
mcur number;
mstat number;
v_name varchar2(40);
begin
mcur:=dbms_sql.open_cursor;
for i in 1 .. 10000000 loop
dbms_sql.parse(mcur,‘select /* sess_226 */ * from moe where id=1‘,dbms_sql.native);
mstat:=dbms_sql.execute(mcur);
end loop;
dbms_sql.close_cursor(mcur);
end;
/
在242中执行如下:
declare
mcur number;
mstat number;
v_name varchar2(40);
begin
mcur:=dbms_sql.open_cursor;
for i in 1 .. 10000000 loop
dbms_sql.parse(mcur,‘select /* sess_242 */ * from moe where id=1‘,dbms_sql.native);
mstat:=dbms_sql.execute(mcur);
end loop;
dbms_sql.close_cursor(mcur);
end;
/
查看两个会话的等待:
SQL> select sid,event,p1raw,p2raw,p3raw from v$session where sid in (‘226‘,‘242‘);
SID EVENT P1RAW P2RAW P3RAW
---------- ----------------------------- ---------------- ---------------- ----------------
226 SQL*Net message from client 0000000062657100 0000000000000001 00
242 SQL*Net message from client 0000000062657100 0000000000000001 00
to be continued...
#end
整理自:oracle内核技术揭密 吕海波著
本文出自 “刚刚出壳的小鸟” 博客,请务必保留此出处http://qhd2004.blog.51cto.com/629417/1597647
标签:mutex latch library cache qhd2004 reference count
原文地址:http://qhd2004.blog.51cto.com/629417/1597647