延迟块清理介绍(select也会产生redo的原因)
在Oracle中数据锁(这里主要指TX类型行锁)实际上是数据的属性,存储在块首部,称之为事务槽(ITL)。
COMMIT操作的职责包括释放块上的锁,实际的释放方式即清除块上相应的事务槽,但这里存在一个性能的考量:
设想一个UPDATE大量数据的操作,因为执行时间较长,一部分已修改的块已被缓冲池flush out写至磁盘,当UPDATE操作完成执行COMMIT操作时,则需要将那些已写至磁盘的数据块重新读入,这将消耗大量I/O,并使COMMIT操作十分缓慢。
下面说说我关于上面那句话的理解:
假设有100个块在进行update,但是还没有进行commit操作,由于buffer cache快被占满了,就先将前20个块从缓冲池写到了磁盘(对应一部分已修改的块已被缓冲池flush out写至磁盘),此时这20个块的记录的状态信息是不对的还是未commit,因此在真正commit期间,还需要将这些状态信息不对的块重新读入到buffer cache里进行状态信息的修改(对应需要将那些已写至磁盘的数据块重新读入),因此会造成许多无用的I/O,而且使得commit缓慢。
因此伟大的Oracle在这里引入了延迟段清理的概念(文档号40689.1)。
它留给访问受更新影响的任何块的下一个事务(select、update、delete、insert都可以)来“整理”该块,也就是修改块的状态信息(因此称为“延迟块清理”)。
对待存在以下情况的块COMMIT操作不做块清除(而是转为延迟清除):
1、在更新过程中,被缓冲池flush out写至磁盘的块 若更新操作涉及的块超过了块缓冲区缓存的10%时,超出的部分块
2、被其他事务被顺带着从buffer刷到了磁盘上的块(buffer cache是一个整体,不会看你是哪个事务来分开来去刷到磁盘上)
虽然COMMIT放弃对这些块的块清除(block cleanout)操作,但COMMIT操作仍会修改回滚段的段头,回滚段的段头包括了段中的事务的字典,COMMIT操作将本事务转化为非ACTIVE状态。当下一次操作(事务)如SELECT,UPDATE,INSERT或DELETE访问到这些块时可能需要在读入后完成块清除,这样的操作称之为块延迟清除(deferred block cleanout)。
块延迟清除通过事务槽上的回滚段号,槽号等信息访问回滚段头的事务字典,若事务不再活跃或事务过期则完成清除块上的事务槽,事务槽清除后继续执行相应的操作。
总结来说块延迟清除是COMMIT操作的一个延续,始终是一种十分轻微的操作,且该种操作是行级的,不会使段(Segment)的属性有所改变。
块延迟清除的影响在SELECT操作过程中体现的最为明显。在select时若发生了延迟块清除,就会去修改块的状态信息,因此会产生redo。