• Oracle ORA-01555 快照过旧 说明


    ORA-01555 快照过旧,是数据库中非经常见的一个错误。比方当我们的事务须要使用undo来构建CR块的时候,而此时相应的undo 已经不存在了。 这个时候就会报ORA-01555的错误。

    ORA-01555错误在Oracle 8i及之前的版本号最多。从9i開始的undo自己主动管理,至如今的10g、11g中的undo auto tuning,使得ORA-01555的错误越来越少。可是这个错误,仍然不可避免。

    Oracle undo 管理 http://www.linuxidc.com/Linux/2011-09/42706.htm

    1. 出现ORA-01555错误。通常有2种情况:

    (1)SQL语句运行时间太长。或者UNDO表空间过小。或者事务量过大,或者过于频繁的提交,导致运行SQL过程中进行一致性读时。SQL运行后改动的前镜像(即UNDO数据)在UNDO表空间中已经被覆盖,不能构造一致性读块(CR blocks)。  这样的情况最多。

    (2)SQL语句运行过程中。訪问到的块,在进行延迟块清除时,不能确定该块的事务提交时间与SQL运行開始时间的先后次序。 这样的情况非常少。

    2. 1种情况解决的办法:

    (1)添加UNDO表空间大小

    (2)添加undo_retention 时间,默认仅仅有15分钟

    (3)优化出错的SQL,降低查询的时间,首选方法

    (4)避免频繁的提交

    有关Undo 的很多其它信息,參考我的文章:

    Oracle undo 表空间管理 http://www.linuxidc.com/Linux/2011-07/38006.htm

    3. 另外一种情况描写叙述

     

    在块清除过程中,假设一个块已被改动,下一个会话訪问这个块时,可能必须查看最后一个改动这个块的事务是否还是活动的。

    一旦确定该事务不再活动。就会完毕块清除。这样还有一个会话訪问这个块时就不必再历经相同的过程。

    要完毕块清除,Oracle 会从块首部确定前一个事务所用的undo 段(ITL,然后确定从undo 首部能不能看出这个块是否已经提交。

     

    能够用下面两种方式完毕这样的确认:

    一种方式是Oracle 能够确定这个事务非常久曾经就已经提交,它在undo 段事务表中的事务槽已经被覆盖。

    还有一种情况是COMMIT SCN 还在undo 段的事务表中,这说明事务仅仅是稍早前刚提交,其事务槽尚未被覆盖。

     

    当满足下面条件时。就会从defered clean 收到ORA-01555的错误:

    首先做了一个改动并COMMIT。块没有自己主动清理(即没有自己主动完毕“提交清除”,比如改动了太多的块,在SGA 块缓冲区缓存的10%中放不下)。

    其它会话没有接触这些块。并且在我们这个“倒霉”的查询(稍后显示)命中这些块之前,不论什么会话都不会接触它们。

    開始一个长时间执行的查询。这个查询最后会读当中的一些块。这个查询从SCN t1 開始,这就是读一致SCN,必须将数据回滚到这一点来得到读一致性。

     

    開始查询时。上述改动事务的事务条目还在undo 段的事务表中。

    查询期间。系统中运行了多个提交。运行事务没有接触运行已改动的块(假设确实接触到,也就不存在问题了)。

     

    因为出现了大量的COMMITundo 段中的事务表要回绕并重用事务槽(ITL)。

    最重要的是,将循环地重用原来改动事务的事务条目。另外,系统重用了undo 段的区段,以避免对undo 段首部块本身的一致读。

    此外,因为提交太多,undo 段中记录的最低SCN 如今超过了t1(高于查询的读一致SCN)。假设查询到达某个块,而这个块在查询開始之前已经改动并提交。就会遇到麻烦。正常情况下,会回到块所指的undo 段,找到改动了这个块的事务的状态(换句话说,它会找到事务的COMMIT SCN)。

    假设这个COMMIT SCN 小于t1,查询就能够使用这个块。假设该事务的COMMIT SCN 大于t1,查询就必须回滚这个块。只是,问题是,在这样的特殊的情况下,查询无法确定块的COMMIT SCN 是大于还是小于t1对应地,不清楚查询是否能使用这个块映像。这就导致了ORA-01555 错误。

     

    大批量的UPDATE 或INSERT 会导致块清除(block cleanout),所以在大批量UPDATE 或大量载入之后使用DBMS_STATS收集相关对象的统计信息,载入之后完毕这些对象的清理。

     

    关于块清除这块,在<Oracle 10g 编程艺术> 一书中有更具体的说明。

     

     

    老熊blog上关于defered Clean的场景说明:

    (1)有事务大量改动了A表的数据,或者A表的数据尽管被事务少量改动。可是一部分改动过的块已经刷出内存并写到了磁盘上。随即事务提交。提交时刻为SCN1。而提交时有数据块上的事务没有被清除。

    (2)在SCN2时刻。開始运行SELECT查询A表,对A表进行全表扫描,并且A表非常大。也可能是其它情况。比方是小表,可是是一个游标方式的处理过程。而处理过程中又非常耗时。注意。这里SCN2SCN1之间可能相隔了非常远。从时间上来说,甚至可能有数十天。

    无论怎么样,这SCN1SCN2时间之间,系统中存在大量的事务,使得UNDO表空间的块以及UNDO段头的事务表所有被重用过。

    (3)SELECT语句在读A表的一个块时。发现表上有活动事务,这是因为之前的事务没有清除所致。ORACLE依据数据块中ITLXID检查事务表。这时会有2种情况:

    (A)XID相应的事务表中的记录仍然存在并发现事务已经提交,能够得到事务准确的提交SCN(commit scn),称为SCN3。等于SCN1。非常显然。因为查询的时刻SCN2晚于事务提交的时刻SCN1,那么不须要构造一致性读块

    (B)XID相应的事务表中的记录已经被重用,这个时候仍然表明表明事务已经被提交。那么这个时候,Oracle没办法准确地知道事务的提交时间,仅仅能记录为这样一个事实。事务提交的SCN小于其UNDO段的事务表中近期一次重用的事务记录的SCN(即这个事务表最老的事务SCN)。这里称这个SCN为SCN4。

     

    (4)SCN4可能远小于SCN2。那是由于事务非常早之前就已经提交。也可能SCN4大于SCN2。这是由于SELECT语句运行时间非常长,同一时候又有大量的事务已经将事务表重用。对于后者。非常显然,Oracle会觉得该事务的提交时间可能在SELECT開始运行之后。这里为什么说可能,是由于ORACLE仅仅能推断出事务是在SCN4之前提交的,并非就刚好在SCN4提交。而此时,利用UNDO BLOCK进行一致性读数据的构造也非常可能失败,由于UNDO BLOCK非常可能已经被覆盖,特别是SCN1远小于SCN2的情况下。

                在这样的情况下。ORA-01555错误就会出现。

               

                对这样的因为表上存在未清除的事务,同一时候导出时间过长。UNDO段头的事务表被所有重用,ORACLE在查询到有未清除事务的块时不能确定事务提交时间是否早于导出(查询)開始时间,报ORA-01555错误。

                老熊blog上有2个解决方法,一是提高SQL 性能,还有一个是清除表上的事务。即延时块清楚(Defered Clean)。

     这种方法也非常easy。就是select。

                SQL>SELECT /*+ FULL(A) */ COUNT(*) FROM BIG_TABLE A;

     

                SELECT COUNT(*)。速度显然大大高于SELECT *,所需的时间也更短。出现ORA-01555错误的可能性就很低了。

     

    注意:

                (1)使用FULL HINT,以避免查询进行索引高速全扫描,而不是对表进行全表扫描。

                (2)这里不能为了提高性能而使用PARALLEL(并行),測试表明,在表上进行并行查询。以DIRECT READ方式读取表并不会清除掉表上的事务。

               

                假设表过大。SELECT COUNT(*)的时间过长,那么我们能够用以下的代码将表分成多个段,进行分段查询。

    /* Formatted on 2011/6/29 19:18:40 (QP5 v5.163.1008.3004) */

    SELECT DBMS_ROWID.rowid_create (1,

                                    oid1,

                                    fid1,

                                    bid1,

                                    0)

              rowid1,

           DBMS_ROWID.rowid_create (1,

                                    oid2,

                                    fid2,

                                    bid2,

                                    9999)

              rowid2

      FROM (SELECT a.*, ROWNUM rn

              FROM (  SELECT chunk_no,

                             MIN (oid1) oid1,

                             MAX (oid2) oid2,

                             MIN (fid1) fid1,

                             MAX (fid2) fid2,

                             MIN (bid1) bid1,

                             MAX (bid2) bid2

                        FROM (SELECT chunk_no,

                                     FIRST_VALUE (

                                        data_object_id)

                                     OVER (

                                        PARTITION BY chunk_no

                                        ORDER BY

                                           data_object_id, relative_fno, block_id

                                        ROWS BETWEEN UNBOUNDED PRECEDING

                                             AND     UNBOUNDED FOLLOWING)

                                        oid1,

                                     LAST_VALUE (

                                        data_object_id)

                                     OVER (

                                        PARTITION BY chunk_no

                                        ORDER BY

                                           data_object_id, relative_fno, block_id

                                        ROWS BETWEEN UNBOUNDED PRECEDING

                                             AND     UNBOUNDED FOLLOWING)

                                        oid2,

                                     FIRST_VALUE (

                                        relative_fno)

                                     OVER (

                                        PARTITION BY chunk_no

                                        ORDER BY

                                           data_object_id, relative_fno, block_id

                                        ROWS BETWEEN UNBOUNDED PRECEDING

                                             AND     UNBOUNDED FOLLOWING)

                                        fid1,

                                     LAST_VALUE (

                                        relative_fno)

                                     OVER (

                                        PARTITION BY chunk_no

                                        ORDER BY

                                           data_object_id, relative_fno, block_id

                                        ROWS BETWEEN UNBOUNDED PRECEDING

                                             AND     UNBOUNDED FOLLOWING)

                                        fid2,

                                     FIRST_VALUE (

                                        block_id)

                                     OVER (

                                        PARTITION BY chunk_no

                                        ORDER BY

                                           data_object_id, relative_fno, block_id

                                        ROWS BETWEEN UNBOUNDED PRECEDING

                                             AND     UNBOUNDED FOLLOWING)

                                        bid1,

                                     LAST_VALUE (

                                        block_id + blocks - 1)

                                     OVER (

                                        PARTITION BY chunk_no

                                        ORDER BY

                                           data_object_id, relative_fno, block_id

                                        ROWS BETWEEN UNBOUNDED PRECEDING

                                             AND     UNBOUNDED FOLLOWING)

                                        bid2

                                FROM (SELECT data_object_id,

                                             relative_fno,

                                             block_id,

                                             blocks,

                                             CEIL (sum2 / chunk_size) chunk_no

                                        FROM (SELECT                   /*+ rule */

                                                    b.data_object_id,

                                                     a.relative_fno,

                                                     a.block_id,

                                                     a.blocks,

                                                     SUM (

                                                        a.blocks)

                                                     OVER (

                                                        ORDER BY

                                                           b.data_object_id,

                                                           a.relative_fno,

                                                           a.block_id)

                                                        sum2,

                                                     CEIL (

                                                        SUM (a.blocks) OVER ()

                                                        / &trunks)

                                                        chunk_size

                                                FROM dba_extents a, dba_objects b

                                               WHERE a.owner = b.owner

                                                     AND a.segment_name =

                                                            b.object_name

                                                     AND NVL (a.partition_name,

                                                              '-1') =

                                                            NVL (b.subobject_name,

                                                                 '-1')

                                                     AND b.data_object_id

                                                            IS NOT NULL

                                                     AND a.owner = UPPER ('&owner')

                                                     AND a.segment_name =

                                                            UPPER ('&table_name'))))

                    GROUP BY chunk_no

                    ORDER BY chunk_no) a);

     

    该SQL 在运行时须要输入几个參数:

                trunks: 表示把表分成的段数

                owner: 表的全部者

                table_name: 表名

    关于Rowid 的说明,參考文章: Oracle Rowid 介绍 http://www.linuxidc.com/Linux/2011-07/37998.htm



  • 相关阅读:
    虚函数/纯虚函数/抽象类/接口/虚基类
    泛型编程
    C++解题报告 : 迭代加深搜索之 ZOJ 1937 Addition Chains
    C++题解:Matrix Power Series ——矩阵套矩阵的矩阵加速
    C++矩阵加速经典题目:Warcraft III 守望者的烦恼 [vijos 1067]
    C++数论板题(弹药科技):Lengendre定理和欧拉函数
    Java并发工具包提供了哪些并发工具类
    Mac OS X系统深入了解--系统文件结构篇(二)
    Mac OS X系统深入了解--系统文件结构篇(一)
    Java Web(一) Servlet详解!!
  • 原文地址:https://www.cnblogs.com/slgkaifa/p/6940235.html
Copyright © 2020-2023  润新知