Skip to content

未分类 - 2. page

DDL操作产生产生UNDO和REDO。原因是:
delete 需要把每个有数据的数据块读出来,写上delete标记, 另外还要把delete的内容拷一份到undo,还要把它的动作记到redo。
而truncate , 就是在系统表(或者tablespace bitmap)里面,把相关的extent 的 标记未未使用,另外把dba_objects(obj$)里的data_object_id换一下。

1.ddl所产生的undo量视乎其所要维护数据字典的操作类型和操作量.DDL执行失败也产生少量UNDO,因为执行少量递归操作后,Oracle发现所要drop的对象并不存在,将会rollback之前的”部分”递归dml操作。
2.DDL操作产生的REDO是因为DDL修改的字典表和一些段头信息产生的redo。

SQL> select name,value as bytes from (select b.name,a.value from v$mystat a,v$statname b where a.STATISTIC#=b.statistic#) where name=’redo size’ or name like ‘undo change%’;
NAME BYTES
—————————————————————- ———-
redo size 5628
undo change vector size 2212
SQL> select * from tab;
TNAME TABTYPE CLUSTERID
—————————— ——- ———-
SYS_TEMP_FBT TABLE
TEST TABLE
TEST1 TABLE
SQL> drop table test;
Table dropped
SQL> select name,value as bytes from (select b.name,a.value from v$mystat a,v$statname b where a.STATISTIC#=b.statistic#) where name=’redo size’ or name like ‘undo change%’;
NAME BYTES
—————————————————————- ———-
redo size 12608
undo change vector size 4216
SQL> select * from cat;

TABLE_NAME TABLE_TYPE
—————————— ———–
BIN$eSjr2ednQKavapT1EPZ/aw==$0 TABLE
SYS_TEMP_FBT TABLE
TEST1 TABLE

SQL> select 12608-5628 as redo_size_drop_change,4216-2212 as undo_size_dorp_change from dual;

REDO_SIZE_DROP_CHANGE UNDO_SIZE_DORP_CHANGE
——————— ———————
6980 2004

DDL操作产生UNDO和REDO的示例

先来张大图:

所用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中读取数据块解析-从逻辑读到物理读

服务器进程在扫描LRU主列时,将脏块移动到LRUW列。当扫描到一个阀值(10G/11G是40%- _db_block_max_scan_pct参数决定)时会停止扫描同时通知DBWR写脏块-写脏块完后的块重新挂载到辅助LRU列,就得到了空闲buffer。
DBWR进程写脏块完成前,服务器进程等待空闲buffer时出现free buffer waits等待事件

关于物理读时找空闲BUFFER,LRUW与写脏块详细过程,详见:
_db_block_max_scan_pct 参数的默认值–11.2.0.4
P_NAME P_DESCRIPTION P_VALUE ISDEFAULT ISMODIFIED ISADJ
—————————————- ————————————————– —————————— ——— ———- —–
_db_block_max_scan_pct Percentage of buffers to inspect when looking for ree 40 TRUE FALSE FALSE

引起争用的场景及解决思路:
低效SQL-需要更多无关的数据块,需要更多buffer,容易出现free buffer waits等待事件
buffer cache小不够用–就是内存小buffer cache给的空间小,是实实在在的不够用,考虑增大buffer cache。
DBWR写的性能差–可能是数据库的主机CPU资源紧张,DBWR进程工作慢,或者I/O慢,影响写入速度,或者是CPU和I/O都没问题,可以考虑增大DBWR进程数量。
因为DBWR进程写脏块到数据文件时,会以独占模式占用脏缓冲区,此时有其它进程要读取或修改脏缓冲区,需要等待DBWR写数据完成,此时会出现write complete waits等待。
判断DBWR/存储性能导致此类问题的步骤:
磁盘繁忙程度
磁盘响应时间–大I/0不超过20MS,小I/O不超过5MS
IOPS是否正常–每盘150左右
存储CACHE是否正常–这个偶尔会出现
和存储相关的硬件是否正常运行
——-如磁盘正常,要考虑增加DBWR进程数量–一般不能超过CPU数量。

free buffer waits等待常由应用–低效SQL引起
write complete waits等待常由存储性能引起。 –此段来自周亮《ORACLE DBA实战攻略》

buffer cache实验8-free buffer waits-完成

1.working set与Latch:cache buffers lru chain:
每个working set都具有它自己的一组LRU和LRUW链表(LRU和LRUW链表总是成对出现的)。
ORACLE为了提高buffer cache性能(大内存),使用了多个working set
每个working set都由一个名为“Latch:cache buffers lru chain”的latch来保护,每一个lru latch对应一个working set。
而每个被加载到buffer cache的buffer header都以轮询的方式挂到working set(包含的LRU链表)上去–。
而每个被加载到buffer cache的buffer header都以轮询的方式挂到working set上去。也就是说,当buffer cache加载一个新的数据块时,其对应的buffer header会去找一个可用的lru latch,如果没有找到,则再找下一个lru latch,直到找到为止。如果轮询完所有的lru latch也没能找到可用的lru latch,该进程只有等待latch free等待事件,同时出现在v$session_wait中,并增加“latch misses”。
如果启用了多个DBWR后台进程的话,每个DBWR进程都会对应一个不同的working set,而且每个DBWR只会处理分配给它的working set,不会处理其他的working set。

2.cache buffers lru chain LATCH的个数:
ORACLE默认创建的cache buffers lru chain LATCH数量与CPU个数、DBWR个数相关。
DBWR小于4,个数为:4*CPU个数
DBWR大于4,个数为:DBWR*CPU个数
每个buffer pool使用自己的cache buffers lru chain LATCH,一个数据库实例可以配置:DEFAULT,2KB,4KB,8KB,16KB,32KB,KEEP,RECYCLE 这8种类型的buffer pool,故cache buffers lru chain LATCH的数量最少为8个。详见: 点击打开链接
或使用以下语句查出buffer cache中各个pool中cache buffers lru chain LATCH的获取情况: –语句来自周亮《ORACLE DBA实战攻略》
SYS@ bys3>select d.blk_size,c.child#,p.bp_name,c.gets,c.sleeps from x$kcbwds d,v$latch_children c,x$kcbwbpd p where d.set_latch=c.addr and d.set_id between p.bp_lo_sid and p.bp_hi_sid order by c.child#;

BLK_SIZE CHILD# BP_NAME GETS SLEEPS
———- ———- ——————– ———- ———-
8192 1 KEEP 18 0
8192 3 RECYCLE 18 0
8192 5 DEFAULT 864096 144
2048 7 DEFAULT 18 0
4096 9 DEFAULT 18 0
8192 11 DEFAULT 18 0
16384 13 DEFAULT 18 0
32768 15 DEFAULT 18 0

在我的虚拟机中,CPU是一个,在10G中是有8个LRU LATCH,在11GR2中,是16个LRU LATCH.见:http://blog.csdn.net/haibusuanyun/article/details/19084583#t6

3.cache buffers lru chain LATCH在什么情况下需要使用:
数据块读入buffer cache前需要获得cache buffers lru chain LATCH–因为要到LRU上找空闲buffer
DBWR扫描LRUW链表前需要获得cache buffers lru chain LATCH
SMON将空闲buffer移动至LRU辅助列,也需要cache buffers lru chain LATCH。
4.cache buffers lru chain LATCH在哪些情况下出现争用:
简单说就是多个进程同时检索LRU/LRUW时出现。
具体情况有: –物理读过多引起争用概念高。
多个会话并发访问不同表或索引,因为这样发生物理读可能较大–由上面可知物理读时需要在LRU找空闲buffer
脏块过多,DBWR写出慢(DBWR的性能不足-主机性能问题,或者磁盘I/O),涉及DBWR调优-在此不多讨论。

注:CBC LATCH主要发生在逻辑读时-多会话并发访问相同的表或索引-相同表或索引多集中于几条相同hash chain。

buffer cache实验6-latch:cache buffers lru chains

1.CBC latch产生的原理:
一次逻辑读时CBC latch锁及Buffer pin锁的获取和释放过程如下:
1.加Latch X
2.进入hash chain,在相应的BH上加Buffer pin S (0–>1)
3.释放Latch X
4.进行逻辑读–也就是通过BH中的buffer adderss找到数据块在内存中真实位置 —假如读了1MS
5.加Latch X
6.释放Buffer pin S (1–>0) 0:没锁 1:共享锁 2:独占锁
7.释放Latch X
物理读时需要将数据块的buffer header挂载到hash chain上,也需要获取CBC LATCH,buffer header中有hash chain的信息,BH挂载到hash chain上,应该在BH内存结构中会有信息更改,BH的修改也就需要有Buffer pin了,锁的获取和释放应该和逻辑读时差不多,不过是在Buffer pin S 上要加2号独占锁。当然这一点是我大胆推测的哈哈。

CBC latch上一般都用的独占锁,使用共享锁的情况是:9I后在索引的根、枝叶读时使用共享CBC LATCH,无BUFFER PIN方式访问上
BH上Buffer pin锁状态:
0 未加锁
1 共享锁,读BUFFER BLOCK —SELECT
2 独占锁,写BUFFER BLOCK —DML语句

2.从CBC latch产生的原理可以发现,出现CBC LATCH争用会有以下情况:

1.CBC latch保护不同的链表、不同BH :同一CBC LATCH下多个hash chain上的多个BH被同时访问时,
2.CBC latch保护同一链表下同一BH :同一hash chain上同一BH被同时访问时
3.物理读将数据块挂载到hash chain上时,多个物理块HASH冲突都挂载到同一个hash chain或者要挂载的hash chain上的BH在被逻辑读
这里的被访问,可能发生在逻辑读或者物理读,即:使用到CBC latch的场景是:
服务器进程需要扫描hash chain上数据块–逻辑读
服务器进程需要将数据块挂载到hash chain上–物理读
关于hash chain与hash bucket,详见本系列第一篇: 点击打开链接

3.关于CBC LATCH争用的模拟实验,有两种思路:
一是热链–这个不太好模拟,我也没做成功。
二是热块-这个好模拟,同时还会有buffer busy waits,具体实验见: 点击打开链接

4.latch: cache buffers chains 解决思路:
1、热链:调整_db_block_hash_latches加大latch数量,作用是减少同一LATCH下多个桶被同时访问的情况。即多个表的相应块在BUFFER CACHE中对应不同BH,不同BH又对应在不同HASH BUCKETS,但是这多个HASH BUCKETS是属于同一个LATCH。。
alter system set “_db_block_hash_latches”=10240 scope=spfile;
2、热块:调整BUFFER _CACHE,参数:db_cache_size,big integer 100M
热块是:同一表在BUFFER CACHE中的块(一个块对应一个BH,BH对应一个HASH BUCKET)被多个会话同时读,–全表扫描时容易出现。可以使用多个会话同时读取同一表的同一行的方式来模拟产生CBC latch,查询时使用ROWID做条件,查询速度快,更容易引起CBC latch。
3、修改应用,减少全表扫描,也就是优化SQL语句了

例如:CBC Latch的产生次数的查询:
SYS@ bys3>col name for a20
SYS@ bys3>select NAME,GETS ,MISSES ,SLEEPS ,IMMEDIATE_GETS, IMMEDIATE_MISSES from v$latch where name like ‘%cache buffers chains%’;
NAME GETS MISSES SLEEPS IMMEDIATE_GETS IMMEDIATE_MISSES
——————– ———- ———- ———- ————– —————-
cache buffers chains 2006932 0 0 78095 0

buffer cache实验5-latch:cache buffers chain