buffer cache中获取block 过程
DML or select时候 oracle根据 SQL语句执行计划,找到block,构造一个 叫buffer descriptor的block描述内存的结构(主要含block 物理地址 ,type,object id),这个block存在 session的 pga中,oracle应用buffer descriptor记录的信息 运用hash算法,得到需要的block所在的hash bucket(确定block在哪条hash chain上),从 上面 挂的 第一个buffer header搜索到最后一个buffer header,
hash chain上 搜索逻辑 :
1.比较buffer header 上所记录的block地址,不符合条件SKIP 此buffer header
2.skip status为cr的buffer header
3.如果buffer header状态为 reading则等待,直到状态改变后比较buffer header 记录的block地址是否符合
4.若发现block地址符合的buffer header,查该buffer header是否位于正在使用的list上,如果是 则判断已存在的lock mode,与要求的lock mode是否兼容,如果兼容则返回该buffer header中记录的block地址,将当前process id放入buffer header 所处的正在使用的list上
5.如果lock mode不兼容,用buffer header所指向的block中的内容构建一个xcurrent的block 和一个cr状态的buffer header(指向新建立xcurrent状态的 复制block)
6.搜索完整个hash chain还未发现需要的buffer header,从disk读取datafile,读入到buffer cache中 相应的buffer header 挂在hash chain上
buffer cache指向block的状态分6种
1.free =可以被重用的block
2.xcurrent=已EXCLUSIVE方式获取的当前模式的block(insert,update,delete时产生),scurrent=可以与其他instance共享的当前模式block
3.cr=一致读块,永远不会写入disk
4.reading=正从disk读取出来的块
5.mreciver=正在进行介质恢复的block
6.ircovery=正在进行instance recovery的block
SQL> select distinct status from v$bh;
STATUS
-------
xcur
free
cr
buffer header
在buffer cache中,每一个block读入 buffer cache时,都会在buffer cache中构造一个buffer header(buffer header与block一一对应)
1.存放该block在buffer cache中实际存储地址
2.存放该block的类型(data,segment header,undo header,undo block等类型)
3.由于此buffer header 所在的hash chain,是通过在buffer header保存指向前一个buffer header的指针和指向后一个buffer header的指针方式实现,所以还存指针
4.存储lru,lruw,ckptq,fileq等队列,一样是通过记录前后buffer header指针方式实现
5.当前该buffer header所对应的数据块的状态以及标记
6.该buffer header被访问的次数(touch次数)
7.正在等待该buffer header的进程列表(waiter list)及正在使用此buffer header的(user list)
bucket 与cache buffers chains latch
oracle通过bucket管理buffer cache ,oracle用hash算法将不同的buffer分配到bucket中,当需要定位需要的buffer是否在buffer cache中时,用同样的hash 算法 到相应的bucket上 搜索对应的buffer header既可 bucket 中buffer header通过cache buffer chains连接(双向链表)
查 buffer 中 默认 hash bucket数量既有多条hash chain
SQL> set linesize 132
SQL> column name format a30
SQL> column value format a25
SQL> select
2 x.ksppinm name,
3 y.ksppstvl value,
4 y.ksppstdf isdefault,
5 decode(bitand(y.ksppstvf,7),1,'MODIFIED',4,'SYSTEM_MOD','FALSE') ismod,
6 decode(bitand(y.ksppstvf,2),2,'TRUE','FALSE') isadj,x. KSPPDESC
7 from
8 sys.x$ksppi x,
9 sys.x$ksppcv y
10 where
11 x.inst_id = userenv('Instance') and
12 y.inst_id = userenv('Instance') and
13 x.indx = y.indx and
14 x.ksppinm like '%_&par%'
15 order by
16 translate(x.ksppinm, ' _', ' ')
17 /
输入 par 的值: db_block_hash
原值 14: x.ksppinm like '%_&par%'
新值 14: x.ksppinm like '%_db_block_hash%'
NAME VALUE ISDEFAULT ISMOD IS
ADJ
------------------------------ ------------------------- --------- ---------- --
---
KSPPDESC
--------------------------------------------------------------------------------
----------------------------------------------------
_db_block_hash_buckets 65536 TRUE FALSE FA
LSE
oracle一直在优化hash buckets数目 因为 hash bucket 越多,存放的buffer header越分散(但bucket太多的话对空间,管理 都有压力) 对于cache buffer chain latch争用越少
查看buffer header结构
SQL> create table t1 (a char(2000), b char(2000), c char(2000));
表已创建。
SQL> insert into t1 values ('a','a','a');
已创建 1 行。
SQL> insert into t1 values ('b','b','b');
已创建 1 行。
SQL> commit;
提交完成。
SQL> select header_file,header_block,owner from dba_segments where segment_name=
'T1';
HEADER_FILE HEADER_BLOCK OWNER
----------- ------------ ------------------------------
4 1115 XH
SQL> select file#,block# ,rowid from (select dbms_rowid.rowid_relative_fno(rowi
d) file#,dbms_rowid.rowid_block_number(rowid) block# ,rowid from T1);
FILE# BLOCK# ROWID
---------- ---------- ------------------
4 1117 AAAMm+AAEAAAARdAAA
4 1118 AAAMm+AAEAAAAReAAA
SQL> select object_id,data_object_id from user_objects where object_name='T1';
OBJECT_ID DATA_OBJECT_ID
---------- --------------
51646 51646
SQL> select file#,block#,status,class# from v$bh where bjd=51646;
FILE# BLOCK# STATUS CLASS#
---------- ---------- ------- ----------
4 1118 xcur 1
4 1113 xcur 8
4 1116 xcur 1
4 1119 xcur 1
4 1114 xcur 9
4 1117 xcur 1
4 1120 xcur 1
4 1115 xcur 4
SQL>
SQL> alter session set events 'immediate trace name buffers level 1';
会话已更改。
1.dump buffer header
2.1+block header
3.2+block内容
4.dump buffer header & hash chain
5.1+dump block header &hash chain (1+4)
6.2+dump block header &hash chain (2+4)
8.dump buffer header & hash chain+ user list+waiter list
9.1+dump block header & hash chain+ user list+waiter list
10.2+dump block内容+hash chain+ user list+waiter list
BH (15FEB49C) file#: 4 rdba: 0x0100045b (4/1115) class: 4 ba: 15CF6000
set: 3 blksize: 8192 bsi: 0 set-flg: 2 pwbcnt: 239
dbwrid: 0 obj: 51646 objn: 51646 tsn: 4 afn: 4
hash: [2019a6a0,2019a6a0] lru: [15feb5a0,15feb440]
ckptq: [15feb364,183eb414] fileq: [15feb36c,183eb41c] objq: [1eb25148,15feb494]
st: XCURRENT md: NULL tch: 4
flags: buffer_dirty gotten_in_current_mode redo_since_read
LRBA: [0xb.22ea.0] HSCN: [0x0.d1044] HSUB: [1]
..................................
BH (15FEABAC) file#: 4 rdba: 0x01000460 (4/1120) class: 1 ba: 15CDC000
set: 3 blksize: 8192 bsi: 0 set-flg: 2 pwbcnt: 239
dbwrid: 0 obj: 51646 objn: 51646 tsn: 4 afn: 4
hash: [201a8d38,201a8d38] lru: [15feacb0,163f3840]
ckptq: [1a3f8304,15feac84] fileq: [2025c878,15feac8c] objq: [15fead04,1eb25148]
st: XCURRENT md: NULL tch: 2
flags: buffer_dirty gotten_in_current_mode redo_since_read
LRBA: [0xb.2300.0] HSCN: [0x0.d1044] HSUB: [1]
...............................
在buffer cache中 一共可以找到 (select file#,block#,status from v$bh where bjd=51646;)对应的所有buffer header
分析:
hash: [2019a6a0,2019a6a0] 就是指向前一个buffer header的指针 和 指向后一个buffer header的指针,x$bh中 NXT_HASH
PRV_HASH,例中两个值相等表示这个hash chain上只有一个buffer header
lru: [15feb5a0,15feb440] x$bh中 nxt_repl,prv_repl,lru上的下一个buffer和 上一个buffer,ckptq,fileq都是buffer修改后 使用的队列
可以看出buffer header就是一个双向链表组成
class:表示buffer header对应block的类型,1=data block,2=sort block,3=save undo block,4=segment header,5=save undo header,6=free list,7=extent map,
8=1st level bmb;9=2nd level bmb;10=3rd level bmb;11=bitmap block;12=bitmap index block;13=unused;14=undo header;15=undo block。
对应v$bh中class#
rdba:buffer header对应的 块地址
SQL> variable file# number;
SQL> variable blk# number;
SQL> execute :file#:=dbms_utility.data_block_address_file(to_number('1000460','x
xxxxxx'));
PL/SQL 过程已成功完成。
SQL> execute :blk#:=dbms_utility.data_block_address_block(to_number('1000460','x
xxxxxxx'));
PL/SQL 过程已成功完成。
SQL> print file#
FILE#
----------
4
SQL> print block#
SP2-0552: 未声明绑定变量 "BLOCK#"。
SQL> print blk#
BLK#
----------
1120
另外可以 rdba: 0x01000460 ,100前3位代表file#,0460代表block#
SQL> select to_number('100','xxxx') file#,to_number('0460','xxxxxx') block# f
dual;
FILE# BLOCK#
---------- ----------
256 1120
整体buffer header内容 对应 x$bh,v$bh