《我的ORACLE笔记四:关于Cleanout》


在写完上一节之后,我突然想明白了页面级CR的意义。终于可以肯定,ORACLE页面级RC和RAC是一体的设计。所以写这个系列让我自己也得到了启发,这算是意外之喜吧。

在基本协议中我们提到:“事务提交时,获得CommitSCN...更新数据时,把事务的CommitSCN赋给它”。好吧,你肯定注意到了这里面的语病,事务在更新数据时,实际上还没有获得CommitSCN。所以,在ORACLE中,当事务提交时,它实际上没有改变任何数据。那些被它更新的数据还被标识着是活跃的,锁也没有放,系统只是把获得的CommitSCN写入事务表中,并在事务表中把该事务标识成已提交。

所以我们需要在某个合适的时机,设置页面上事务的提交状态,更新ITL中的CommitSCN,并释放相应的资源(比如锁)。什么是合适的时机?一个很直观的想法,就是在其它事务再次访问该ITL时。当事务访问某行数据时,它会发现该数据目前还是活跃的,因此它会通过Xid来检查该事务在事务表中的状态,如果发现该事务实际上已经提交,那么它会把事务表中的CommitSCN复制到ITL中,将该事务标示成已提交,并释放相应的资源。

这个过程在ORACLE中被称为Cleanout (Deferred Cleanout)。这是一个不错的设计,把原本复杂的提交动作分摊到后续的各个访问中,让整个处理变得更加平滑。

但是这里有一个问题,如果在事务提交之后,很长时间之后,其更新的数据才被再次访问(比如你是一个很拽的部门,收到用户投诉后总是习惯先晾一个星期再处理)。那么再次访问时,你可能会发现,原来的那个事务在事务表中已经被重用了。你可以确认的是,这个事务肯定已经提交了,所以你仍然可以安全地释放资源,但你可能无法获取真实的CommitSCN。

这时候,你需要系统帮你选择一个近似的CommitSCN,它必须是安全的,因此它肯定不会小于真实的CommitSCN(否则你的CR版本可能不是最新的)。它也必须尽可能准确,因此你希望它尽可能接近真实的CommitSCN(否则如果你的SnapshotSCN恰好位于真实的和近似的CommitSCN之间,你会受到无妄之灾)。

你可以有很多选择,你可以UNDO事务表中该事务的slot(这点和Uba(Undo Block Address)的处理很像,注意对事务表的修改也是产生UNDO/REDO的)。当然,为了减少对回滚段的压力,你也可以利用系统其他的一些信息,比如事务表中最近一次被重用的事务的CommitSCN,比如被删除的回滚段的当前SCN。很明显,这些SCN都是安全的。所以这实际上是一个迭代的过程,你不断地获取更小的SCN,去努力逼近真实的CommitSCN,在这个过程中会出现三种情况:

1)你通过UNDO事务表找到了当时事务的状态,获得了真实的CommitSCN,这时候你可以标示该事务已提交(C)。

2)你找到了一个比你的SnapshotSCN小的近似CommitSCN,这对于你来说是足够安全的。所以你停下来,并标示这个中间状态(C|U,Upper BoundCommit),当后续有比你的SnapshotSCN更小的事务访问该ITL时,它会发现这个状态,知道这个CommitSCN是近似的,所以它会继续重复上述迭代过程。其实你也可以选择一次性构造出最小的CommitSCN,但也许在ORACLE看来,这样的Incremental方式,从概率的角度上说处理的性能更好。

3)你无法获得足够安全的CommitSCN(比如UNDO事务表所需的信息在回滚段中已经被重用),那么你会收到“Snapshot too old”。

尽管3)出现的概率很小,我们仍然希望尽可能早地将真实的CommitSCN复制到各ITL中,所以ORACLE引入了Fast Cleanout的处理。简单地说,在事务运行时,你将被你更新的数据的地址记录下来,并在事务提交之后,立即重新访问这些数据,并设置他们的CommitSCN。为了减轻对事务提交的性能影响,Fast Cleanout被设计得很轻量级,它只是标示事务的状态(U),并设置CommitSCN,它并不释放任何资源,后续的Cleanout会发现这个状态,知道CommitSCN已经被更新并且是真实准确的,所以只需要释放资源即可。所以,不要期望FastCleanout能够代替Cleanout。

我们可以列出很多情况,说明FastCleanout不一定会成功,比如页面正在被人访问,或者在你更新到提交之间,它被替换或回刷了。但这些并不重要,Fast Cleanout,它不需要必然成功(反正有Cleanout垫背),它甚至不会产生任何REDO日志。FastCleanout的价值不仅仅在于减少了3)出现的概率,更重要的是,它可以避免Cleanout过程中去访问事务表或者回滚段,在RAC环境下,提交事务和后续的访问事务可能根本不在一个实例中,因此这一点显得尤为珍贵。

整体来说,我觉得ORACLE在Cleanout的处理上堪称完美,它值得我们所有做资源后台回收的去借鉴。

后续将为您带来《我的ORACLE笔记五:关于保留空间》,敬请期待!



【连载中】冯柯《我的ORACLE笔记四:关于Cleanout》_java