参考文献
- 深入解析Oracle
- v$latch
- v$latch_children
一、计算机进程访问数据原理
当计算机进程需要访问数据时,会优先从内存中查找是否有想要的数据,若内存中没有,再从硬盘中读取相应的数据。这样设计是有原因的,因为计算机访问内存中数据的速率远高于访问硬盘中数据的速率,利用好内存中已有的缓存数据,可以大大提高计算机的运行效率。
二、Oracle对于buffer cache的管理
将buffer cache比作一部汉语字典,所有的buffer就是字典的具体内容,而buffer header就是字典目录里的字,buffer header记录了相应buffer的文件号、块地址、状态等信息,通过buffer header我们可以找到相应的buffer。但是当我们想要查找某一个字的信息时,总不能通过遍历所有字来先找到这个字,所以有了bucket,这个bucket就是字典里的拼音查找或者部首查找法,Oracler通过计算数据块地址(DBA)的哈希值来决定一个buffer块存放在哪个bucket中,同一个bucket中的cache组成了cache buffer chain。所以当Oracle进程需要访问数据时,先计算待访问数据的数据块地址的哈希值,通过该值找到对应的bucket,再从对应的bucket中查找数据的buffer header,最后通过buffer header在内存中找到要访问的数据。
但是,若待访问的数据在内存中不存在且无足够空间存放从磁盘读入的数据时,就需要用到LRU和LRUW链表,这俩链表也记录了buffer的buffer header,不同的是,LRU链表记录的是未被修改过的buffer信息,LRUW记录的是被修改过的buffer信息。Oracle会先扫描一遍LRU链表,将其中被修改过的buffer移到LRUW链表,随后调动DBWR将LRUW链表中的数据写入磁盘,释放buffer空间,以便读入新的数据。
三、 Cache buffer LRU chain
当进程需要读数据到buffer cache中时,会扫描更新LRU链表,当进程访问buffer cache中的数据时,也会通过LRU算法更新LRU链表。为了防止多个进程同时修改LRU链表,一个进程在更新LRU链表时必须先获得latch,这个用于锁定LRU的latch就是cache buffer lru chain。
查看cache buffer lru chain信息:
SET lines 2000
col name FOR a30
SELECT addr,
latch#,
name,
gets,
misses,
immediate_gets,
immediate_misses
FROM v$latch
WHERE name = 'cache buffers lru chain';
查看各子latch使用情况:
SET lines 2000 col name FOR a20
SELECT addr,
child#,
name,
gets,
misses,
immediate_gets igets,
immediate_misses imisses
FROM v$latch_children
WHERE name = 'cache buffers lru chain';
四、 Cache buffer chain
同一个bucket中的数据块连接成cache buffer chain,当有多个进程需要访问同一个bucket中的数据块时,若都是只读,则可以共享这个bucket的latch,共同访问其中的数据块,若有进程需要写入,则需要独自占有bucket的latch。
查询热点块
SELECT *
FROM (SELECT addr,
ts#,
file#,
dbarfil,
dbablk,
tch
FROM x$bh
ORDER BY tch DESC)
WHERE rownum < 11;
查询热点块所属对象信息
SET lines 2000
col owner FOR a20
col segment_name FOR a40
SELECT e.owner,
e.segment_name,
e.segment_type,
b.addr,
b.ts#,
b.file#,
b.dbarfil,
b.dbablk,
b.tch
FROM dba_extents e,
(SELECT *
FROM (SELECT addr,
ts#,
file#,
dbarfil,
dbablk,
tch
FROM x$bh
ORDER BY tch DESC)
WHERE rownum < 11) b
WHERE e.file_id = b.file#
AND e.block_id <= b.dbablk
AND e.block_id + e.blocks > b.dbablk;
将具体的latch竞争与热点块相关联
SET lines 2000
SELECT b.addr,
a.ts#,
a.dbarfil,
a.dbablk,
a.tch,
b.gets,
b.misses,
b.sleeps
FROM (SELECT *
FROM (SELECT addr,
ts#,
file#,
dbarfil,
dbablk,
tch,
hladdr
FROM x$bh
ORDER BY tch DESC)
WHERE rownum < 11) a,
(SELECT addr,
gets,
misses,
sleeps
FROM v$latch_children
WHERE name = 'cache buffers chains') b
WHERE a.hladdr = b.addr;
将具体的latch竞争与热点块与具体的块信息关联
SET lines 2000
col owner FOR a20
col segment_name FOR a60
SELECT /*+ rule */ e.owner,
e.segment_name,
e.segment_type,
b.addr,
a.ts#,
a.dbarfil,
a.dbablk,
a.tch,
b.gets,
b.misses,
b.sleeps
FROM dba_extents e,
(SELECT *
FROM (SELECT addr,
ts#,
file#,
dbarfil,
dbablk,
tch,
hladdr
FROM x$bh
ORDER BY tch DESC)
WHERE rownum < 11) a,
(SELECT addr,
gets,
misses,
sleeps
FROM v$latch_children
WHERE name = 'cache buffers chains') b
WHERE a.hladdr = b.addr
AND e.file_id = a.file#
AND e.block_id <= a.dbablk
AND e.block_id + e.blocks > a.dbablk;
查询执行在热点块上具体的sql语句
SET lines 2000
col owner FOR a20
col segment_name FOR a60
SELECT /*+ rule */ hash_value,
sql_text
FROM v$sqltext
WHERE ( hash_value, address ) IN (SELECT s.hash_value,
s.address
FROM v$sqltext s,
(SELECT e.owner,
e.segment_name,
e.segment_type,
b.addr,
a.ts#,
a.dbarfil,
a.dbablk,
a.tch,
b.gets,
b.misses,
b.sleeps
FROM dba_extents e,
(SELECT *
FROM (SELECT addr,
ts#,
file#,
dbarfil,
dbablk,
tch,
hladdr
FROM x$bh
ORDER BY tch DESC)
WHERE rownum < 11) a,
(SELECT addr,
gets,
misses,
sleeps
FROM v$latch_children
WHERE name = 'cache buffers chains') b
WHERE a.hladdr = b.addr
AND e.file_id = a.file#
AND e.block_id <= a.dbablk
AND e.block_id + e.blocks > a.dbablk) abe
WHERE Upper(s.sql_text) LIKE '%'
|| abe.segment_name
|| '%'
AND abe.segment_type = 'TABLE')
ORDER BY hash_value,
address,
piece;