块清除(Block cleanout)实际上就是移除已变更数据块中的锁相关信息。这个概念对于我们理解ORA-01555: snapshot too old错误至关重要。
在Oracle中,锁实际上就是数据的一个属性,保存在块头部中。这种实现带来一个问题:当下一次访问数据块时,我们可能必须要把它清空,即移除它的事务信息。该操作会产生重做日志并使块成为“脏”块(如果它还不是的话)。这就意味着即使是单个SELECT语句也可能产生重做日志并使下次checkpoint时将这些数据块写入磁盘。在大多数正常情况下这是不会发生的。如果你的系统中都是中小型的事务或者你已经有一个运行直接路径加载并使用DBMS_STATS分析表的数据仓库,那么你会发现数据块通常都是已清除状态的。
在Oracle中,COMMIT操作的其中一步就是重新访问那些依然在SGA中的可访问块(没有被其他人修改),然后把它们清除出去——这个过程就是所谓的提交清除(commit clear out),即在已修改块中清除事务信息。一般来说提交都会做块清除,这样后面的SELECT读操作就不必执行清除操作。只有对该块的更新操作才会真正地清除残存事务信息,因为UPDATE本身就产生重做日志,因此清除过程就不易被察觉。
了解了commit操作是如何进行块清除的,我们可以强制不发生块清除以观察它的效果。Oracle会在一个提交列表中保存多个包含已变更块的列表。每个列表的长度都是20个数据块。Oracle会按需分配任意多个块列表,但其包含的总块数不能超过BUFFER CACHE的10%。比如我们的buffer cache有3000个块,Oacle只会维护一个最多包含300个数据块的列表。一旦提交Oracle会根据列表中的块指针找到数据库并判断数据块依然可以访问。如果可以访问,则Oracle会执行一个快速的块清除。因此,只要已变更数据块的数目不超过Buffer Cache的10% 并且数据块仍在缓存中且可访问,那么Oracle就会在COMMIT操作时将其清除,否则它只会跳过它们(不做清除)。
在执行了一个大型的insert,update或delete操作(只要修改的块数超过了buffer cache的10%)之后的第一个访问这些块的查询语句就会产生重做日志并将被访问块标记为“脏块”,使得DBWR将其写回磁盘。如果Oracle不这么做(不做延迟块清除)的话,那COMMIT操作就可能会和事务处理花相同的时间。COMMIT可能必须要重新访问每个数据块,甚至是重新从磁盘中读出这些数据块。
OLTP系统块清除操作不经常发生,因为这些系统中的事务通常都是持续时间很短且修改块数也很少的。被修改的块都会来得及被执行块清除。但在数据仓库系统中,一次大规模的更新会涉及到非常多数据块,这时候块清除就是必须要考虑的问题了。诸如CREATE TABLE AS SELECT,直接路径加载或插入数据这样的操作会创建“干净”的块,而更新,传统的插入或删除操作则可能会创建那些首次访问就需要执行清除的数据块。如果按照以下顺序处理,那么你可能会受到块清除操作的影响:
-- 批量加载新数据到数据仓库中
-- 对所有加载数据执行更新操作(产生需要被清除的数据块)
-- 有别人查询这些数据
加载或修改大量数据之后,你至少要对它进行分析。它将执行块清除,这样下次查询就不再需要这么做了。执行DBMS_STATS收集统计信息也可以清除搜有块,因为它也是使用SQL语句进行查询的。
在Oracle中,锁实际上就是数据的一个属性,保存在块头部中。这种实现带来一个问题:当下一次访问数据块时,我们可能必须要把它清空,即移除它的事务信息。该操作会产生重做日志并使块成为“脏”块(如果它还不是的话)。这就意味着即使是单个SELECT语句也可能产生重做日志并使下次checkpoint时将这些数据块写入磁盘。在大多数正常情况下这是不会发生的。如果你的系统中都是中小型的事务或者你已经有一个运行直接路径加载并使用DBMS_STATS分析表的数据仓库,那么你会发现数据块通常都是已清除状态的。
在Oracle中,COMMIT操作的其中一步就是重新访问那些依然在SGA中的可访问块(没有被其他人修改),然后把它们清除出去——这个过程就是所谓的提交清除(commit clear out),即在已修改块中清除事务信息。一般来说提交都会做块清除,这样后面的SELECT读操作就不必执行清除操作。只有对该块的更新操作才会真正地清除残存事务信息,因为UPDATE本身就产生重做日志,因此清除过程就不易被察觉。
了解了commit操作是如何进行块清除的,我们可以强制不发生块清除以观察它的效果。Oracle会在一个提交列表中保存多个包含已变更块的列表。每个列表的长度都是20个数据块。Oracle会按需分配任意多个块列表,但其包含的总块数不能超过BUFFER CACHE的10%。比如我们的buffer cache有3000个块,Oacle只会维护一个最多包含300个数据块的列表。一旦提交Oracle会根据列表中的块指针找到数据库并判断数据块依然可以访问。如果可以访问,则Oracle会执行一个快速的块清除。因此,只要已变更数据块的数目不超过Buffer Cache的10% 并且数据块仍在缓存中且可访问,那么Oracle就会在COMMIT操作时将其清除,否则它只会跳过它们(不做清除)。
在执行了一个大型的insert,update或delete操作(只要修改的块数超过了buffer cache的10%)之后的第一个访问这些块的查询语句就会产生重做日志并将被访问块标记为“脏块”,使得DBWR将其写回磁盘。如果Oracle不这么做(不做延迟块清除)的话,那COMMIT操作就可能会和事务处理花相同的时间。COMMIT可能必须要重新访问每个数据块,甚至是重新从磁盘中读出这些数据块。
OLTP系统块清除操作不经常发生,因为这些系统中的事务通常都是持续时间很短且修改块数也很少的。被修改的块都会来得及被执行块清除。但在数据仓库系统中,一次大规模的更新会涉及到非常多数据块,这时候块清除就是必须要考虑的问题了。诸如CREATE TABLE AS SELECT,直接路径加载或插入数据这样的操作会创建“干净”的块,而更新,传统的插入或删除操作则可能会创建那些首次访问就需要执行清除的数据块。如果按照以下顺序处理,那么你可能会受到块清除操作的影响:
-- 批量加载新数据到数据仓库中
-- 对所有加载数据执行更新操作(产生需要被清除的数据块)
-- 有别人查询这些数据
加载或修改大量数据之后,你至少要对它进行分析。它将执行块清除,这样下次查询就不再需要这么做了。执行DBMS_STATS收集统计信息也可以清除搜有块,因为它也是使用SQL语句进行查询的。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/425993/viewspace-714559/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/425993/viewspace-714559/