暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

ORA-01555全面解析(下半部分)

Oracle蓝莲花 2021-04-15
2797

《ORA-01555全面解析 (上半部分)请看上一篇文章》

3.延迟块清除导致ORA-01555:块清除( block cleanout) ,即生成所修改数据库块上与“ 锁定” 有关的信息

3.1在Oracle中数据锁(这里主要指TX类型行锁)实际上是数据的属性,存储在块首部,称之为事务槽(ITL)。COMMIT操作的职责包括释放块上的锁,实际的释放方式即清除块上相应的事务槽,但这里存在一个性能的考量。

   设想一个UPDATE大量数据的操作,因为执行时间较长,一部分已修改的块已被缓冲池flush out写至磁盘,当UPDATE操作完成执行COMMIT操作时,则需要将那些已写至磁盘的数据块重新读入,这将消耗大量I/O,并使COMMIT操作十分缓慢;

   为了解决这一矛盾,Oracle使用了延迟块清除的方案,对待存在以下情况的块COMMIT操作不做块清除

3.1.1在更新过程中,被缓冲池flush out写至磁盘的块

3.1.2若更新操作涉及的块超过了块缓冲区缓存的10%时,超出的部分块。

3.1.3虽然COMMIT放弃对这些块的块清除(block cleanout)操作,但COMMIT操作仍会修改回滚段的段头,回滚段的段头包括了段中的事务的字典,COMMIT操作将本事务转化为非ACTIVE状态。

3.1.4当下一次操作如SELECT,UPDATE,INSERT或DELETE访问到这些块时可能需要在读入后完成块清除,这样的操作称之为块延迟清除(deferred block cleanout);块延迟清除通过事务槽上的回滚段号,

槽号等信息访问回滚段头的事务字典,若事务不再活跃或事务过期则完成清除块上的事务槽,事务槽清除后继续执行相应的操作。

3.1.5块延迟清除的影响在SELECT操作过程中体现的最为明显。总结来说块延迟清除是COMMIT操作的一个延续,始终是一种十分轻微的操作,且该种操作是行级的,不会使段(Segment)的属性有所改变。


3.2在Oracle中如何让SELECT查询绕过UNDO:

3.2.1原则上,在Oracle官方文档或者Asktom的时候显然会提到Oracle是不实现脏读的, 总是有undo来提供数据块的前镜像以维护一致性Consistent, 通过正常途径我们几乎不可能破坏Oracle中查询的一致性来实现脏读,这里

给各位介绍一种方法,取巧的方法:我们知道Undo有两个参数_offline_rollback_segments or _corrupted_rollback_segments ,这2个隐藏参数对于熟悉Oracle数据库异常恢复或者解决ORA-600应该不陌生,因为这2个参数是针对

    Undo存在Corruption讹误时忽略问题的有力工具。一般强制打开数据库,控制一致性读和延迟块清除,或者是强制删除某个rollback segment回滚段可以用到这两个参数。

注意:在没有活跃事务的情况下,直接读取current block,全表扫描一致性读,consistent gets只要3次 

注意:为了一致性读 上面的查询需要通过undo构造CR块,这导致consistent gets上升到 505

注意:到上面为止 虽然通过kill进程 和禁止smon 回滚dead transaction ,形成了一个不回滚的死事务 但是仍通过undo实现了一致性读


找出当前active的rollback segment的名字

注意:以上可以看到 consistent gets下降到3,服务进程读取数据块发现存在活跃事务,但是ITL指向的UNDO SEGMENTS在_corrupted_rollback_segments的列表中,所以直接认为该事务已经COMMIT提交,以便绕过UNDO

4.延迟快清除续:

4.1也许我们可以强制清除不发生来观察它的副作用, 并了解提交清除是怎么工作的。 在与我们的事务相关的提交列表中, Oracle会记录已修改的块列表。这些列表都有 20个块, Oracle会根据需要分配多个这样的列表,

直至达到某个临界点。如果我们修改的块加起来超过了块缓冲区缓存大小的10%, Oracle会停止为我们分配新的列表。 例如, 如果缓冲区缓存设置为可以缓存3,000 个块,Oracle会为我们维护最多300 个块 ( 3,000

的10%) 。 COMMIT时, Oracle会处理这些包含20个块指针的列表,如果块仍可用,它会执行一个很快的清理。所以,只要我们修改的块数没有超过缓存中总块数的10%,而且块仍在缓存中并且是可用的, Oracle

就会在COMMIT时清理这些块。否则,它只会将其忽略


4.2注意,我把DB_CACHE_SIZE设置为一个很低的值4MB,这足以放下512个8KB的块(我的块大小是8KB) 。然后创建一个表,其中每行刚好能在一个块中放下(我不会在每块里放两行) 。接下来在这个表中填入了500行,并COMMIT。我要测

量到此为止生成的 redo量。然后运行一个 SELECT,它会访问每个块,最后测量这个SELECT 生成的redo量


4.3这里首先做一个结论,SELECT是会生成redo。不仅如此,它还会把这些修改块“ 弄脏”,导致DBWR再次将块写入磁盘,这是因为延迟快清除的原因,来看下面的试验:

注意:我的表,每个块中一行(我的数据库中块大小为8KB) 。现在测量读数据是生成的redo量:

统计信息

注意:这个SELECT在处理期间生成了大约3.15Mredo。这表示对T进行全表扫描时修改了3.15M 的块首部。 DBWR会在将来某个时间把这些已修改的块写回到磁盘上。现在,如果再次运行这个查询:

注:最后一笔查询没有生成redo,块是干净的。

结论:

如果执行一个大的INSERT(如上所述) 、 UPDATE或DELETE,这种块清除行为的影响最大,它会影响数据库中的许多块(缓存中10%以上的块都会完成块清除) 。你会注意到,在此之后,第一个“ 接触”块的

查询会生成少量的redo,并把块弄脏,如果DBWR已经将块刷新输出或者实例已经关闭,可能就会因为这个查询而导致重写这些块,并完全清理缓冲区缓存。对此你基本上做不了什么。这是正常的,也在意料之

中。如果Oracle不对块完成这种延迟清除,那么COMMIT的处理就会与事务本身一样长。 COMMIT必须重新访问每一个块,可能还要从磁盘将块再次读入(它们可能已经刷新输出) 

------------------------------------

5.上面我们讲了太多关于逻辑读,一致性读,延迟块,以及Undo相关的工作原理和内容,接下来,我们继续URA-01555做解释,也许我们该弄明白那些操作可能会生成最多或者最少的undo,通常我的经验是insert 生成的undo最小,其次为

update,生成undo最多的应该是delete操作,因为delete操作Oracle必须把整个行的前镜像记录到undo segment中,其实ORA-01555本身涉及不到数据块坏块或者数据丢失或者非常严重的数据库行为,唯一一影响就是如果数据库报错,查询

将无法进行而已,其实无非两个原因,Undo segment太小,不足以满足正常的工作,另一个原因比较重要的就是块清除。


ORA-01555错误的几种解决方案, 一般来说可以采用下面的方法:

1.适当地设置参数UNDO_RETENTION(要大于执行运行时间最长的事务所需的时间) 。可以用V$UNDOSTAT来确定长时间运行的查询的持续时间。另外,要确保磁盘上已经预留了足够的空间,使undo段能根据所请求的UNDO_RETENTION增大。

2.使用手动undo管理时加大或增加更多的回滚段。这样在长时间运行的查询执行期间,覆盖undo数据的可能性就能降低。

3.减少查询的运行时间(调优) 。如果可能的话,这绝对是一个好办法,所以应该首先尝试这种方法。这样就能降低对undo段的需求,不需求太大的undo段。

4.收集相关对象的统计信息。这有助于避免前面所列的第三点。

5.INSERT会导致块清除( block cleanout) ,所以需要在大批量UPDATE或大量加载之后以某种方式收集统计信息

----------------------------------

6.LOB大字段引起的ORA-01555:

ORA-01555: snapshot too old: rollback segment number with name "" too small

6.1 LOB数据不使用撤消段来保留读取的一致性前镜像。 在任何DML之前,旧版本都存储在LOB Segments本身中。

LOB段的ORA-01555通常有两种情况:

查询访问损坏的LOB段

读取LOB数据的一致前镜像在LOB段中不可用。 这是由于LOB段的PCTVERSION RETENTION属性设置错误造成的。


6.2针对第一种情况,在LOB列上解析ORA-1555的第一步是检查是否有损坏。 这是最常见的情况:

--创建一个虚拟表来存储损坏的LOB的所有rowid

create table corrupt_lobs (corrupt_rowid rowid, err_num number);

--执行以下PL SQL块来识别损坏的行

注:输出结果为以下内容

--执行如下脚本剔除坏块:

--当然也可以通过数据泵方式实现:

--直到11gR1,LOB段的RETENTION属性将等于UNDO_RETENTION参数。 因此,我们建议将UNDO_RETENTION设置为数据库中查询的最大持续时间。 检查maxquerylen

7.ORA-01555深入分析:

我们知道,当Oracle更新数据块时,会在回滚段(UNDO Segment)记录下这一更新动作。并且产生一个Cleanout SCN,在回滚段中,会产生对应的Transaction ID以及相应的数据记录镜像。并在对应的数据记录上,产生锁标志。在事务提交

(commit)前,会在数据块的头部记录下这个Cleanout SCN(Csc)号、Undo Block Address(Uba)和Transaction ID(Xid);并且在在对应Interested Transaction List(Itl)中设置锁标志,记录这个事务在这数据块中产生的锁的数目;同时

在对应修改的数据记录上打上行级锁标志,并映射到对应的Itl去。当提交时,并不会一一清除掉所有锁标志,而是给对应的Itl打上相应标志,告诉后面访问该数据块的事务,相应的事务已经提交。这就叫做快速提交(Fast Commit)。

而后面访问该数据块的的事务就先检查锁标志和对应的事务状态,如果发现前面的事务没有提交,并且要访问的数据记录被锁住了,就被阻塞;否则就清除相应的锁标志,并提交自己的锁标志,再重复以上动作。这就事延迟块清除。


而如果前面的事务在提交之前buffer cache中的脏数据已经被DBwn进程写回,那么Itl中的事务标志就不会被更新,并且数据块的Itl列表也不会记录下事务的Commit SCN。后面的事务或查询语句访问该数据块时,为了检测是否需要进行一致性读

(如果数据块的Itl中记录的提交事务的Commit SCN大于当前访问该数据块的SCN,则需要进行一致性读),就需要通过Undo Block Address和Transaction ID到回滚段的事务信息表中去检查前面事务的状态和它的Commit SCN,确定是否做一致性读,

最后将前面事务在该数据块上的标志做一次Cleanout。


来看下面的案例分析:

--创建获取rowid的自定义函数

--创建测试表

--插入测试数据

--该存储过程可以打印该表的列属性字段,索引分布情况,这里面后期愿意写自己写吧,比如dba_tab_statistics,dba_tab_col_statistics,dba_index的blevel,leaf,cluster_factory内容,我只是为了满足需求,随便写了一个

具体trace文件内容在下面一章节介绍:

可以看到,目前事务标志是----,这是为什么呢?请注意,上面过程在commit之前进行了buffer cache flush,也就是说,oracle进程在改写数据块时,该事务还未提交,也未回滚,所以标志为空。

而假如将buffer cache flush放在commit之后,该标致就为--U-,即事务已经提交,但是相应的锁并没有清除,因为对应lock是1,表示刚才我们修改了一条记录。再看每条记录中的行级锁对应Itl条目lb:都是0x1。即Itl中的第一条,

------------------------------------------------

8.SELECT语句在读T_600_DEMO5表的一个块时,发现表上有活动事务,这是由于之前的事务没有清除所致。ORACLE根据数据块中ITL的XID检查事务表,这时会有2种情况:


    8.1 XID对应的事务表中的记录仍然存在并发现事务已经提交,可以得到事务准确的提交SCN(commit scn),称为SCN3,等于SCN1。很显然,由于查询的时刻SCN2晚于事务提交的时刻SCN1,那么不需要构造一致性读块。

    8.2 XID对应的事务表中的记录已经被重用,这个时候仍然表明表明事务已经被提交。那么这个时候,Oracle没办法准确地知道事务的提交时间,只能记录为这样一个事实,事务提交的SCN小于其UNDO段的事务表中最近一次

重用的事务记录的SCN(即这个事务表最老的事务SCN)。这里称这个SCN为SCN4。

    8.3如果表过大,SELECT COUNT(*)的时间过长,那么我们可以用下面的代码将表分成多个段,进行分段查询,此脚本来自网上一位大师手笔

-----------------------------

                                                                                  《点亮梦想.拒绝平庸》

                                                                          600团队(QQ群:851604218)                  


文章转载自Oracle蓝莲花,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论