《Oracle内核技术揭秘》(笔记)--第五章 Redo调优与备份恢复原理

Redo:重做,就是把已经做了的数据库操作再做一遍。一听就是用来做恢复的

Oracle 10g开始新增一种机制:IMU(In Memory Undo)。对Redo的机制做了不少改动。默认在IMU下工作

IMU最大价值:合并Redo Recorder,减少Redo Recorder条数;实际写的内容并没有太大变化,但可以减少写Redo时的Rdo Copy Latch和Redo Allocation latch的竞争,提升并发度

如:Redo数据有1000字节,如果分3个Redo Recorder写,则需要进行3次如下流程:

1、申请Redo相关Latch

2、向Log Buffer传送信息,释放Redo相关latch

如果是一条Redo Recorder,值需要申请、释放各一次Latch

了解非IMU与IMU Redo格式不同之处

设置是否采用IMU格式,用隐藏参数_in_memory_undo修改。修改IMU模式要重启。

 

alter system set "_in_memory_undo" = false;

以例子来说明:

初始表数据:

select * from vega whre id <=2; 有2条记录

通过观察修改这些数据,在Logfile 中dump出来的数据,查看差异。

通常,非IMU方式下,Oracle会尽量将每条DML语句的Redo数据放到一个Redo Recorder。如果是不同的DML,肯定会分别对应Redo Recorder

更新方式1:非IMU下,一个事务更新2条记录

update vega set name = lower(name) where id <=2;
commit;

做了一个update,更新2条记录,并commit。DUMP出来的redo内容如下:

2条语句(update、commit),对应2条Redo Recorder

 

更新方式2:非IMU下,执行2条update语句,每次修改一行

 

update vega set name = lower(name) where id = 1;
update vega set name = lower(name) where id = 2;
commit

很显然,这里有3条语句,应该有3条Redo recorder

 

更新方式3:IMU方式下,修改2条语句

update vega set name = lower(name) where id = 1;
update vega set name = lower(name) where id = 2;
commit

与上面比,完全乱序:

Redo数据流

DML:update vega set name = 'aaaaa' where id = 1;

假设:原来数据在:4号文件,4899号块,第一行(行号0),原值为AAAA。

非IMU下流程

1、拿到目标值:从共享池保存的SQL语句中,取出目标值aaaaa,传送到PGA。

2、构造UNDO对应的Redo

1)从Buffer中读取原值AAAAA到Log Buffer(在Log Buffer中构造UNDO的Redo),生成UNDO对应的Redo数据。

2)其他与Redo Change#相关数据,是在PGA生成,再传到Log Buffer。

3)因为是第一条,还需要在Log Buffer生成Redo Recorder header。

3、构造表块的Redo:从PGA将目标值aaaa传送到Log Buffer,把在PGA中生成的Redo change#相关其他数据复制到Log Buffer,生成表块的Redo Change#。

4、修改UNDO块:从表块Buffer将AAAAA传到UNDO块,UNDO的BUffer变为脏Buffer。

5、修改表块:将目标值传送到Buffer Cache的表块Buffer中,表块变为脏块。

6、当LGWR活跃时,或用户commit,将Redo Recorder所在的Redo块写入Redo文件。

 

IMU下流程

1、拿到目标值:从共享池保存的SQL语句中,取出目标值aaaaa,传送到PGA。-----第一步一样。

2、在共享池的UNDO IMU区保存UNDO信息和对应的RedoChange#信息。----原来是在Log Buffer,这里放在共享区

注意:在想共享池的IMU区或私有Redo区写入数据前,需要获得In Memory Undo Latch

3、在共享池的私有Redo区生成表块的Redo Change#。---- 原来是在Log Buffer,这里放在共享区

4、从私有Redo区读目标值,传递到表块Buffer,覆盖原来的AAAA,表块变脏。-----注意,这时候Log Buffer还没啥东西呢,先改了数据。此时,只有表块变脏;UNDO块还没修改

5、从UNDO IMU区将前映像复制到UNDO的Buffer中。-----修改UNDO块。

在提交时,这一部分不一定是由Server完成的,可能是SMON完成。SMON每3秒出发一次,必做工作之一就是从IMU向UNDO块的Buffer写前映像数据。如果进程提交时SMON还没来得及写,则由提交的进程来写。

完成这一步骤,UNDO变脏。在IMU下,表块的修改在UNDO块的修改之前,UNDO块直到提交才修改

6、写目标值和Redo change#到Log Buffer。

7、写UNDO块的Redo Change#到Log Buffer

8、将Log Buffer中,表块、UNDO块对应的Redo Change#,还有commit生成的Change#,一次性写入Redo文件。

 

可以看出,实际使用IMU 共享池做缓存,日志相关的都在共享池中准备;然后先修改Buffer,再修改UNDO块;然后在写入Log Buffer。顺序上发生了变化,变为一次写入Redo

IMU与非IMU相关的Redo Latch

IMU下和Redo相关的Latch

  • In Memory Undo Latch:开始修改块之前(在共享池修改块),Server进程会以独占模式、Nowait形式获得In Memory Undo latch。在申请空间、释放空间时都需要请求

所谓Nowait:就是如果无法获得,进程会转而使用老的、非IMU的方式。 

  • Redo Allocation Latch:因为它和In Mmory Undo Latch是一对的;所以申请到第一个,才会申请第二个,所以不会产生阻塞。这个用来申请私有Redo区(在共享池的私有Redo区)。在申请空间、释放空间时都需要请求。

还有,用来在Lob Buffer空间分配时需要的:

  • Redo Allocation latch(和上面一个不一样):用在Log Buffer空间分配,分配后立刻释放。
  • Redo Copy latch:将Redo Change#从共享池分配到Log Buffer中。复制完释放。

还有:

  • Messages Latch:向一个消息去写入信息,通知LGWR“我已提交”。

总结一下

 

非IMU下机制:进程执行命令时,Redo流程:每条命令都会执行以下步骤

1、获得Redo Copy Latch----准备拷贝数据

2、获得redo Allocation Latch,分配Log Buffer空间

3、申请完空间,释放 redo Allocation Latch

4、写Redo 到Log Buffer

5、释放Redo Copy Latch。

IMU机制下:由于Redo Change#数据线放入共享区区的私有Redo区,因此,即使执行多条修改命令,也只会在事务最后提交时一次性写入Lob Buffer。所以,只需要一次Redo Copy和Redo Allocation latch(当然是针对Lob buffer的)。所以,单个事务的DML语句越多,使用IMU后,越能减少Redo Copy latch和Redo Allocation latch的竞争。

 

非IMU下提交过程:

1、持有Redo Write Latch

2、持有Messages latch,向共享池的消息内存中写一条给LGWR的信息

3、释放Messages latch

4、持有Post/write Queue Latch

5、唤醒LGWR

6、释放Post/Write Queue Latch

7、释放Redo Write

8、开始等待Log File Sync事件

在IMU下不需要Redo Write Latch。

 

 

Redo Allocation Latch:这里说的是申请Log Buffer的。

在IMU下,整个Log Buffer称为Public Redo Area。会被分为多个Public Redo Strand Area

每个Public Redo Strand Area由一个Redo Allocation Latch保护。v$latch_children视图的IMMEDIATE_GETS列+1。

要向Log Buffer写数据时,需要以NoWait方式申请一个Redo Allocation Latch。如果得不到,向下一个申请。如果都申请不到,最后等待

当提交时

1、提交对应的Redo Change#也是在私有Redo区生成

2、然后获得Redo Copy Latch

3、再获得Log Buffer的Redo Allocation latch

4、然后申请空间。完成后释放Redo Allocation latch

5、从私有Redo区复制Redo信息到Public Redo Starnd Area中。

6、释放Redo Copy latch

这部分和上面非IMU一样

 

查看时公有还是私有Redo Allocation Latch竞争,查看Latch miss。私有Redo区的Latch Miss通常会有kcrf_pvt_strand_bind、kcrf_pvt_strand_unbind。公有区时kcrfw_redo_gen:redo allocation 1。

 

 

Log Buffer空间使用。

实际前面已经介绍了不少Log Buffer空间使用的内容了。

Log Buffer空间时循环且线性(使用过程不会跳过某个块)使用。 会用一个变量记录空间当前使用位置。空间分配就是修改这个变量

以非IMU为例

1、进程A执行一条DML,要将Redo Change#写入Log Buffer:A先获得Redo Copy Latch和Redo Allocation Latch,在Redo Allocation Latch保护下获得一块空间。释放Redo Allocation Latch,A可以向新空间复制数。

2、B、C同时申请Redo Allocation Latch申请。因为有两个,,所以各自获取一个。然后都获得空间。

3、然后B、C同时可以向不同的空间写数据。

4、D也申请空间。

5、如果有已经申请的块写满,还需要空间,则继续获得Redo Allocation latch,新分配块。这可以导致一个事务的块会在不同的公共Redo区域。结果如图:

 

虽然事务都没提交,但LGWR每3秒活动一次。会刷新Log Buffer(写到redo File去)。它会刷新已经满的块。写过的Log Buffer会标记位可覆盖

 

 

如果有进程提交,则对应的Redo 都要从Log Buffer写入到磁盘。没满也写。

LGWR以Log Buffer块为单位写磁盘。一次最少写一个Lob Buffer块

未满的空间称为“Redo 浪费”,可以在v$sysstat的Redo Wastage中查看浪费空间大小。

浪费能提高LGWR的写入性能。因为磁盘IO有最小单位,如512字节。如果不浪费,写了一部分,那接着写,还需要把数据读出来,修改再写。浪费的话就可以直接从下一个块中写了。

 

如果Log Buffer已经满了

这时进程需要新写Log Buffer,进程会紧急通知LGWR马上刷新Log Buffer,然后等待Log Buffer Space事件。

产生Log Buffer Space事件的原因:Redo数据太多、LGWR写得不够快。解决思路自然是反过来,减少Redo数据,或者加快LGWR性能

 

LGWR与Log File Sync和Log File Parallel Write

Log File Parallel Write:是LGWR写Log Buffer到Redo File时的等待事件

Log file Sync:时前台进程从提交到提交完成的等待事件

下面这个图比较详细的描述了整个提交过程:

 

前台进程commit后,准备工作:如IMU下,提交时要修改UNDO块。

 

Log file Sync = CPU+ 几个Latch+log File Parallel Write

如果CPU资源不成问题,LGWR在申请Latch时也没有遇到竞争,则Log file Sync只比Log File Parallel Write略大

如果差距太大,则:

1、CPU资源紧张

2、LGWR申请Latch遇到竞争

3、同时提交的进程太多。---这个在第二章介绍。

 

IMU使用条件

IMU和非IMU一起使用的。

选择非IMU的场景:

从2/3看,看上去,IMU使用很受限制。

1、DML产生太多Redo,超过了共享池中IMU区、私有Redo区任何一个的大小。当出现这两个不够,会出现IMU flushes,即将IMU区些到UNDO块中,私有Redo区写到Log Buffer中。v$sysstat中的IMU Flushes+1。事务后续的DML不再使用IMU机制,即DML直接修改UNDO、所有Redo直接写入Log Buffer。

2、RAC下不用IMU

3、如果打开SUPPLEMENTAL LOG,也不用IMU

附加日志(supplemental log)可以指示数据库在日志中添加额外信息到日志流中,以支持基于日志的工具,如逻辑standby、streams、GoldenGate、LogMiner。可以在数据库和表上设置。  。

4、Redo文件过小,不用IMU。小于50MB不用。---这个资料里面没有介绍,是作者识别出来的

5、修改,_in_memory_undo参数为False,就是直接用隐藏参数关闭。

6、事务第一条DML语句运行时,申请IMU Latch不成功,或申请成功,但所有IMU区、私有redo区都被占用,放弃IMU机制,使用非IMU。

 

 

注意点

 

1、IMU得不到使用,而退化成非IMU,会加剧竞争,导致高并发下响应慢。

2、如果没IMU,处理redo,就是向Log Buffer复制数据极易出现竞争。

 

v$systat中:

user commits:记录了instance启动以来总的提交次数

IMU commits记录了使用IMU测次数。这两个的比例很能说明问题。

IMU Flushes:IMU刷新次数,刷新后,事务后续DML也不用IMU。

 

 

在高并发事务、修改非常繁忙的数据中心,提高小事务的IMU比率:尽量保持事务短小、让事务尽快提交。不提交会占用IMU块、私有Redo区,导致退化成非IMU。所以,段事务提交时间对IMU很重要

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值