buffer cache实验9-从buffer caceh中读取数据块解析-从逻辑读到物理读

先来张大图:

buffer cache实验9-从buffer caceh中读取数据块解析-从逻辑读到物理读_第1张图片

 

所用SQL语句:
BYS@ ocm1>select dbms_rowid.rowid_relative_fno(rowid) file#,dbms_rowid.rowid_block_number(rowid) block#,deptno from bys.test;
     FILE#     BLOCK#     DEPTNO
---------- ---------- ----------
         4        391         10

就以上图为例,文字描述分析一下前台进程发出查询语句时获取所需数据块的过程:

注:本文不涉及SQL语句的解析部分、客户端与服务器交互等,只涉及buffer cache。
这里的物理读是非直接路径读、非大表全表扫描--此点最后会有介绍。
如果发出的是更新语句,只是在buffer pin上所加的锁为X独占锁,其它步骤基本一致。
本文的例子只读取了一个数据块。
从buffer cache中读取一个数据块一般需要100ns左右,从一般的存储硬盘中读取一个数据块需要10ms;所以大概算一下,从内存中读取数据块比从硬盘中快近十万倍。
故oracle在读取数据块时,先在buffer cache中查找,如存在,则读取--逻辑读;如果数据块不存在,则发生物理读,从物理文件中将数据读入buffer cache(不考虑直接读的情况)。
之前写过的逻辑读的: 数据读取之逻辑读简单解析--关于BUFFER CACHE

下面正式开始:--首先是逻辑读的过程

1.前台进程发出查询语句select deptno from bys.test;
2.根据DBA计算HASH值,根据HASH值找到相应的Hash bucket
3.获取CBC LATCH,如获取失败,则将产生: latch:cache buffers chains
4.在CBC LATCH保护下,服务器进程扫描hash chain,查找是否有所需BH
5.如查找到所需BH,将在Buffer Header上加buffer pin锁(这里是读操作所以是共享锁(找到BH时的锁常见有:当前读锁、一致读锁或修改锁),如获取buffer pin失败(比如正在X模式申请S模式),会产生 buffer busy waits等待),并根据BH中指定的块在内存中实际地址,读取buffer,并将结果返回前台进程。读取完毕(纳秒级)后,将再次获取CBC LATCH,释放buffer pin锁,再释放CBC LATCH。 

-----以上为逻辑读,如果未找到buffer,将发生如下的物理读:

6.如果未查找到所需BH,将发生物理读。服务器进程将从磁盘上的相应数据文件中读取所需块,并将此块读入buffer cche中。
7.将块读入buffer cache中时,如何找到一个可以使用的buffer呢?下面步骤进行一步步解析。
8.首先在辅助LRU的最尾端向前查找可用buffer,TCH<2的块可以被重用。
9.如果辅助LRU最尾端的块是TCH<2的块,则将直接使用此块,并将其移动到主LRU的冷端头。
同时也会根据此块的DBA进行HASH,查找相应的HASH BUCKET,将此块加入到对应的HASH CHAIN上,并对BH中的相应信息进行修改(如对应X$BH中的LRU_FLAG,NXT_HASH、BA等字段的具体值)--此过程也需要相应的CBC LATCH /buffer pin锁的获取释放等。
再把数据块的值返回前台进程,此时物理读就完成了。
10.如果辅助LRU最尾端的块是TCH>=2的块,则首先将此块移动到主LRU的热端头, 同时TCH清零;然后在在辅助LRU上继续向前查找,直到找到可用的块---TCH<2。 之后的过程和步骤9中的就一样了(SMON每三秒时,服务器进程扫描空闲BUFFER时;都会把辅助LRU中TCH大于等于2的移到主的热端头)
11.如果在辅助LRU上搜索完毕扔未找到可以使用的块,则将从主LRU的冷端尾开始搜索。
12.如果主LRU最尾端的块是TCH<2的块,则将直接使用此块,并将其移动到主LRU的冷端头, TCH为1。如是TCH>=2的块,则将其移动到热端头,TCH清零。如果是脏块,则将其移动到主LRUW上。依此规则向前搜索查找可用块。( SMON每3秒,从主LRU冷端查找TCH小于2的非脏块到辅助LRU确保辅助LRU中有可用BUFFER)
13.如果从主LRU最尾端向前搜索了40%(隐含参数_db_block_max_scan_pct,)还未找到可用块,则将触发DBWR写LRUW上的脏块--(CKPTQ队列的写不涉及LRUW, 只有DBWR会写LRUW上脏块,并且写的是LRUW上的全部脏块-每三秒醒来也要全部写出LRUM上所有块才会休眠写LRUW上脏块的步骤是:DBWR进程写时或者SMON进程每三秒醒来时(LRUW进程不像辅助LRUW那样,非DBWR进程也允许访问),会将主LRUW上的一部分脏块移动到辅助LRUW,然后在辅助LRUW上排序、写入磁盘;然后再从主LRUW上移动下一批,直到写出完毕再次进入睡眠。并且在DBWR写出过程中,会产生 free buffer waits),写出后的buffer将重新挂载到辅助LRU上并变为可用。

关于大表全表扫描及_small_table_threshold   参数的说明:

大小表的界限是:_small_table_threshold,此参数中的VALUE 是数据块个数。
大表的全表扫描只使用辅助LRU,其块的TCH为1。这样做不对主LRU上的块进行冲击,同时也方便大表中块的重用。此时如有其它用户语句需要从辅助LRU上查找可用buffer,直接可以使用,节约时间。
小表的全表扫描和普通数据块一样来查找可用buffer.


P_NAME                                   P_DESCRIPTION                                      P_VALUE                        ISDEFAULT ISMODIFIED ISADJ
---------------------------------------- -------------------------------------------------- ------------------------------ --------- ---------- -----
_small_table_threshold                   lower threshold level of table size for direct rea 89                             TRUE      FALSE    FALSE
                                         ds
这里的89是BLOCK数量,表所使用的BLOCK的数量--不是直接的MB或者KB。。
_small_table_threshold的值在数据库启动的时候自动配置成BUFFER数量的2%。--可以修改buffer cache大小并重启数据库验证。
SYS@ bys3> select count(*)*0.02 from x$bh;   ---从X$BH中获取BUFFER的数量
COUNT(*)*0.02
-------------
        88.98


关于直接路径读的说明: ---来自百度

直接路径读(direct path read)通常发生在Oracle直接读数据到进程PGA时,这个读取不需要经过SGA。直接路径读等待事件的3个参数分别是file number(指绝对文件号)、first dba、block cnt数量。在Oracle 10g/11g中,这个等待事件被归于User I/O一类。db file sequential read、db file scattered read、direct path read 是常见的集中数据读方式。

在数据仓库环境大量的direct path read是正常的。在OLTP中,大量direct path read意味应用有问题导致大量磁盘排序读取操作。

最为常见的是第一种情况。在DSS系统中,存在大量的direct path read是很正常的,但是在OLTP系统中,通常显著的直接路径读(direct path read)都意味着系统应用存在问题,从而导致大量的磁盘排序读取操作。直接路径写(direct paht write)通常发生在Oracle直接从PGA写数据到数据文件或临时文件,这个写操作可以绕过SGA。直接路径写等待事件的3个参数分别是:file number(指绝对文件号)、first dba和block cnt数量,在Oracle 10g/11g中,这个等待事件同direct path read一样被归于User I/O一类。这类写入操作通常在以下情况被使用:·直接路径加载;·并行DML操作;·磁盘排序;·对未缓存的“LOB”段的写入,随后会记录为direct path write(lob)等待。最为常见的直接路径写,多数因为磁盘排序导致。对于这一写入等待,我们应该找到I/O操作最为频繁的数据文件(如果有过多的排序操作,很有可能就是临时文件),分散负载,加快其写入操作。

直接路径插入时,不产生表块的回滚信息,而是依赖高水位点实现回滚
但是,如果表有索引,将会产生索引的回滚信息,而且索引的块会被读进buffer cache
Oracle官方文档建议,如果使用直接路径插入,向表中传送大量数据,可先将表上的索引删掉,插入结束后,再重新建立索引

在Oracle 11g版本中串行的全表扫描可能使用直接路径读取(direct path read)的方式取代之前版本中一直使用的DB FILE SCATTERED READ, 显然direct path read具备更多的优势:

1. 减少了对latch争用

2.物理IO的大小不再取决于buffer_cache中所存在的块;
试想某个8个块的extent中1,3,5,7号块在高速缓存中,而2,4,6,8块没有被缓存,传统的方式在读取该extent时将会是对2,4,6,8块进行4次db file sequential read,其效率往往要比单次读取这个区间的所有8个块还要低得多,
而direct path read则可以完全避免这类问题,尽可能地单次读入更多的物理块。

当然直接路径读取也会引入一些缺点:
1.在直接路径读取某段前需要对该对象进行一次段级的检查点(A segment checkpoint).
2.可能导致重复的延迟块清除操作
http://www.oracledatabase12g.com/archives/direct-read-impact-on-delayed-block-read.html


你可能感兴趣的:(buffer cache实验9-从buffer caceh中读取数据块解析-从逻辑读到物理读)