AI智能
改变未来

Oracle链表揭秘

第一个区,从文件的128号开始占用空间?
每个文件的前128个块都是文件头被oracle留用。
文件头又分为两部分。 0号块与1 号块才是真正的文件头,2-127号块是位图块。

位图块:记录区中空间使用情况

为一个表分配一个区时,哪怕这个区只使用了1个字节,其他表无法再使用这部分的空间。
小区空间利用率高
大区适合全表扫描 连续的空间更多

一次IO最大为1M

堆表的特点就是无序,插入快速。

全表扫描 读取段头 区地图

Oracle hash表的Bucket存放的是指向链表头的指针。
解决hash冲突,链表。

热链竞争
热块竞争

Alter system dump datafile 5 block 111.

读不阻塞写,写阻塞读。
造成Buffer Busy Wait等待的元凶只能是DML语句。
xcur
Log buffer space :如果log buffer中没有空间,进程会先等待LGWR刷新log buffer中的空间。
保证log buffer中有空间,可以减少buffer pin锁的持有时间

生成redo数据
现在PGA中记录后映像数据,传入到共享池中,再传入到buffer中。

检查点队列:
块的状态由不脏变为脏时,会被链到检查点队列中,等待DBWR写入。
DBWR写脏块时,按照检查点队列依次写入。
实例恢复时,从检查点队列头部所指向的LRBA位置开始进行实例恢复

CKPT每三秒会将检查点队列头部对应的LRBA记录到控制文件中。
检查点队列中脏块的顺序就是LRBA的顺序

脏块何时别写入到磁盘中呢?
DBWR会检查检查点队列的长度,也就是查看脏块数量。

恢复过程:
恢复进程从磁盘中将5号文件的4321号块读进buffer cache中,
再将后映像xxxx 写到 buffer cache中4321块的第一行第一列。

小结:
1.块被修改时产生redo记录。
2.块在由不脏变脏时会被链接到检查点队列中。
3.检查点队列中块的排列顺序和redo记录的顺序基本一致
4.DBWR每三秒检查一次检查点队列的长度,也就是脏块数。如果队列过长,将触发写进程写脏块。
5.DBWR会沿着检查点队列的顺序写脏块。
6.CKPT每三秒一次,将检查点队列头对应的LRBA写进控制文件。
7.如果发生数据库崩溃、宕机等情况,需要进行实例恢复。实例恢复的起始点就是控制文件中记录的检查点队列头所对应的LRBA。
8.实例恢复开始时,Oracle找到此LRBA,再定位到某个redo文件的某个位置,开始依次进行实例恢复。

DBWR如何写脏块:

DBWR决定要写脏块时,会对要写的脏块进行整合,从而将相邻的脏块合并。
DBWR会将脏块从检查点队列中移到对象队列(OBJ-Q)

如果一次要写的脏块太多,DBWR不可能将所有脏块进行合并,那就是将脏块分成多个Batch,
分别对每个Batch进行合并

DB file parallel write产生的原因:
当DBWR写脏块时,会将脏块从检查点队列中移到各个对象的对象链表上进行合并(整合),如果一次性要写入的脏块太多,Oracle会分批进行合并,也就是将要写入的脏块分成几个Batch,然后分别依次对每个Batch进行合并,当第一个Batch开始写脏块前,DBWR会记录一个等待事件db file parallel write,直到这一个Batch所有脏块的写操作都完成了,db file parallel write事件才结束。可以通过db file parallel write 确定Batch的个数。

在一个batch中所有脏块写IO是异步的
Batch与batch之间是同步的,需要等待

单个Batch越大,Batch总数越少
如何提高DBWR的写效率
提高DBWR的写效率,就是减少块数、IO次数和“批数” Batch数

Hash链表 :逻辑读
检查点队列链表 :记录脏块
对象链表: 用于写I/O合并算法

LRU链表:

进行物理读时,如果空间不足时,需要通过LRU链表寻找可以覆盖的块,LRU分为冷端热端,会先将冷端头的buffer置换出内存。
LRU记录了buffer被访问的频率。

物理读访问LRU链表:
1.服务进程从辅助LRU链表的尾端开始搜索可覆盖的buffer,
可覆盖的标准就是:一不是脏块,二TCH值小于2
2.搜索到可以覆盖的buffer时,会将其移动到主LRU的冷端头等待被覆盖
3.LRU链相关操作完成后,进程将实际的发起一次物理读,从而将指定文件指定号块读进buffer cache中。

当辅助LRU没有buffer时,会从主LRU尾端开始搜索,当遇到TCH大于等于2的buffer时,会将其移动到热端头,并将TCH清零。遇到脏块时,服务进程会将其移动到主LRUW链表,等待DBWR进程醒来将其写入。

SMON进程和CKPT,DBWR,LGWR,PMON都是3秒醒来一次。

SMON每次醒来都会检查主,辅LRU链表的长度,
如果辅助LRU中buffer数量少于百分之25,SMON会从主LRU冷端尾搜索TCH小于2的非脏块,将其移动到辅助LRU中,保持辅助LRU buffer百分之25的比例。
目的:加快搜索LRU链表的效率

_small_table_threshold用来设置区分是大表扫描还是小表扫描,其单位是块数。
表的块数低于此参数就为小表,默认值是buffer cache总buffer数的百分之2。

LRUW链表:也是存放脏块的,与检查点队列相辅相成。
与检查点队列不同,块在变脏时,并不会立即进入到LRUW链表,而是等进程在LRU链表上搜索到可覆盖的牺牲者时(发现TCH值小于2的脏块),这些脏块才会被移动到LRUW中。

TCH大于等于2的脏块,会被移动到热端头,并将TCH清零。(按照检查点队列顺序写他们)
TCH小于2的脏块,会被移动到LRUW链表中

当有脏块进入LRUW链表后,不会马上将其写入,DBWR没3秒醒来一次,当DBWR醒来后,会将脏移动到辅助LRUW链表中,从辅助LRUW写入磁盘。
当DBWR醒来后,不管LRUW中有多少个脏块,都会将它们写入磁盘。

当要写的脏块有多个时,在被移到辅助LRUW后,DBWR也会对他们进行合并(将相邻的脏块合并为一个大脏块,然后写磁盘)

由于所有的脏块都在检查点队列中,从LRUW写的脏块,当写完成时,要从检查点队列中去掉。

如果脏块是从LRUW中写的,在其写完后,会被移到辅助LRU的尾端,等待被覆盖。

完整过程:
第一步:它被服务器进程从主LRU链表移到主LRUW链表。
第二步:一旦LRUW有脏块,DBWR3秒后醒来,发现LRUW中有脏块,它会将脏块从主LRUW移到辅助LRUW。
第三步:脏块被写磁盘。
第四步:写磁盘完成后,脏块被从检查点队列去除。
第五步:写完成的脏块已经不脏了,它被放入辅助LRU链表尾端,等待被下次物理读或CR块相关操作覆盖。

Free buffer waits等待的原因:
当服务器进程扫描LRU链表寻找可用块时,如果找了百分之40的buffer(百分之40受_db_block_max_scan_pct控制)还没有找到可以覆盖的buffer,进程将停止继续扫描LRU,唤醒DBWR写脏块,同时进程转入睡眠,开始等待free buffer waits。

这些不可被覆盖的buffer包含三种类型:
1.正在被其他进程加buffer pin lock的 (跳过)
2.TCH大于等于2的。 (移到热端)
3.脏块(TCH小于2的移动到LRUW中等待DBWR写入,TCH大于等于2的移动到热端)
LRU链中不可覆盖的buffer太多,是产生free buffer waits的直接原因。
只有脏块可以通过DBWR来减少,可以通过适当增肌写脏块频率来减少等待

脏块一旦进入LRUW,在两种情况下,会被写出:
1:完成扫描后,服务进程会判断总脏块数,如果检查点队列中的脏buffer没有达到总buffer数的百分之25,那么等待3秒超时,DBWR会从LRUW写脏块。
2:如果检查点队列中的脏buffer达到总buffer数的百分分支25,不需要等待超时,服务进程会唤醒DBWR,醒来后DBWR马上会从LRUW写脏块。

DBWR在什么情况下会醒来:
1.DBWR 三秒醒来,会检查LRUW上是否有脏块,如果有则马上写,查看检查点队列长度与redo数量,来决定是否写脏块。如果LRUW上没有脏块,检查点队列长度与redo数量也不多,则在3秒超时中什么也不做。

日志切换与写脏块:
没有等待的日志切换,并不会触发DBWR立即写脏块。日志切换只是唤醒DBWR,并告知DBWR已经发生了日志切换。写不写脏块,由DBWR自己判断。如果检查点队列中脏块数不多,上次写脏块后产生的redo也不多,而且LRUW中也没有脏块,那么DBWR就不会写脏块。

流程:
日志切换时,CKPT找到19号redo file在检查点队列中RBA最大的脏块,将它的SCN、RBA通知DBWR。
然后DBWR仍然按照原来的增量检查点机制写脏块,CKPT也仍然3秒一次检查DBWR的写机制。
CKPT醒来,发现DBWR已经写到上图的位置处,但是还没有将19号redo file中的redo记录对应的脏块全部写完。19号redo file的状态仍然保留为active。
CKPT又一次醒来,发现DBWR已经将19号redo file中redo recorder对应的脏块全部写完。
这时CKPT会做如下动作:
1.修改控制文件中19号redo file的状态,新状态为inactive
2.修改控制文件中数据文件的SCN
3.修改数据文件头的SCN和RBA

日志切换时,LGWR通知CKPT和DBWR,,然后马上向下一组redo file中写redo recorder

此时日志切换无等待。

日志切换等待:

此时如果再切换一次日志,在已经没有inactive日志文件的情况下,再发生日志切换,
Active的日志文件不能覆盖,这时DBWR将不再按正常的增量检查点机制判断是否写脏块,无论检查点队列中有几个脏块,都会马上开始从检查点队列写脏块。

下一个要被覆盖的redo file是371号日志,LGWR的日志切换需要等待DBWR写完371对应的脏块。而此时的等待事件就是log file switch。

增量检查点与完全检查点

如果是完全检查点,完全检查点会将buffer cache所有的脏块都写回数据文件实例恢复时,实例恢复时只能从发生完全检查点的时刻开始恢复
Shutdown immediate / 事务关闭 / 正常关闭 与 手动触发 alter system checkpoint

如果是增量检查点,每三秒DBWR醒来会检查检查点队列的长度以及redo记录容量决定是否将脏块写入,并且CKPT进程每3秒会将检查点队列上的LRBA写进控制文件,实例恢复时,从检查点队列头所指向的LRBA开始进行恢复。所以增量检查点减少了实例恢复的时间

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Oracle链表揭秘