第六章 重做(Redo)
Redo的内容
Oracle通过Redo来实现快速提交,一方面是因为Redo Log File可以连续、顺序地快速写出,另一个方面也和Redo记录的精简内容有关。
两个概念:
改变向量(Change Vector)
改变向量表示对数据库内某一个数据块所做的一次变更。改变向量中包含了变更的数据块的版本号、事务操作代码、变更从属数据块的地址(DBA)以及更新后的数据。例如:一个update事务包含一系列的改变向量,对于数据块的修改是一个向量,对于回滚段的修改又是一个向量。
重做记录(Redo Record)
重做记录通常由一组改变向量组成,是一个改变向量的集合,代表一个数据库的变更(INSERT、UPDATE、DELETE等操作),构成数据库变更的最小恢复单位。例如:一个Update的重做记录包括相应的回滚段的改变向量和相应的数据块的改变向量等。
假定发出一个更新语句:
Update emp set sal=4000 where empno=7788;
这个语句的执行如下所示:
检查empno=7788记录在Buffer Cache中是否存在,如果不存在则读取道Buffer Cache中;
在回滚段表空间的相应回滚段事务表上分配事务槽,这个操作需要记录Redo信息;
从回滚段读入或者在Buffer Cache中创建sal=3000的前镜像,这需要产生Redo信息并记入Redo Log Buffer;
修改sal=4000,这是update的数据变更,需要记入Redo Log Buffer;
当用户提交时,会在Redo Log Buffer记录提交信息,并在回滚段标记该事务为非激活。
对于数据块的修改,如果执行写出,那么通常需要写出8KB的Block,而对于Redo日志来说,重做信息却相当精简,Oracle只需要记录那些重构事务必须的信息(如事务号、文件号、块号、行号、字段等)即可,这个数据量大大减少。
产生多少Redo
在SQL*Plus中使用autotrace功能
当在SQL*Plus中启用autotrace跟踪后,在执行了特定的DML语句时,Oracle会显示该语句的统计信息,其中,Redo size一栏表示的就是该操作产生的Redo的数量:
SQL> set autotrace trace stat
SQL> insert into test
2 select empno,ename from scott.emp;
已创建12行。
Statistics
----------------------------------------------------------
189 recursive calls
2 db block gets
37 consistent gets
4 physical reads
564 redo size
778 bytes sent via SQL*Net to client
823 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
12 rows processed
通过v$mystat查询:
Oracle通过v$mystat视图记录当前session的统计信息,也可以从该视图中查询得到session的Redo生成情况:
SQL> col name for a30
SQL> select a.name,b.value
2 from v$statname a,v$mystat b
3 where a.statistic#=b.statistic# and a.name='redo size';
NAME VALUE
------------------------------ ----------
redo size 5000
SQL> insert into test
2 select empno,ename from scott.emp;
已创建12行。
SQL> select a.name,b.value
2 from v$statname a,v$mystat b
3 where a.statistic#=b.statistic# and a.name='redo size';
NAME VALUE
------------------------------ ----------
redo size 5564
SQL> select 5564-5000 from dual;
5564-5000
----------
564
通过v$sysstat查询:
对于数据库全局redo的生成量,可以通过v$sysstat视图来查询得到:
SQL> col value for 999999999999
SQL> select name,value from v$sysstat
2 where name='redo size';
NAME VALUE
------------------------------ -------------
redo size 11471552
从v$sysstat视图中得到的是自数据库实例启动以来累计日志生成量,可以根据实例启动时间来大致估算每天数据库的日志生成量:
SQL> select
2 (select value/1024/1024/1024 from v$sysstat where name='redo size')/
3 (select sysdate-(select startup_time from v$instance) from dual)
4 redo_gb_per_day from dual;
REDO_GB_PER_DAY
---------------
.073238253
Redo写的触发条件:
每3秒钟超时(Timeout)
当LGWR处于空闲状态时,它依赖于rdbms ipc message等待,处于休眠状态,直到3秒超时时间到,如果LGWR发现有Redo需要写出,那么LGWR将执行写出操作,log file parallel write等待时间将会出现。
启用10046事件,从LGWR跟踪文件中可以观察到这些事件。
域值达到:
两个触发日志写的条件:
Redo Log Buffer 1/3满;
Redo Log Buffer 具有1MB脏数据。
这两者都是限制条件,在触发时是协同生效的。
只要有进程在Log Buffer中分配和使用空间,已经使用的Log Buffer的数量将被计算。如果使用的块的数量大于或等于一个隐含参数_log_io_size的设置,那么将会触发LGWR写操作。如果此时LGWR未处于活动状态,那么LGWR将被通知去执行后台写操作。
缺省的_log_io_size等于1/3log buffer大小,上限值为1MB,此参数在X$KSPPSV中显示的0值意为缺省值。
也就是LGWR将在Min(1M,1/3 log buffer size)时触发,注意此处的log buffer size是以Log Block来衡量的。
经常有人推荐Log Buffer设置为3MB大小,就是因为当Redo Log Buffer为3MB时,以上两个条件可能同时达到。
用户提交
当一个事务提交时,在Redo Stream中将记录一个提交标志。在这些Redo被写到磁盘上之前,这个事务是不可恢复的。所以在事务返回成功标志给用户前,必须等待LGWR写完成。进程通知LGWR写,并且以Log File Sync事件开始休眠,超时时间为1秒。
Oracle的隐含参数_wait_for_sync参数可以设置为false来避免Redo File Sync的等待,但是将无法保证事务的恢复性。
存在一个SGA变量用以记录Redo线程序要同步的Log Block Number。如果多个提交在唤醒LGWR之前发生,此变量记录最高的Log Block Number,在此之前的所有Redo都将被写入磁盘,这有时被称为组提交。
在DBWn写之前
Redo Log Buffer的大小设置
Redo Log Buffer的大小由初始化参数LOG_BUFFER定义,该参数的缺省值为:
MAX(512KB,128KB*CPU_COUNT)
通常这个缺省值是足够的,由于Redo Log Buffer的写出操作相当频繁,所以过大的Log Buffer设置通常是没有必要的。如果缺省值不能满足要求,一般来说3MB是一个较合理的调整开端。
log_buffer参数的设置是否需要调整,可以从数据库的等待事件来判断:
SQL> select event#,name from v$event_name where name='log buffer space';
EVENT# NAME
---------- ----------------------------------------------------------------
178 log buffer space
当Log Buffer Space等待事件出现并且较为显著时,可以考虑增大Log Buffer以缩减竞争。
Commit做了什么
当完成事务操作,发出Commit命令之后,随后会收到一个反馈“Commit complete”。
提交完成,意味着Oracle已经将此时间点之前的Redo写入重做日志文件中,这个日志写完成之后,Oracle可以释放用户去执行其他任务。如果此后发生数据库崩溃,那么Oracle可以从重做日志文件中恢复这些提交过的数据,从而保证提交之后的数据不会丢失。
日志的状态
CURRENT:指的是当前的日志文件,该日志文件是活动的,当前正在被使用的,在进行崩溃恢复时,Current的日志文件时必须的。
ACTIVE:活动的非当前日志,该日志可能已经完成归档也可能没有归档,活动的日志文件在Crash恢复时会被用到。
ACITVE状态意味着检查点尚未完成,如果日志文件循环使用再次到达该文件,数据库将处于等待的停顿状态,此时在alert文件中,可以看到类似如下记录:Checkpoint not complete
当这种问题出现时,可以从数据库内部通过v$session_wait来观察,该视图会显示数据库当前哪些session正处于这种等待。
Checkpoint not complete在数据库中体现为等待事件log file switch(checkpoint incomplete):
SQL> select sid,event,state from v$session_wait;
在此同时,可能DBWR进程正在进行db file parallel write,日志文件必须等待DBWR完成检查点触发的写操作之后才能被覆盖。如果设置了参数log_checkpoints_to_alert为TRUE的话,还可以在alert文件中清晰地看到检查点的增进和完成情况。
引起Checkpoint Incomplete可能有以下多种原因:
日志文件过小,切换过于频繁;
日志组太少,不能满足正常业务量的需要;
日志文件所在磁盘I/O存在瓶颈,导致写出缓慢,阻塞数据库正常运行;
由于数据文件磁盘I/O瓶颈,DBWR写出过于缓慢;
由于事务量巨大,DBWR负载过高,不堪重负。
解决方法:
适当增加日志文件大小;
适当增加日志组数;
使用更快的磁盘存储日志文件(如采用更高转速磁盘;使用RAID10而不是RAID5等方式);
改善磁盘I/O的性能 ;
使用多个DBWR进程或使用异步I/O等。
注意:Checkpoint Incomplete是一类严重的等待,它意味着数据库不能再产生日志,所有数据库修改操作将全部挂起。
INACTIVE:非活动日志,该日志在实例恢复时不再需要,但是在介质恢复时可能会用到。INACTIVE状态的日志也可能没有被归档。如果数据库启动在归档模式,在未完成归档之前,日志文件也不允许被覆盖,这时候活动进程会处于log file switch(archiving needed)等待之中。
日志是否完成归档,可以根据v$log视图的archived字段进行判断。
UNUSED:是指该日志从未被写入,这类日志可能是刚被添加到数据库或者在RESETLOGS之后被重置。被使用之后,该状态会被改变。
日志块的大小:
初始化参数LOG_BUFFER决定了Redo Log Buffer的大小,这个参数的缺省值为MAX(512KB,128KB*CPU_COUNT)。
虽然LOG_BUFFER中的Redo Entries的大小是以bytes为单位,但是LGWR仍然以block为单位把redo写入磁盘,Redo Block Size是Oracle源代码中固定的,通常的操作系统都是以512bytes为单位。
可以从v$sysstat中的统计信息中通过计算粗略得到,主要有以下几个统计信息:
Redo Size:Redo信息的大小;
Redo Wastage:浪费的Redo的大小;
Redo Block Written:LGWR写出的Redo Block的数量;
额外的信息:每个Redo Block Header需要占用16bytes。
SQL> select name,value from v$sysstat
2 where name in('redo size','redo wastage','redo blocks written');
NAME VALUE
---------------------------------------------------------------- ----------
redo size 6298004
redo wastage 1458232
redo blocks written 15641
SQL> select ceil(16+(6298004+1458232)/15641) rbsize from dual;
RBSIZE
----------
512
在Linux/UNIX下,Oracle还提供另外一个命令行工具可以用于检查文件的Block Size大小:dbfsize
也可以通过转储日志文件的方式来获取日志文件块大小:
SQL> alter session set events 'immediate trace name redohdr level 10';
LOG FILE #1:
(name #3) D:\ORACLE\ORADATA\GVORA\REDO01.LOG
Thread 1 redo log links: forward: 2 backward: 0
siz: 0x32000 seq: 0x0000008c hws: 0x6 bsz: 512 nab: 0xb060 flg: 0x0 dup: 1
Archive links: fwrd: 0 back: 0 Prev scn: 0x0000.0060b530
Low scn: 0x0000.00620001 05/03/2009 18:41:45
Next scn: 0x0000.00635173 05/04/2009 08:57:50
日志文件的大小:
当日志文件发生切换时,会触发一个检查点,那么日志文件的大小就和检查点的触发频率相关。频繁的检查点操作可以缩短数据库的恢复时间,但是过于频繁的检查点却会带来性能负担。所以如何合理的设置日志文件的大小也是数据库优化的一个重要内容。
一般来说,在实际生产环境中,把log switch的时间控制在半小时左右即可。
为什么热备份期间产生的Redo要比正常的多:
在数据库处于热备份状态时,会产生比平常更多的日志。这是因为在热备份期间,Oracle为了解决SPLIT Block的问题,需要在日志文件中记录修改的行所在的数据块前镜像,而不仅仅是修改信息。
简单了解一下SPLIT Block的概念:
Oracle的数据块是由多个操作系统块组成。通常UNIX文件系统使用512bytes的数据块,而Oracle使用8KB的db_block_size。当热备份数据文件时,要使用文件系统的命令工具(cp)拷贝文件,并且使用文件系统的blocksize读取数据文件。
这种情况下,可能出现如下状况:当拷贝数据文件的同时,数据库正好向数据文件写数据。这就使得拷贝的文件中包含这样的database block,它的一部分OS Block来自于数据库向数据文件(这个DB Block)写操作之前,另一部分来自于写操作之后。对于数据库来说,这样的Block本身并不一致,而是一个分裂块(SPLIT Block)。这样的分裂块在恢复时并不可用(会提示Corrupted Block)。
所以在热备份状态下,对于变更的数据,Oracle需要在日志中记录整个变化的数据块的前镜像。这样如果在恢复的过程中,数据文件中出现分裂块,Oracle就可以通过日志文件中的数据块的前镜像覆盖备份,以完成恢复。
分裂块产生的根本原因在于备份过程中引入了操作系统工具(如cp工具等),操作系统工具无法保证Oracle数据库的一致性。如果使用RMAN备份,由于RMAN可以通过反复对区获得一致的Block,从而可以避免Split Block的生成,所以不会生成额外的Redo。因此建议在备份时(特别是繁忙的数据库),应该尽量采用RMAN备份。
能否不生成Redo
NOLOGGING对于数据库的影响
正常的数据库必须生成Redo,这是数据库的机制,否则数据库在遇到故障或Crash时则无法恢复。但是Oracle为了增强某些特殊操作的性能,对于一些SQL语句,Oracle允许使用NOLOGGING子句,NOLOGGING可以使得日志生成大幅降低,但是必要日志(比如对于字典表的修改)仍然会被记录。
可以使用NOLOGGING的环境非常有限,在以下操作中,可以增加NOLOGGING子句:
创建索引或重建索引时;
通过/*+append */提示,使用直接路径批量INSERT操作或SQL*Loader直接路径加载数据;
CTAS方式创建数据表时;
大对象(LOB)的操作;
一些Alter table操作,如move、split等。
需要注意的是,由于NOLOGGING操作会导致对于数据的操作不记录日志,如果数据库崩溃,这部分数据是无法恢复的,所以通常的建议是,在进行了NOLOGGING操作之后,需要对数据库进行备份,以避免数据因数据库实效而丢失。
disable_logging对于数据库的影响
Oracle存在一个内部参数,可以使数据库关闭日志记录,从而实现某些特殊需要或测试目的,这个参数是_disable_logging,可以动态设置这个参数。
由于不记录日志,在进行数据库恢复时,这些数据是无法恢复的。
如果数据库运行在归档模式下,设置该参数会导致日志文件损坏。因为在设置该参数之后,归档进程无法识别该日志文件格式,会将该日志文件标记为损坏。
设置了_disable_logging参数,可以禁用日志的生成,从而提高某些测试的性能。
Force logging(强制日志)模式
当使用Dataguard作为数据库的备份或容灾高可用性手段时,通常日志就变得不可缺少。在Oracle 9iR2中,可以将数据库置于强制日志模式(Force Logging Mode)。在强制日志模式下,所有操作都将记录日志。
Redo故障的恢复
丢失非活动日志组的故障恢复:
如果数据库丢失的是非活动(INACTIVE)日志组,由于非活动日志组已经完成检查点,数据库不会发生数据损失,此时只需要通过clear重建该日志组即可。
清除日志组的命令:alter database clear logfile group 2;
如果数据库处于归档模式下,并且该日志组未完成归档则需要使用如下命令强制清除:
Alter database clear unarchived logfile group 2;
丢失活动或当前日志文件的恢复:
在损失当前日志时,数据库是正常关闭的:
由于关闭数据库前,Oracle会执行全面检查点,当前日志在实例恢复中可以不再需要。直接clear即可。
在Oracle 9i中,可能无法对当前日志进行clear,需要通过until cancel恢复后,resetlogs打开。
在损失当前日志时,数据库是异常关闭的:
如果在损失当前日志时,数据库是异常关闭的,那么Oracle在进行实例恢复时必须要求当前日志,否则Oracle将无法保证提交成功的数据不丢失(也就意味着Oracle会丢失数据),在这种情况下,Oracle数据库将无法启动。
对于这种情况,通常需要从备份中恢复数据文件,通过应用归档日志文件向前推演,直到最后一个完好的日志文件,然后通过resetlogs启动数据库完成恢复。丢失的数据就是损坏的日志文件中的数据。
可是不幸的是,很多数据库是从来不备份的,那么在面对这种情况时,Oracle提供了一种内部手段可以用来强制性数据库打开、忽略一致性等问题。在打开数据库之后,Oracle建议导出数据,然后重建数据库,再导入数据,完成灾难恢复。
SQL> select ksppinm,ksppdesc from x$ksppi
2 where ksppinm like '%_resetlogs_%';
KSPPINM
----------------------------------------------------------------
KSPPDESC
----------------------------------------------------------------
_allow_resetlogs_corruption
allow resetlogs even if it will cause corruption
该参数的含义是,允许在破坏一致性的情况下强制重置日志,打开数据库。_allow_resetlogs_corruption将使用所有数据文件最旧的SCN打开数据库,所以通常需要保证SYSTEM表空间拥有最旧的SCN。
丢失ACTIVE日志和CURRENT日志的情况下,如果没有备份,只能使用隐含参数_allow_resetlogs_corruption强制启动数据库,设置此参数后,在数据库open过程中,Oracle会跳过某些一致性检查,从而使数据库可能跳过不一致状态,直接打开。
Alter system set “_allow_resetlogs_corruption”=true scope=spfile;
Recover database using backup controlfile until cancel; ----mount
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/15203236/viewspace-600884/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/15203236/viewspace-600884/