5、分析redo
redo的管理是数据库的一个串行点,每个oracle数据库实例都只有一个LGWR进程,所有的事务都会要求LGWR进程去管理,写他们的各自的redo,每个操作的LGWR写的越多,就会使系统越慢。所以我们就要时刻关注每一个事务生成的rodo的量。
如何查看redo的量呢? 你可能会想到用sql*plus内置的特性AUTOTRACE来查看,但是有个问题Tom说这个特性只能查看比较简单的DML语句,对于一些复杂的操作我们还是需要查看两个动态性能表:
V$MYSTAT: 其中有每个会话的提交的信息。
V$STATNAME:这个视图告诉我们V$MYSTAT视图的每一行表示什么
对于Oracle9i Release 2,DBA 可能把数据库置于FORCE LOGGING 模式。在这种情况下,所有操作都会计入日志。查询SELECT FORCE_LOGGING FROM V$DATABASE 可以查看是否强制为日志模式。这个特性是为了支持Data Guard, Data Guard 是Oracle 的一个灾难恢复特性,它依赖于redo来维护一个备用数据库(standby database)备份。
在sql中设置nologing:
有些SQL 语句和操作支持使用NOLOGGING 子句。这并不是说:这个对象的所有操作在执行时都不生成重做日志,而是说有些特定操作生成的redo 会比平常(即不使用NOLOGGING 子句时)少得多。注意,我只是说“redo”少得多,而不是“完全没有redo“。所有操作都会生成一些redo——不论日志模式是什么,有数据字典操作都会计入日志。只不过使用NOLOGGING 子句后,生成的redo 量可能会显著减少。
5.1 块清除:
有时候我们在执行select操作的时候也可能会产生redo,其一个可能原因就是oracle块清除。块清除就是删除所修改数据块上与"锁定"有关的信息,即事务信息。Oracle在事务相关的提交列表中,记录下已修改的块列表,每个列表记录20个块,根据需要可能分配有多个这种列表。这种块列表有一个上限,就是缓冲区缓存大小的10%.
如果一次修改的块,没有超过了缓冲区缓存大小的10%,并且这些块在内存中,则commit时,会清除块上的事务信息,否则,就不会理会它,直到下次访问这些块时,再清除块中的事务信息,这就是延迟块清除.因为这个Select修改了块的事务信息,所以就会产生Redo.
块清楚的分类:
fast commit cleanout 和 delayed block cleanout 这两种:
如果一个事务修改的块不超过数据缓冲区(data buffer catch)的10%,那么oracle 做的是fast commit cleanout。
如果是一个事务修改的块超过了10%,那么oracle就执行delayed block cleanout,还有一种情况会执行delayed block cleanout,那就是当事务还没有提交的时候,数据块已经被刷新输出到了磁盘中,当执行commit的时候,oracle不会再把这个块调入缓冲区来执行块清除,而是把这个操作留到下次touch这个块的时候。(我自己理解的时候注意到,刷新输出数据块的时候,要考虑到操作系统块和oracle的数据块是不一样大小的,所以我觉得这个cleanout操作是在数据缓冲区完成的。恩,是的,调入buffer catch完成。)
我们首先回忆一下,数据锁是数据的属性,它是存储在数据块的首部的,当一个大批量的insert 或者 update 结束后,oracle进行commit操作,虽然说这个事务已经提交了,但是还是会有一些数据块的数据没有被刷新输出到磁盘,(我之前觉得也有可能是已经刷新输出后,itl事务槽还没有被覆盖,但是我看到有人说好像只要commit完成,就会清除itl事务槽里面的信息,保留下commit的SCN号)这时的块事务槽(也就是ilt事务槽里面的数据)都还在这个数据块内,没有被删除。
当再一次接触(touch)这些数据的时候(如,select操作,数据块的下一个读者首先检查该块的事务状态是否为活动,不活动的话,修改事务的标志(flag)。这样可以避免不必要的事务读。),就会去修改这些块头,从而产生了不必要的脏数据(因为数据块被修改成了脏数据,但是数据内容又没有修改,所以成了假的脏数据,产生不必要的redo 日志)在完成下一个检查点(check point)的时候会有大量的块写入磁盘,(因为检查点就是阶段性的提交,会有很多数据块刷新出来)。
下面来看一看提交清除是怎么工作的:
在与我们的事务相关的提交列表中,oracle会记录已修改的块的列表,这些列表的大小时固定的,一个表有20个块,一个事务可能有很多个这样的列表,如果我们做强制清除,那么oracle会设置一个上限,即数据块缓冲区的10%,超过了这个上限,oracle将不会再给这个事务分配列表。commit的时候,oracle就会处理这些列表,oracle回过头来再到SGA的数据块缓冲区中访问这些块(有的块可能已经刷新输出到了磁盘,这些只能下次访问的时候再做块清除咯),如果块可用(没有别的事务正在给他加锁修改这个块)那就完成清理(当做fast commit cleanout时,oracle不会清理 Row locks lb标志位,ITL lck标志位。),删除itl事务槽里面的事务信息。
delayed block cleanout中读(如select)操作是如何工作的:
当一个进程读到一个块的时候, 必须确保一点:块上的 itl SCN号必须小于这个进程的读SCN号(如果大于,那就说明又有另一个事务对块做了操作,必须到undo段里去读原来的旧数据镜像),如果不进行块清除,itl 事务槽里也就没有提交SCN(COMMIT SCN ),这时,这个读进程先去回滚段中尝试找真实的 commit scn,也就自然的找到上次提交的scn ,再判断一下上次提交的scn和这次查询的scn谁大谁小,如果这次查询scn小于上次提交scn,则根据回滚条目进行回滚,找到那时的数据进行读取。这时,也就做了delayed block cleanout,把没有commit scn的块标上scn,下次再来查询的时候就不用跑到undo段里去了,直接根据 块首itl槽里的commit scn 判断是否需要进行回滚。还有意外,那就是如果回滚段被覆盖了咋办?只要 读scn 能在回滚段里找到了比他小的最小scn,那就将这个commit scn 标记为 回滚段中所能找到的最小scn。(就是说延迟块清除的事物肯定是早已经提交了的,没有从回滚段找到他对应的scn,说明已经被覆盖,从回滚段找个min scn就可以,这个min scn肯定是比延迟块清除事物的scn大。)
延迟清除的块在被select 时,如果读的select 的scn 比这个回滚段里面最小的scn 还要小的话(回滚段已回绕),那么在回滚段里面找不到数
据了,oracle 就没有办法判断select 的SCN 与被要清除的数据块的大小关系,于是ora-01555就出现了,这个时候oracle 就不知道数据块里面
的数据是不是是查询时刻需要的数据.
如果查询 scn 小于回滚段中所能找到的最小 scn ,并且 所需要的回滚段信息又被覆盖了,则发生1555 错误。对于为什么要用最小的scn更新itl,对于查询安全要怎么理解 :
commit 的时候没有来得及更改这个block(delay block cleanout),事后cleanout 的时候已经找不到当时对应的scn了,但是,从回滚段中可以得到一个最小的提交scn,并且该事务已经提交肯定小于这个从回滚段中还存在的最小scn。那么oracle给这个 cleanout 的事务分配一个从回滚段中找到的最小事务scn。这虽然不准确,但是是安全的,对于数据访问也不构成影响。所以叫 upper bound ,猜测的一个scn的上限。
另一种情况是delayed block cleanout,当transaction还未commit或rollback时modified block已经被写回磁盘,当发生commit时oracle并不会把block重新读入做cleanout,而是把cleanout留到下一次对此块的dml或select。当delayed cleanout时候如果undo segment header的transaction table slot还没有被覆盖,那么可以找回该事务递交的exact scn,如果slot已经被覆盖,那么将会使用undo segment header中的control scn来做为upper bound scn。
针对读一致性考虑:
每个数据头部都会记录一个提交SCN,当数据更改提交后,提交scn同时被修改,这个scn在查询时可以用来进行一致性判断.
假如:查询开始时间为T1,则在查询获取的数据块中,如果数据块的提交scn小于T1,则oracle接受该数据,如果提交的scn大于t1或者数据被锁定修改尚未记录commit scn,则oracle需要通过回滚段构造前镜像来返回结果.
关于ORA-01555的理解:
如果当一个查询触发延迟块清除的时候,ORACLE 需要去查询回滚段获得该事务提交的SCN,如果事务前镜像信息已经被覆盖,并且查询的SCN小于回滚段记录中记录的最小提交SCN信息,那么oracle将无法判断查询的scn和事物提交scn的大小.
如果当一个查询触发延迟块清除的时候,ORACLE 需要去查询回滚段获得该事务提交的SCN,如果事务前镜像信息已经被覆盖,并且查询的SCN大于回滚段记录中记录的最小提交SCN信息,则将commit 标记为 回滚段中所能找到的最小 scn(对于查询安全).
下面我们对块清除做一个总结:
我们已经讨论过块清除机制,不过这里可以做一个总结:在块清除过程中,如果一个块已被修改,下一个会话访问这个块时,可能必须查看最后一个修改这个块的事务是否还是活动的。一旦确定该事务不再活动,就会完成块清除,这样另一个会话访问这个块时就不必再历经同样的过程。要完成块清除,Oracle 会从块首部确定前一个事务所用的undo 段,然后确定从undo 首部能不能看出这个块是否已经提交。可以用以下两种方式完成这种确认:
一种方式是Oracle 可以确定这个事务很久以前就已经提交,它在undo 段事务表中的事务槽已经被覆盖。
另一种情况是COMMIT SCN 还在undo 段的事务表中,这说明事务只是稍早前刚提交,其事务槽尚未被覆盖。
再者就是undo段内事务表中有这个块对应的事务提交scn,但是没有对应的块首部commit scn,这说明这个事务需要做块清除(我自己的思想)