redo详解程序员

 

什么是REDO 
REDO记录transaction logs,分为online和archived。以恢复为目的。 比如,机器停电,那么在重起之后需要online redo logs去恢复系统到失败点。 比如,磁盘坏了,需要用archived redo logs和online redo logs区恢复数据。 比如,truncate一个表或其他的操作,想恢复到之前的状态,同样也需要。


下面是官网的解释:

The most crucial structure for recovery operations is the redo log, which consists of two or more preallocated files that store all changes made to the database as they occur. Every instance of an Oracle Database has an associated redo log to protect the database in case of an instance failure.

两个概念:

改变向量(Change Vector)

改变向量表示对数据库内某一个数据块所做的一次变更。改变向量中包含了变更的数据块的版本号、事务操作代码、变更从属数据块的地址(DBA)以及更新后的数据。例如:一个update事务包含一系列的改变向量,对于数据块的修改是一个向量,对于回滚段的修改又是一个向量。

重做记录(Redo Record)

重做记录通常由一组改变向量组成,是一个改变向量的集合,代表一个数据库的变更(INSERT、UPDATE、DELETE等操作),构成数据库变更的最小恢复单位。例如:一个Update的重做记录包括相应的回滚段的改变向量和相应的数据块的改变向量等。

COMMIT和ROLLBACK操作:

COMMIT 以前,常想当然地认为,一个大的TRANSACTION(比如大批量地INSERT数据)的COMMIT会花费时间比短的TRANSACTION长。而事实上是没有什么区别的, 
因为ORACLE在COMMIT之前已经把该写的东西写到DISK中了, 
我们COMMIT只是 
1,产生一个SCN给我们TRANSACTION,SCN简单理解就是给TRANSACTION排队,以便恢复和保持一致性。 
2,REDO写REDO到DISK中(LGWR,这就是log file sync),记录SCN在ONLINE REDO LOG,当这一步发生时,我们可以说事实上已经提交了,这个TRANSACTION已经结束(在V$TRANSACTION里消失了) 
3,SESSION所拥有的LOCK(V$LOCK)被释放。 
4,Block Cleanout(这个问题是产生ORA-01555: snapshot too old的根本原因) ROLLBACK ROLLBACK和COMMIT正好相反,ROLLBACK的时间和TRANSACTION的大小有直接关系。因为ROLLBACK必须物理上恢复数据。COMMIT之所以快,是因为ORACLE在COMMIT之前已经作了很多工作(产生UNDO,修改BLOCK,REDO,LATCH分配), 

ROLLBACK慢也是基于相同的原因。 
ROLLBACK会 
1,恢复数据,DELETE的就重新INSERT,INSERT的就重新DELETE,UPDATE的就再UPDATE。 
2,RELEASE LOCK ROLLBACK要比COMMIT消耗更多资源,因为ORACLE认为你一旦做数据更新,那么就意味着你要COMMIT(其他数据库不全是这种设计理念,比如DB2),所以在你更新数据的时候就做了大量的工作,这也可以理解为什么不建议用TABLE来做TEMPORARY TABLE。(TEMP TABLE消耗的REDO比固定表在INSERT时要少很多 ,UPDATE时差不多是1/2, 
但是DELETE却相差无几) REDO 产生REDO 越多,你的系统越慢,不但影响你自己的SESSION,还影响其他SESSION,LGWR管理REDO,并且是TRANSACTION的结束标志。

REDO和UNDO的区别

写的次序:

redo–> undo–>datafile insert一条记录时, 表跟undo的信息都会放进 redo 中, 在commit 或之前, redo 的信息会放进硬盘上. 故障时, redo 便可恢复那些已经commit 了的数据. redo->每次操作都先记录到redo日志中,当出现实例故障(像断电),导致数据未能更新到数据文件,则数据库重启时须redo,重新把数据更新到数据文件 undo->记录更改前的一份copy,但你系统rollback时,把这份copy重新覆盖到原来的数据 redo->记录所有操作,用于恢复(redo records all the database transaction used for recovery) undo->记录所有的前印象,用于回滚(undo is used to store uncommited data infor used for rollback) redo->已递交的事务,实例恢复时要写到数据文件去的 undo->未递交的事务. redo的原因是:每次commit时,将数据的修改立即写到online redo中,但是并不一定同时将该数据的修改写到数据文件中。因为该数据已经提交,但是只存在联机日志文件中,所以在恢复时需要将数据从联机日志文件中找出来,重新应用一下,使已经更改数据在数据文件中也改过来!

undo的原因是:在oracle正常运行时,为了提高效率,加入用户还没有commit,但是空闲内存不多时,会由DBWR进程将脏块写入到数据文件中,以便腾出宝贵的内存供其它进程使用。这就是需要UNDO的原因。因为还没有发出commit语句,但是oracle的dbwr进程已经将没有提交的数据写到数据文件中去了。 undo 也是也是datafile, 可能dirty buffer 没有写回到磁盘里面去。只有先redo apply 成功了,才能保证undo datafile 里面的东西都是正确的,然后才能rollback 做undo的目的是使系统恢复到系统崩溃前(关机前)的状态,再进行redo是保证系统的一致性. 不做undo,系统就不会知道之前的状态,redo就无从谈起 所以instance crash recovery 的时候总是先rollforward, 再rollback undo 回退段中的数据是以“回退条目”方式存储。回退条目=块信息(在事务中发生改动的块的编号)+在事务提交前存储在块中的数据 在每一个回退段中oracle都为其维护一张“事务表” 在事务表中记录着与该回退段中所有回退条目相关的事务编号(事务SCN&回退条目) redo 重做记录由一组“变更向量”组成。每个变更变量中记录了事务对数据库中某个块所做的修改。

当用户提交一条commit语句时,LGWR进程会立刻将一条提交记录写入到重做日志文件中,然后再开始写入与该事务相关的重做信息。 #事务提交成功后,Oracle将为该事备生成一个系统变更码(SCN)。事务的SCN将同时记录在它的提交记录和重做记录中。

commit 提交事务前完成的工作:

·在SGA区的回退缓存中生成该事务的回退条目。在回退条目中保存有该事务所修改的数据的原始版本。

·在SGA区的重做日志缓存中生成该事务的重做记录。重做记录中记载了该事务对数据块所进行的修改,并且还记载了对回退段中的数据块所进行的修改。缓存中的重做记录有可能在事务提交之前就写入硬盘中。

·在SGA区的数据库缓丰中记录了事务对数据库所进行的修改。这些修改也有可能在事务提交之前就写入硬盘中。

提交事务时完成的工作:

·在为该事务指定的回退段中的内部事务表内记录下这个事务已经被提交,并且生成一个惟一的SCN记录在内部事务表中,用于惟一标识这个事务。

·LGWR后进进程将SGA区重做日志缓存中的重做记录写入联机重做日志文件。在写入重做日志的同时还将写入该事务的SCN。

·Oracle服务进程释放事务所使用的所有记录锁与表锁。

·Oracle通知用户事务提交完成。

·Oracle将该事务标记为已完成。

 rollback 回退事务完成的工作:

·Oracle通过使用回退段中的回退条目,撤销事务中所有SQL语句对数据库所做的修改。

·Oracle服务进程释放事务所使用的所有锁

·Oracle通知事务回退成功。

·Oracle将该事务标记为已完成

举个例子: insert into a(id) values(1);(redo) 这条记录是需要回滚的。回滚的语句是delete from a where id = 1;(undo) 试想想看。如果没有做insert into a(id) values(1);(redo) 那么delete from a where id = 1;(undo)这句话就没有意义了。 现在看下正确的恢复: 先insert into a(id) values(1);(redo) 然后delete from a where id = 1;(undo) 系统就回到了原先的状态,没有这条记录了。

日志的状态

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之后被重置。被使用之后,该状态会被改变。

 

产生多少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

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/aiops/7268.html

(0)
上一篇 2021年7月17日 08:46
下一篇 2021年7月17日 08:46

相关推荐

发表回复

登录后才能评论