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

ORA-01555全面解析

Oracle蓝莲花 2021-04-15
2366

1.本篇文档主要讨论什么情况下会产生ORA-01555快照过旧情况,以及如何避免ORA-01555报错情况,以及通过案例去分析ORA-01555,进行问题的讨论与分析。

1.1 oracle官方术语undo segment和scn以及延迟块清除说明,以下是Oracle官方对撤销段以及读一致性的解释说明。

为管理多版本的读取一致性模型,当表同时被查询和更新时, 数据库必须创建一组读取一致的数据。 Oracle 数据库通过使用撤销数据实现了这一目标。每当用户修改了数据, Oracle 数据库会创建撤销条目,并写入到撤销段

撤销段。 撤销段包含由未提交事务或最近提交的事务所更改的数据的旧值。因此, 同一数据在各个不同时间点上的多个版本,都可以存在于数据库中。数据库可以使用在不同时间点的数据快照, 来提供数据读取一致视图,

并实现非阻塞查询读取一致性在单实例和 Oracle RAC环境中都可以得到保证。 Oracle RAC 使用一种称为缓存融合的传输机制, 将一个数据库实例中的数据块读取一致映像传

送到另一个实例中

系统更改号 (SCN) 是一个由 Oracle 数据库使用的逻辑、 内部的时间戳。SCN 按数据库中发生的事件排序,以满足在事务 ACID 属性的需要。 Oracle数据库使用 SCN 来标记某个位置, 在其之前的所有更改都被认为已写到磁

盘上, 以避免在恢复过程中应用不必要的重做。 数据库还使用 SCN 来标记一个其数据不存在重做信息的点,以便恢复过程可以在该点停止


SCN 是按单调递增的顺序发生的。 Oracle 数据库可以像使用时钟一样使用 SCN,因为一个 SCN 观察值指示一个逻辑时间点, 而其重复观察值相比之 前会相同或更大。 若一个事件的 SCN 比另一个事件的 SCN 低,则它在数据

库中发生在一个更早的时间。几个事件可以共享相同的 SCN, 这意味着他们在数据库中同时发生。


每个事务都有一个 SCN。 例如, 如果事务更新了一行,则数据库记录此更新发生时的 SCN。 此事务中其他修改具有相同的 SCN。当事务提交时,数据库将为此提交记录一个 SCN


Oracle 数据库在系统全局区域( SGA) 中递增 SCN。事务中修改数据时, 数据库会将一个新的 SCN 写入到分配给事务的撤销数据段。 然后日志写入器进程立即将事务的提交记录写入在线重做日志。提交记录具有事务的唯一

SCN。 Oracle 数据库还使用 SCN 作为其实例恢复和介质恢复机制的一部分。

1.4 undo参数说明:

注:

undo_management表示回滚段管理方式,值可以为MANUAL/AUTO。9i中默认是MANUAL,10g中默认是AUTO,从9i后,回滚段就以表空间的形式管理,并且支持系统自动管理回滚段。一个回滚表空间上可以创建多个回滚段,一个数据库可以创建

多个回滚表空间。但是,一个实例(Instance)只能使用一个回滚表空间。

undo_retention表示:设置回滚段中的被提交或回滚的数据强制保留时间,单位是秒这个参数和1555错误有非常大的关系。但是,需要提醒的是,并不是回滚段中的数据超过这个时间以后就会被清除掉,而是等到后面事务产生的回滚数据

覆盖掉“超期”数据。所以这就是为什么我们往往看到系统的回滚表空间占有率始终是100%的原因了

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

2.对oracle 逻辑读概念分析和工作原理理解:

2.1逻辑io是大量消耗cpu的操作,在awr load profile指标明确标注逻辑读在两次快照范围内生成情况,Logical Read,单位为:单位  次数*块数,逻辑读高则DB CPU往往高,也往往可以看到latch: cache buffer chains等待,

同时对应awr报告中,% Blocks changed per Read指标每次逻辑读导致数据块变化的比率,如果’redo size’, ‘block changes’ ‘pct of blocks changed per read’三个指标都很高,则说明系统正执行大量insert/update/delete

同时还可以关注awr报告中session logical reads,这里,简单介绍一下db block gets 、consistent gets 以及 session logical reads的关系:

db block gets=db block gets direct+ db block gets from cache

consistent gets = consistent gets from cache+ consistent gets direct

consistent gets from cache= consistent gets – examination  + else

consistent gets – examination==>指的是不需要pin buffer直接可以执行consistent get的次数,常用于索引,只需要一次latch get

同时还可以关注awr报告中的Service Statistcs指标,有具体Logical的产生情况,单位为每KB,表示本服务所消耗的逻辑读。同时还可以到Segments by Logical Reads 指标中关注Logical Reads :该数据段上发生过的逻辑读,

针对逻辑读还可以关注Global Messaging Statistics指标,全局通信统计信息会记录逻辑读的情况。在集群环境还可以关注Global Cache Efficiency Percentages指标中的Buffer access – local cache %,这个指标表示数据块

从本地缓存命中占会话总的数据库请求次数的比例,以上为通过awr报告定位逻辑读产生的指标关注,见招拆招,,回到刚才的话题我们在2.2中继续介绍。

2.2逻辑读被分为一致性读和当前读两种情况,在v$sysstat中有consistent gets和db block gets两个指标的具体参数消耗情况,一致性读针对select,而当前读则是针对dml和ddl语句,在shared pool工作原理中,注意,select

语句只会申请一致性读模式的buffer pin lock,而dml ddl会申请当前读模式和修改模式的buffer pin lock.对于dml的当前读模式和select的一致性读模式他们并不会互相阻塞,但是dml和ddl的当前读模式是会互相阻塞的,

这样做的目的是还处在定位要修改行位置的dml操作,并不会阻塞select,但是却可以阻塞其他dml操作。来看以下简单的试验:

2.2.1 session A完成如下操作:

注意:

一致性读除了consistent gets还有诸如consistent gets examination (fastpath),consistent gets examination,consistent gets direct,consistent gets from cache,consistent gets pin性能指标,其中consistent gets examination

就是cache buffer chain latch进行的逻辑读,索引无论是根块,枝块还是叶子到表级别的blocks都是基于共享的cache buffer chain latch方式进行逻辑读。当我们做如下操作会发现

session A:

注意:

pl/sql匿名块筛选where rn=1,rn上有唯一索引,每次只会查询出一行,因此,如上面所赘述的内容,每次的cache buffer chain latch都将是共享的latch,而且每次逻辑读需要申请一次cache buffer chain latch,因此

得出一个结论,基于索引每次读其实都是consistent gets examination,因为consistent gets没有发生任何变化


2.3如何减少逻辑读,这是一个很大概念,我们从工作原理做一个简要分析,为了帮助600成员能够了解oracle内部工作原理:

注意:

2.3.1,在不改变sql语句的情况下是否可以降低逻辑读?

我们说oracle如何在内存中找到一个buffer ,读buffer时候,首先会申请一个cache buffer chain latch,然后是buffer pin lock,在buffer pin lock的作用下对应的进程将buffer 中的行读取到pga内存,然后释放buffer pin

lock 关于buffer 的内容本文档不扩展赘述,假设我们要读取7499 ALLEN内容,oracle要从buffer cache中做内存复制将7499 ALLEN的数据复制到pga中,基于pro oracle c++中memcpy接口函数,它的作用是将N个字节大小的内存从

一个区域复制到另外一个区域。


2.3.2假设一个oracle blocks有700行,一次逻辑读会从块中复制多少行到pga,简单说就是一次性的逻辑读能够读取多少行。这时候就需要考虑oracle预读取的特性也就是prefetch特性,缺省情况下oracle预读取的行数为15行,

针对当前会话我们可以做如下操作

SQL> show arraysize;

arraysize 15

SQL> set arraysize 100;

SQL>

当我们批量预抓取对应数据行到pga中,服务器进程将持有cache buffer chain latch然后在latch的保护下获得一致性模式的buffer pin,接着,服务器进程将会等待sql* net message to client等待事件,然后客户端开始

接受行源生成器返回的数据,再然后服务器进程开始复制数据,700行数据从buffer 读入pga内存,然后服务器进程释放buffer pin lock,当然释放buffer pin lock仍在还需要cache buffer chain latch的内存机制保护下进行

这中间设计到sdu的概念,本文档对oracle sdu概念不做赘述,感兴趣600成员可以关注oracle net services administrator guide中关于optimizing performance中关于session data unit的概念介绍。来看以下试验:

注:这个简单的测试结果不做赘述,大家都能看得懂,我们主要是解释Oracle逻辑读工作原理浅析以及buffer到pga内存复制的过程。

2.4一致性读(Consistent Get)可以说是产生1555错误的主要原因,在标准SQL中,为了防止并发事务中产生脏读,就需要通过加锁来控制。这样就会带来死锁、阻塞的问题,即时是粒度最小的行级锁,也无法避免这些问题,

为了解决这一矛盾。Oracle充分利用的回归段,通过会滚段进行一致性读取,即避免了脏读,又大大减少了系统的阻塞、死锁问题。下面就看下Oracle是如何实现一致性读的。


当Oracle更新数据块(Data Block Oracle中最小的存储单位)时,会在两个地方记录下这一更新动作。一个是重做段(Redo Segment),是用于数据库恢复(Recover)用的。一个是回滚段(UNDO Segment),而回滚段是用于事务回滚(Rollback)的(

我们只关心回滚段了)。并在数据块头部标示出来是否有修改数据。一个语句在读取数据快时,如果发现这个数据块是在它读取的过程中被修改的(即开始执行读操作时并没有被修改),就不直接从数据块上读取数据,而是从相应的回滚段条目中读取数据。

这就保证了最终结果应该是读操作开始时的那一时刻的快照(snapshot),而不会受到读期间其他事务的影响。这就是Oracle的一致性读,也可以叫做多版本(Multi-Versioning)


Oracle的多版本模型会使用undo段数据依照语句或事务开始时的原样来重建块(究竟是语句还是事务,这取决于隔离模式) 。如果必要的undo信息不再存在,你就会收到一个ORA-01555: snapshot too old错误消息,查询也不会完成。所以,如果像前面的那个例子

一样,你一边在读取表,一边在修改这个表,就会同时生成查询所需的undo信息。 UPDATE生成了undo信息,你的查询可能会利用这些undo信息来得到待更新数据的读一致视图。如果提交了所做的更新,就会允许系统重用刚刚填写的undo段空间。

如果系统确实重用了undo段空间,擦除了旧的undo数据(查询随后要用到这些undo信息) ,你就有大麻烦了。 SELECT会失败,而UPDATE也会中途停止。这样就有了一个部分完成的逻辑事务,而且可能没有什么好办法来解决。


通常我的经验是,在执行一次,一般都会好,工作原理在这里不做解释,来看一个小案例:

注意:收到了这个错误。应该指出,这里向查询增加了一个索引提示以及一个WHERE子句,以确保随机地读取这个表(这两方面加在一起,就能使基于代价的优化器读取按索引键“ 排序”的表) 。通过索引来处理

表时,往往会为某一行读取一个块,可是我们想要的下一行又在另一个块上。最终,我们确实会处理块1上的所有行,只不过不是同时处理。假设块1可能包含OBJECT_NAME以字母A、 M、 N、 Q和Z开头的所有行

的数据。这样我们就会多次命中(读取)这个块,因为我们在读取按OBJECT_NAME排序的数据,而且可能有很多行的OBJECT_NAME以A~M之间的字母开头。 由于我们正在频繁地提交和重用undo空间,最终再次访

问一个块时,可能已经无法再回滚到查询开始的那个时间点,此时就会得到这个错误。


这是一个特意构造的例子,纯粹是为了说明如何以一种可靠的方式发生这个错误。 UPDATE语句正在生成undo信息。我只能用一个很小的undo表空间(大小为2MB) 。这里多次在 undo段中回绕,因为undo

段要以一种循环方式使用。每次提交时,都允许Oracle覆盖前面生成的undo数据。最终,可能需要某些已生成的undo数据,但是它已经不在了(即已经被覆盖) ,这样就会收到ORA-01555错误

3.延迟块清除导致ORA-01555:块清除( block cleanout) ,即生成所修改数据库块上与“ 锁定” 有关的信息(请关注我们600团队明天的分享哦)

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

                                                                                     《点亮梦想.拒绝平庸》

                                                                                        QQ群:851604218


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

评论