合 Oracle Undo系列
- 常用SQL
- 一个DML语句的处理过程描述
- 基本概念和名词解释
- Undo回滚段简介
- Undo回滚段的作用
- Undo回滚段的类型
- Undo段存储的内容:不同的DML操作,UNDO BLOCK中保存的前映像内容
- 回滚段的数量规划
- 回滚段的查询
- 块清除
- 延时块清除
- Undo表空间
- 系统回滚段(System Rollback Segment)与延迟回滚段(Deferred Rollback Segment)
- 为什么会“ORA-01555”
- 错误记录的日志文件
- 为什么会发生这个错误
- 如何诊断
- Undo其它问题
- undo表空间不足的问题
- 单个session占用大量undo,导致数据库性能急剧下降的问题总结
- enq: US – contention等待事件
- ORA-600 [4xxx]错误
- undo数据文件丢失的处理示例
- 总结
- 参考
常用SQL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | --1、系统段 2、非系统段 3、表空间脱机后的defered段 SELECT d.segment_type, COUNT(1) FROM dba_segments d GROUP BY d.segment_type; select * from dba_segments d where d.segment_type in ('TYPE2 UNDO','ROLLBACK') ; select * from dba_rollback_segs; select * from dba_undo_extents; select * from v$transaction; select * from v$rollstat; select * from v$rollname; select * from dba_extents d where d.segment_name='_SYSSMU25_17381587$'; select * from v$undostat; ---mount状态可查 --ssolderrcnt : snapshot too old error count select status,count(*) from dba_rollback_segs group by status; SELECT d.TABLESPACE_NAME, d.STATUS, SUM(bytes) / 1024 / 1024 FROM dba_undo_extents d GROUP BY d.TABLESPACE_NAME, d.status ORDER BY d.TABLESPACE_NAME; SELECT r.tablespace_name, r.status "Status", r.segment_name "Name", s.extents "Extents", TO_CHAR((s.bytes / 1024 / 1024), '99999990.000') "SizeM", s.segment_type FROM dba_rollback_segs r, dba_segments s WHERE r.segment_name = s.segment_name AND s.segment_type IN ('ROLLBACK', 'TYPE2 UNDO') ORDER BY r.tablespace_name, 5 DESC; ----估算undo需要的 SELECT (UR * (UPS * DBS)) AS "Bytes" FROM (SELECT value AS UR FROM v$parameter WHERE name = 'undo_retention'), (SELECT undoblks/((end_time-begin_time)*86400) AS UPS FROM v$undostat WHERE undoblks = (SELECT MAX(undoblks) FROM v$undostat)), (SELECT block_size AS DBS FROM dba_tablespaces WHERE tablespace_name = (SELECT UPPER(value) FROM v$parameter WHERE name = 'undo_tablespace')); -------------------- 已用 set line 9999 select s.sid, s.serial#, s.sql_id, v.usn, segment_name, r.status, v.rssize / 1024 / 1024 mb From dba_rollback_segs r, v$rollstat v, v$transaction t, v$session s Where r.segment_id = v.usn and v.usn = t.xidusn and t.addr = s.taddr order by segment_name; SET ECHO OFF SET FEEDBACK 6 SET HEADING ON SET LINESIZE 180 SET PAGESIZE 50000 SET TERMOUT ON SET TIMING OFF SET TRIMOUT ON SET TRIMSPOOL ON SET VERIFY OFF COLUMN status FORMAT a9 HEADING 'Status' COLUMN name FORMAT a30 HEADING 'Tablespace Name' COLUMN type FORMAT a15 HEADING 'TS Type' COLUMN extent_mgt FORMAT a10 HEADING 'Ext. Mgt.' COLUMN segment_mgt FORMAT a10 HEADING 'Seg. Mgt.' COLUMN ts_size FORMAT 9,999,999,999,999 HEADING 'Tablespace Size' COLUMN used FORMAT 9,999,999,999,999 HEADING 'Used (in bytes)' COLUMN free FORMAT 9,999,999,999,999 HEADING 'Free (in bytes)' COLUMN pct_used FORMAT 999 HEADING 'Pct. Used' SELECT d.status status , d.tablespace_name name , d.contents type , d.extent_management extent_mgt , d.segment_space_management segment_mgt , NVL(a.bytes, 0) ts_size , NVL(a.bytes - NVL(f.bytes, 0), 0) used , NVL((a.bytes - NVL(f.bytes, 0)) / a.bytes * 100, 0) pct_used FROM sys.dba_tablespaces d , ( select tablespace_name, sum(bytes) bytes from dba_data_files group by tablespace_name ) a , ( select tablespace_name, sum(bytes) bytes from dba_free_space group by tablespace_name ) f WHERE d.tablespace_name = a.tablespace_name(+) AND d.tablespace_name = f.tablespace_name(+) AND d.tablespace_name like '%UNDO%' ORDER BY 2; --数据文件管理参数: db_create_file_dest CREATE TABLESPACE test DATAFILE SIZE 10m; alter database datafile 3 resize 5G; alter database tempfile 1 resize 2G; alter database datafile 3 autoextend off; alter database tempfile 1 autoextend off; ① 分析数据文件块,转储数据文件n的块m alter system dump datafile n block m; ② 分析日志文件 alter system dump logfile logfilename; ③ 分析控制文件的内容 alter session set events 'immediate trace name CONTROLF level 10'; ④ 分析所有数据文件头 alter session set events 'immediate trace name FILE_HDRS level 10'; ⑤ 分析日志文件头 alter session set events 'immediate trace name REDOHDR level 10'; ⑥ 分析系统状态,最好每10分钟一次,做三次对比 alter session set events 'immediate trace name SYSTEMSTATE level 10'; ⑦ 分析进程状态 alter session set events 'immediate trace name PROCESSSTATE level 10'; ⑧ 分析Library Cache的详细情况 alter session set events 'immediate trace name library_cache level 10'; -------------- 重建undo表空间 create undo tablespace undotbs2 datafile '+DATA' size 100m reuse autoextend off; alter system set undo_tablespace=undotbs2; drop tablespace undotbs1 including contents and datafiles; create undo tablespace undotbs1 datafile '+DATA' size 100m reuse autoextend off; alter system set undo_tablespace=undotbs1; alter database datafile 3 autoextend off; alter database tempfile 1 autoextend off; create undo tablespace undotbs2 datafile '+DATA' size 5M; alter system set undo_tablespace=undotbs2; alter tablespace undotabs2 retention guarantee; select t.rowid,dbms_rowid.rowid_relative_fno(rowid) fno,dbms_rowid.rowid_block_number(rowid) bno, t.owner,t.object_name from t ; alter system dump datafile 4 block 6643; select * from vw_mysession_lhr; UBA : undo block address select * from v$obsolete_parameter; alter database create datafile 3 as '/u03/app/oracle/oradata/ora1024g/undotbs01.dbf' size 50m; ------- undo alter system set "offline_rollback_segments"=true scope=spfile; alter system set "offline_rollback_segments"=false scope=spfile; ---默认 alter system reset "_offline_rollback_segments" scope=spfile sid='*'; *.offline_rollback_segments=('SYSSMU154_3691636531$','SYSSMU155_3686385895$','SYSSMU156_3796802683$','SYSSMU157_2723916652$','SYSSMU158_1435464080$') offline_rollback_segment='SYSSMU3$' _newsort_enabled --排序 -------- 如果undo为recover状态的话还需要加如下参数 alter system set "corrupted_rollback_segments"='SYSSMU1$','SYSSMU2$','SYSSMU3$','SYSSMU4$','SYSSMU5$','SYSSMU6$','SYSSMU7$','SYSSMU8$','SYSSMU9$','_SYSSMU10$' scope=spfile; alter system reset "_corrupted_rollback_segments" scope=spfile sid='*'; alter system set "corrupted_rollback_segments"='SYSSMU1$','SYSSMU2$','SYSSMU3$','SYSSMU4$','SYSSMU5$','SYSSMU6$','SYSSMU7$','SYSSMU8$','SYSSMU9$','SYSSMU10$','SYSSMU11$','SYSSMU12$','SYSSMU13$','SYSSMU14$','SYSSMU15$','SYSSMU16$','SYSSMU17$','SYSSMU18$','SYSSMU19$','_SYSSMU20$' scope=spfile; *.corrupted_rollback_segments=('SYSSMU10_3271578125','SYSSMU11_125382609','SYSSMU1_1240252155','SYSSMU12_2245433549','SYSSMU13_3242268464','SYSSMU14_44821983','SYSSMU15_1872739176','SYSSMU16_1376564431','SYSSMU17_1839632768','SYSSMU18_3088942417','SYSSMU19_2867910983','SYSSMU20_948290921','SYSSMU2_111974964','SYSSMU3_4004931649','SYSSMU4_1126976075','SYSSMU5_2968973961','SYSSMU6_2060978448','SYSSMU7_4222772309','SYSSMU8_3612859353','_SYSSMU9_2370500926') drop rollback segment "drop rollback segment"; drop rollback segment "_SYSSMU154_3691636531$"; ---- 查询undo段 strings /u01/app/oracle/oradata/ora11g/system01.dbf | grep _SYSSMU | sort -u >/tmp/system.txt more /tmp/system.txt 注意:通过system01.dbf查出了,正在使用的undo segment,以上按使用时间做了排序,注意只选择那些排在最前面的(相同回滚段);默认每个undo tablespace 会应用10个undo segments。 ------- 不能创建undo文件(ORA-01178错误),无备份的情况下采用隐含参数启动数据库 set line 9999 col name format a100 select file#, name,status,enabled from v$datafile; select * from v$recover_file; alter system set undo_management=manual scope=spfile; alter database datafile 3 offline; alter system set undo_tablespace=SYSTEM scope=spfile; alter system set "offline_rollback_segments"=true scope=spfile; ! strings '/u01/app/oracle/oradata/orcltest/system01.dbf' | grep _SYSSMU | sort -u alter system set "corrupted_rollback_segments"='SYSSMU3_4004931649$','SYSSMU3_4160240979$','SYSSMU34_2573821980$','SYSSMU35_3476245049$','SYSSMU36_353473384$','SYSSMU37_682878819$','SYSSMU38_2521239011$','SYSSMU39_1467520375$','SYSSMU40_753689919$','SYSSMU4_1126976075$','SYSSMU4_348804819$','SYSSMU5_2968973961$','SYSSMU5_4011504098$','SYSSMU6_2060978448$','SYSSMU6_3654194381$','SYSSMU7_4222772309$','SYSSMU7_894058185$','SYSSMU8_3612859353$','SYSSMU8_87803851$','SYSSMU9_2370500926$','SYSSMU9_2370500926$','SYSSMU9_3945653786$' scope=spfile; shutdown immediate; startup mount; alter database open; select segment_name,status,tablespace_name from dba_rollback_segs; drop tablespace UNDOTBS1; create undo tablespace undotbs1 datafile '/u01/app/oracle/oradata/orcltest/undotbs01.dbf' size 50m autoextend on; alter system set undo_tablespace=UNDOTBS1 scope=spfile; alter system set undo_management=auto scope=spfile; alter system reset "offline_rollback_segments" scope=spfile sid='*'; alter system reset "corrupted_rollback_segments" scope=spfile sid='*'; shutdown immediate; startup ---注意回滚段的命名规范: 11g: SYSSMU1_1189172979$、 _SYSSMU2_1189172979$ 。。。SYSSMU10_1189172979$ 10g、9i:SYSSMU1$、SYSSMU2$、SYSSMU3$ 。。。。SYSSMU10$ 8i 为rollbackspace 即RBS空间:RBS0、RBS1、RBS2 。。。。RBS6 7.3 :RB1、RB2.。。。。RB6 |
一个DML语句的处理过程描述
update undotest set object_type='VIEW' where object_type='PROCEDURE';
检查shared pool中是否存在相同的语句,如果存在,重用执行计划,执行扫描运算,如果不存在,执行硬解析生成执行计划
根据执行计划中的扫描运算,检查undotest表中的相关数据块是否存在buffer cache中,如果不存在则读取到内存中
检查数据块中符合object_type='PROCEDURE'条件的记录,如果没有符合条件的行记录,则结束语句,如果存在则进入下一步
以当前模式(current)获取符合object_type='PROCEDURE'条件的数据块,准备进行更新
在回滚表空间的相应回滚段头的事务表上分配事务槽,这个动作需要记录redo日志
从回滚段数据块上创建object_type='PROCEDURE'的前映像数据,这个动作也要记录redo日志
修改object_type='VIEW' ,这是DML操作的数据变更,而需要记录redo日志
用户提交时,在redo日志中记录提交信息,将回滚段头上的事务表和回滚段数据块标记为非活动,清除修改数据块上的事务信息(也可能延迟清除)。同时必须确保整个事务的redp日志写到磁盘上的日志文件
注意:如果最后用户回滚了事务,oracle从回滚段中将前映像数据提取出来,覆盖被更新的数据块。这个回滚动作本身也需要产生redo日志,因此,我们要知道回滚的代价非常昂贵。
基本概念和名词解释
UBA: Undo block address
RBA: Redo block address
Dba: Data block address
Rdba: Root dba
Xid: Transaction ID
ITL: Interested Transaction List 保存在数据块的头部(事务信息部分),包含XID,UBA,LCK,FLG等重要信息
Transaction Identifiers
Transaction identifiers (XID) uniquely identify a transaction within the system; they are used within the Interested Transaction List (ITL) of the data block.
A transaction identifier consists of:
Undo segment number 即v$rollname中的usn
Transaction table slot number 对应回滚段头中回滚事务表的第几条记录
Sequence number or wrap#
XID = usn# . slot# . wrap#
Undo Block Address
The undo block address (UBA) uniquely identifies the undo block for a given transaction; it is found within the ITL of the data block.
A UBA consists of:
Data block address (DBA) of the block 前映像undo 块地址
The sequence number of the block 序列号
The record number within the block undo记录的开始地址(针对该块)
UBA = DBA. seq#. rec#
Undo回滚段简介
回滚段用于存放数据修改之前的值(包括数据修改之前的位置和值)。回滚段的头部包含正在使用的该回滚段事务的信息。一个事务只能使用一个回滚段来存放它的回滚信息,而一个回滚段可以存放多个事务的回滚信息。
Undo回滚段的作用
Undo主要有以下几个作用:
(1)事务回滚(Rollback Transaction)
当一个事务修改表中数据的时候,该数据修改前的值(即前镜像,Before Image)会被存放在Undo段中,当用户回滚事务(ROLLBACK)时,Oracle将会利用在数据块ITL槽中记录的Undo块地址(Undo Block Address,Uba),然后找到相应的Undo块,接着利用其中的Undo数据(即前镜像)来将修改的数据恢复到原来的值,从而实现对事务所作的改变进行回滚。
(2)事务恢复(Transaction Recovery)
实例恢复(Instance Recovery)的第一阶段称为前滚(Rolling Forward)或者缓存恢复(Cache Recovery),第二阶段称为回滚(Rolling Back)或者事务恢复。前滚和回滚是Oracle数据库实例发生意外崩溃,重新启动的时候,由SMON进行的自动恢复的过程。所谓的前滚,是应用Redo来恢复Buffer Cache的数据,将Buffer Cache恢复到Crash之前状态,所以此时Buffer Cache中既有崩溃时已经提交但还没有写入数据文件的脏块,还有事务被突然终止而导致的既没有提交又没有回滚的事务的脏块(也就是没有COMMIT,但是DBWn已经将改变的数据刷新到底层磁盘)。前滚完成之后就可以确保联机Redo日志中所有已提交的事务操作的数据写回到数据文件中。接下来,前滚之后,任何未提交的更改必须被撤消,而回滚是在数据库做完前滚操作后并打开数据库的情况下完成的,SMON会利用Undo信息将未提交的事务全部进行回滚。具体来说,SMON进程在完成前滚后,查看Undo段头(Undo段的第1个数据块)记录的事务表(每个事务在使用Undo块时,首先要在该Undo块所在的Undo段头记录一个条目,该条目里记录了该事务相关的信息,其中包括是否提交等),将其中既没有提交也没有回滚,而是在实例崩溃时被异常终止的事务全部回滚。
(3)提供一致性读(Consistent Read)
Oracle是一个多用户系统,当一个会话开始读取数据还未结束读取之前,可能会有其他会话修改了该会话将要读取的数据。如果会话读取到修改后的数据,那么就会造成数据的不一致,出现了脏读(Dirty Read)。所以,一致性读是相对于脏读而言的。在Oracle中,一致性读是通过Undo来实现的,一致性读就是为了保证数据的一致性。在一般情况下,普通查询都是一致性读。
举例来说,假设某个表T中有1W条记录,获取所有记录需要15分钟时间。当前时间为9点整,某用户A发出一条查询语句:“SELECT * FROM T;”,该语句在9点15分时执行完毕。当用户A执行该SQL语句到9点10分的时候,另外一个用户B发出了一条DELETE命令,将T表中的最后一条记录删除并提交了。那么到9点15分时,A用户将返回多少条记录?如果返回9999条记录,那么说明发生了脏读;如果仍然返回1W条记录,那么说明发生了一致性读。很明显,在9点钟那个时间点发出查询语句时,表T中确实有1W条记录,只不过由于I/O的相对较慢,所以才会花15分钟完成所有记录的检索。对于Oracle数据库来说,必须提供一致性读,并且该一致性读是在没有阻塞用户的DML操作的前提下实现的。
那么Undo数据是如何实现一致性读的呢?在Oracle数据库中的Buffer Cache中的数据块上都会有最后一次修改数据块时的SCN。如果一个事务需要修改数据块中数据,那么会先在回滚段中保存一份修改前数据和SCN的数据块,然后再更新Buffer Cache中的数据块的数据及其SCN,并标识其为“脏”数据。当其它进程读取数据块时,会先比较数据块上的SCN和自己发出SQL语句时刻的SCN,分为以下两种情况:
① 如果该数据块头部的ITL槽上记录的SCN大于自己查询时刻的SCN,那么表示该块被更新过,此时就要借助Undo块了。在该数据块头部的ITL槽上记录了对应的Undo块的地址(Uba),根据Uba就可以找到对应的Undo块。如果发现该Undo块的ITL槽的SCN号也较大,证明该Undo块也不可用,那么需要在该块的ITL槽上继续寻找上一个Undo块地址,层层递归,最终找到SCN号比发出查询的SCN号小的Undo块,将该Undo块中的被修改前的数据取出,从而构建出发出SQL语句时刻的数据块内容,这样的数据块叫做CR(Consistent Read)块。但是在查找的过程中,可能会发现当前Undo块里记录的ITL槽的SCN号比上一个Undo块里记录的SCN号还要大。这种情况说明由于事务被提交或回滚,导致当前找到的Undo块里的数据已经被其它事务覆盖了,于是就无法再找出小于等于发出查询时的那个时间点的SCN号,这时Oracle就会抛出一个非常经典的错误--ORA-1555,也就是snapshot too old(快照过旧)的错误。对于DELETE来说,其Undo信息就是INSERT,也就是说该构建出来的CR块中就插入了被删除的那条记录。
② 如果数据块头部的ITL槽(事务槽)上记录的SCN小于等于自己查询时刻的SCN,那么分为两种情况:第一,若被查询的块上没有活动的事务,则表示该块没有被更新过,是可用的,可以直接读取该数据块上的数据;第二,若被查询的块上有活动的事务,则需要找Undo的前镜像数据。
(4)实现闪回功能
闪回功能中的闪回查询(Flashback Query)、闪回版本查询(Flashback Version Query)、闪回事务查询(Flashback Transaction Query)和闪回表(Flashback TABLE)都是基于Undo表空间中的回滚信息实现的。
Undo回滚段的类型
回滚段可分为系统回滚段和非系统回滚段, 其中非系统回滚段又分为PUBLIC回滚段和PRIVATE回滚段。
回滚段:
1 系统回滚段
2 非系统回滚段:
- PUBLIC回滚段
- PRIVATE回滚段
系统回滚段用于处理涉及系统的CATALOG的事物(比如大多数的DDL), 它位于SYSTEM表空间, 由于只有SYSTEM表空间可以随时保持可用, 因此,不要把SYSTEM回滚段放在其他的表空间中.
原则1: 系统回滚段应放在SYSTEM表空间中, 并且应该永远保持ONLINE状态.
PUBLIC回滚段对于数据库的所有实例(INSTANCE)都是可用的, 除非将其显式设置为OFFLINE.
PRIVATE回滚段是指对于数据库的某个实例是私有的, 为了使用PRIVATE回滚段, 某个实例应当在其INITsid.ORA的ROLLBACK_SEGMENTS中标明所有要使用的PRIVATE回滚段, 或通过使用ALTER ROLLBACK SEGMENT XXX ONLINE来使用某一个回滚段.
建议1: 在单实例系统中,建议将所有回滚段设为PUBLIC.
建议2: 在多实例系统中(如OPS,RAC), 建议将每个实例的PRIVATE回滚段放置到访问比较快的本地设备上.
Undo段存储的内容:不同的DML操作,UNDO BLOCK中保存的前映像内容
Redo中只会记录少量信息,这些信息足以重演事务;同样Undo中也只记录精简信息,这些信息足以撤销事务。具体来说:
对于INSERT操作,回滚段只需要记录插入记录的ROWID,如果回退,那么只需将该记录根据ROWID删除即可;
对于UPDATE操作,回滚段只需要记录被更新字段的旧值即可(前镜像),回退时通过旧值覆盖新值即可完成回滚;
对于DELETE操作,Oracle则必须记录整行的数据,在回滚时,Oracle通过一个反向操作恢复删除的数据。
总结一下:对于相同数据量的数据操作,通常INSERT产生最少的Undo,UPDATE产生的Undo居中,而DELETE操作产生的Undo最多。所以,当一个大的DELETE操作失败或者回滚,总是需要很长的时间,并且会有大量的Redo生成。所以通常在进行大规模数据删除操作时,推荐通过分批删除分次提交,以减少对于回滚段的占用和冲击。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | 1.开启事务 SQL > create table tt as select object_name,object_id from dba_objects; Table created. SQL > insert into tt values( 'dddddd', 1233456); --执行insert操作 1 row created. 2.检查事务信息和其他一些事务信息 SQL > select HEADER_FILE,HEADER_BLOCK from dba_segments a where segment_name = 'TT'; HEADER_FILE HEADER_BLOCK - - - - - - - - - - - - - - - - - - - - - - - 4 810 SQL > SELECT 2 dbms_rowid.rowid_object(rowid) object_id, 3 dbms_rowid.rowid_relative_fno(rowid) REL_FNO, 4 dbms_rowid.rowid_block_number(rowid) BLOCKNO, 5 dbms_rowid.rowid_row_number(rowid) ROWNO,rowid,object_name 6 FROM tt WHERE object_name = 'dddddd'; OBJECT_ID REL_FNO BLOCKNO ROWNO ROWID - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OBJECT_NAME - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 73430 4 1150 0 AAAR7WAAEAAAAR +AAA dddddd SQL > select xidusn,xidslot,xidsqn,ubablk,ubafil,ubarec,to_char(start_scnw, 'xxxxxxxx') start_scnw, 2 to_char(start_scnb, 'xxxxxxxx') start_scnb, start_scnb +start_scnw *power( 2, 32) start_scn 3 from v$ transaction; XIDUSN XIDSLOT XIDSQN UBABLK UBAFIL UBAREC START_SCNW START_SCNB START_SCN - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 5 21 960 1466 3 7 0 f5520 1004832 根据上面查询获取的事务信息,dump insert事务undo 段头和undo 块 SQL > select name from v$rollname where usn = 5; NAME - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _SYSSMU5_1527469038$ 3.dump undo 段头和undo 块 SQL > oradebug setmypid; Statement processed. SQL > oradebug tracefile_name /u01 /oracle /diag /rdbms /test /test /trace /test_ora_604.trc SQL > alter system dump undo header '_SYSSMU5_1527469038$'; System altered. SQL > alter system dump datafile 3 block 1466; System altered. UNDO BLK: xid: 0x0005. 015. 000003c0 seq: 0x12c cnt: 0x7 irb: 0x7 icl: 0x0 flg: 0x0000 Rec Offset Rec Offset Rec Offset Rec Offset Rec Offset - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0x01 0x1fa4 0x02 0x1f48 0x03 0x1ecc 0x04 0x1e70 0x05 0x1df4 0x06 0x1d90 0x07 0x1d20 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * Rec #0x7 slt: 0x15 objn: 73430(0x00011ed6) objd: 73430 tblspc: 4(0x00000004) * Layer: 11 ( Row) opc: 1 rci 0x00 Undo type: Regular undo Begin trans Last buffer split: No Temp Object: No Tablespace Undo: No rdba: 0x00000000Ext idx: 0 flg2: 0 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uba: 0x00c005b9. 012c. 2d ctl max scn: 0x0000.000f4ed4 prv tx scn: 0x0000.000f4ee0 txn start scn: scn: 0x0000.000f53a9 logon user: 85 prev brb: 12584373 prev bcl: 0 KDO undo record: KTB Redo op: 0x03 ver: 0x01 compat bit: 4 (post - 11) padding: 1 op: Z KDO Op code: DRP row dependencies Disabled --DRP操作完成回滚 xtype: XA flags: 0x00000000 bdba: 0x0100047e hdba: 0x0100032a --bdba表示block address hdba 代表sgment header address itli: 1 ispac: 0 maxfr: 4858 tabn: 0 slot: 0(0x0) - - - - - - - - - 这个slot表示这一行数据在数据块中的行地址(行序列) |
其中bdba表示file 4,block 1150 ,hdba表示file 4,block 810 与我们上面查询出来的地址相符。
我们可以看到,undo块中并没有直接保存rowid信息,但是oracle完全可以根据上面的几个信息(bdba,slot,objd)定位回滚时需要删除的具体数据,因此对于Insert操作,ORACLE只需保留上述信息,即可完成回滚操作
回滚段的数量规划
对于OLTP系统,存在大量的小事务处理,一般建议:
数量多的小回滚段;每四个事务一个回滚段;每个回滚段不要超过十个事务。
对于批处理,一般建议:
少的大回滚段;每个事务一个回滚段。
回滚段的查询
1 查询数据库的的回滚段情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | SYS@ora11g> select owner,segment_id,segment_name,tablespace_name,status from dba_rollback_segs; OWNER SEGMENT_ID SEGMENT_NAME TABLESPACE_NAME STATUS ------ ---------- ------------------------------ ------------------------------ ---------------- SYS 0 SYSTEM SYSTEM ONLINE PUBLIC 14 _SYSSMU14_2191089452$ UNDOTBS1 ONLINE PUBLIC 13 _SYSSMU13_3035791580$ UNDOTBS1 ONLINE PUBLIC 12 _SYSSMU12_970593693$ UNDOTBS1 ONLINE PUBLIC 11 _SYSSMU11_3082648207$ UNDOTBS1 ONLINE PUBLIC 10 _SYSSMU10_1197734989$ UNDOTBS1 ONLINE PUBLIC 9 _SYSSMU9_1650507775$ UNDOTBS1 ONLINE PUBLIC 8 _SYSSMU8_517538920$ UNDOTBS1 ONLINE PUBLIC 7 _SYSSMU7_2070203016$ UNDOTBS1 ONLINE PUBLIC 6 _SYSSMU6_1263032392$ UNDOTBS1 ONLINE PUBLIC 5 _SYSSMU5_898567397$ UNDOTBS1 ONLINE PUBLIC 4 _SYSSMU4_1254879796$ UNDOTBS1 ONLINE PUBLIC 3 _SYSSMU3_1723003836$ UNDOTBS1 ONLINE PUBLIC 2 _SYSSMU2_2996391332$ UNDOTBS1 ONLINE PUBLIC 1 _SYSSMU1_3724004606$ UNDOTBS1 ONLINE 15 rows selected. |
2 查看系统回滚段基本信息
1 | select segment_name,tablespace_name,bytes,next_extent from dba_segments where segment_type='ROLLBACK'; |
从上面仅仅是查询到回滚段的基本信息,要了目前各个回滚段的动态信息,还要查询V$ROLLNAME和V$ROLLSTAT视图。V$ROLLNAME视图只存放各回滚段的编号和名字,V$ROLLSTATS存放各个回滚段当前的情况信息。要想得到每个回滚段的信息,就要查询两个表的信息才能得到。如:
1 | select s.usn,n.name,s.extents,s.optsize,s.hwmsize,s.status from v$rollstat s, v$rollname n where s.usn=n.usn |
3 查看回滚段的使用情况,哪个用户正在使用回滚段的资源(当提交或回滚后资源释放):
1 | SELECT s.username, u.name FROM v$transaction t, v$rollstat r, v$rollname u, v$session s WHERE s.taddr = t.addr AND t.xidusn = r.usn AND r.usn = u.usn ORDER BY s.username; |
4 回滚段当前活动的事物(事务提交或回滚后自动清空)
1 | SELECT s.username,t.xidusn,t.ubafil,t.ubablk,t.used_ublk FROM v$session s,v$transaction t; |
5 分析 UNDO 的使用情况
1 | SELECT TABLESPACE_NAME,STATUS,TRUNC(SUM(BLOCKS) * 8 / 1024) AS "Size M",COUNT(*) Undo_Extent_Num FROM DBA_UNDO_EXTENTS GROUP BY TABLESPACE_NAME, STATUS; |
6 监控undo表空间
1 2 | SELECT BEGIN_TIME, END_TIME, UNDOTSN, UNDOBLKS, TXNCOUNT,MAXCONCURRENCY AS "MAXCON" FROM V$UNDOSTAT; |
7 查询是否有回滚段的争用
1 2 3 4 | select * from v$waitstat; SELECT name, waits, gets, waits/gets "Ratio" FROM v$rollstat a, v$rollname b WHERE a.usn = b.usn; |
8 查看回滚段的统计信息:
1 2 | SELECT n.name, s.extents, s.rssize, s.optsize, s.hwmsize, s.xacts, s.status FROM v$rollname n, v$rollstat s WHERE n.usn = s.usn; |
9 查询回滚段的事务回退率
1 2 3 4 | transaction rollbacks/(transaction rollbacks+user commits) select name,value from v$sysstat where name in('user commits','transaction rollbacks'); |
10 查询回滚段在使用,扩展,回缩的时候extent在循环的次数
1 | select usn,wraps from v$rollstat; |
11 查询回滚段收缩的情况
1 | select usn,optsize,shrinks from v$rollstat; |
块清除
块清除(Block Cleanout)是指清除存储在数据块头部与锁相关的信息,其实质是在清除块上的事务信息,包括数据的行级锁和ITL信息(包括提交标志、SCN等),块清除不需要生成Redo日志。Oracle的块清除有两种:快速块清除(Fast Commit Cleanout)和延时块清除(Delayed Block Cleanout)。
通过命令“alter system dump undo header '回滚段名称';”可以将Undo段头信息dump出来,可以很明显地看到事务表(TRN TBL)信息,其中,状态(state)为10代表活动事务,状态(state)为9表示INACTIVE。Dba列表示该事务对应的Undo Block Dba地址。
每个事务处理只分配给一个Undo段,一个Undo段可以同时服务多个事务处理。UPDATE事务的内部流程如下所示:
① 首先当一个事务开始时,需要在Undo段事务表上分配一个事务槽。
② 在数据块头部获取一个ITL事务槽,该事务槽指向Undo段头的事务槽。
③ 在修改数据之前,需要记录前镜像(Before Image)信息,这个信息以Undo Record的形式存储在回滚段中,回滚段头事务槽指向该记录。
④ 锁定修改行,修改行锁定位(lb-lock byte)指向ITL事务槽。
⑤ 进行数据修改。
在提交事务的时候,如果被修改过的数据块仍然在Buffer Cache之中,那么Oracle可以清除ITL信息,这叫作快速块清除(Fast Block Cleanout),也叫提交清除(Fast Commit Cleanout)。快速块清除还有一个限制,当修改的块数量超过Buffer Cache约10%,则对超出部分不再进行快速块清除。
在提交事务的时候,如果被修改过的数据块已经被写回到数据文件上(或大量修改超出Buffer Cache的10%的部分),再次读出该数据块进行修改,显然成本过于高昂,对于这种情况,Oracle选择延迟块清除(Delayed Block Cleanout),即在提交的时候只会清理Undo Segment Header中的事务表信息,而Data Block上的事务标志不会清除,等到下一次访问该Block时再来清除ITL锁定信息,这就是延迟块清除。Oracle通过延迟块清除来提高数据库性能,加快提交操作。如果Oracle不对块完成这种延迟清除,那么COMMIT的处理可能会很长,COMMIT必须重新访问每一个块,可能还要从磁盘将块再次读入内存。在一个OLTP系统中,可能很少看到这种情况发生,因为OLTP系统的特点是事务都很短小,只会影响为数不多的一些块。
如果执行一个大的INSERT、UPDATE或DELETE,会影响数据库中的许多块,那么就有可能在此之后,第一个“接触”块的查询会做延迟块清除,从而生成Redo日志,所以,SELECT语句也有可能会产生Redo日志。
如果有如下的操作,那么可能会受到块清除的影响:
将大量新数据批量加载到数据仓库中;
在刚刚加载的所有数据上运行UPDATE(产生需要清理的块);
让别人查询这些数据
因此,建议在批量加载了数据后,通过运行DBMS_STATS实用程序来收集统计信息,就能自然的完成块清除工作。Oracle提供了一个内部事件(10203事件)可以用来跟踪数据库的块清除操作,可以通过以下命令设置:
1 | ALTER SYSTEM SET EVENTS '10203 trace name context forever'; |
延时块清除
Delayed logging block cleanout(延时块清除)是ORACLE用来提高写性能的一种机制: 当修改操作(INSERT/UPDATE/DELETE)发生时, ORACLE将原有的内容写入回滚段, 更新每个数据块的头部使其指向相应的回滚段, 当该操作被COMMIT时, ORACLE并不再重新访问一遍所有的数据块来确认所有的修改, 而只是更新位于回滚段头部的事务槽来指明该事务已被COMMIT, 这使得写操作可以很快结束从而提高了性能接下来的任何访问该操作所修改的数据的操作会使先前的写操作真正生效, 从而访问到新的值. Delayed logging block cleanout 虽然提高了性能,但却可能导致ORA-01555. 这种情况下, 在OPEN/FETCH前对该表做全表扫描(保证所有的修改被确认)会有所帮助.
为了保证事务的回退和满足多用户的CR, oracle引入了undo 机制, 由于undo是循环使用的,在一个事务完成过程中,它与redo相互配合,其中undo在一次事务中需要完成以下工作:
(1) Transaction 开始前回滚段获取一个ITL(事务槽),分配空间, 记录事务信息
(2) Transaction 提交后,redo完成记录,同时还清除回滚段的事务信息 包括行级锁,ITL信息(commit 标志,SCN等)
清除这些事务段的信息的过程就叫做块清除, 在完成块清除时, 我们本事务修改的数据块就会存在两种可能
(1) 所有的数据块还保存在 buffer cache 中
(2)部分数据块或者是全部数据块由于LRU管理已经被刷出了buffer cache。
oracle为了考虑到块清除的成本,以及性能,会作以下两种方式的块清除处理:
(1)快速块清除(fast block cleanout), 当事务修改的数据库全部保存在buffer cache 并且修改数据块的数据量没有超过 cache buffer 的 10%,快速清除事务信息。
(2)延迟块清除(delayed block cleanout) 当修改的数据块的阀值超过10% 或者本次事务相关的数据块已经被刷出了 buffer cache, oracle 会下次访问此block 时再清除事务信息。
Undo表空间
Undo信息存储在Undo段中,Undo段又存储在Undo表空间中。Undo表空间仅用于Undo段(在Undo表空间中不能创建其它段类型,例如表、索引等),只能与单个实例相关联。在任意指定时间,一个给定的实例只能有一个表空间是当前可写Undo表空间。Undo表空间是永久的、本地管理的表空间(具有自动区分配),它们由数据库自动进行管理。
Redo和Undo可以从以下几个方面进行区分:
Undo | Redo | |
---|---|---|
记录内容 | 如何还原更改 | 如何重新生成更改 |
用于 | 事务回滚、实例恢复、一致性读、闪回 | 向前滚动数据库更改,例如数据库恢复(在实例恢复中利用Redo做前滚;在介质恢复中利用归档日志做RECOVER恢复操作实现完全或不完全恢复)、LogMiner、DG、OGG等 |
存储于 | Undo段 | Redo日志文件 |
避免 | 在多用户系统中读取不一致 | 数据丢失 |
Oracle Undo段中区3种状态(DBA_UNDO_EXTENTS的STATUS列):ACTIVE、EXPIRED和UNEXPIRED:
ACTIVE即未提交的Undo信息(活动):表示事物还在活动,该值对应的Undo段的DBA_ROLLBACK_SEGS.STATUS一定是ONLINE状态,一旦没有活动的事务在使用Undo段,那么对应的Undo段就变成OFFLINE状态。ACTIVE状态的Undo区不会被覆盖。
EXPIRED即过期的Undo信息(过期):表示事务已经提交且超过了UNDO_RETENTION指定时间,该状态可以被覆盖使用。
UNEXPIRED即提交的Undo信息(未过期):表示事务已经提交但是还没有超过UNDO_RETENTION指定时间,该状态可以被覆盖使用。
关于Undo表空间有如下几个参数:
1 2 3 4 5 6 7 8 9 | SYS@orclasm > show parameter undo NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ _undo_autotune boolean FALSE undo_management string AUTO undo_retention integer 900 undo_tablespace string UNDOTBS1 |
- UNDO_RETENTION参数指定已提交的Undo信息要保留多长时间(单位为秒),默认为900秒(即15分钟)。但是该值不是绝对的,也就是说,如果有其它事务需要Undo空间,而Undo空间出现不足时,这些信息仍然会被覆盖。只有当表空间设置为GUARANTEE时,才能确保已提交的数据保留UNDO_RETENTION参数设置的时间。RETENTION GUARANTEE是表空间属性而不是初始化参数,此属性只可使用SQL命令行语句来更改。通过更改Undo表空间来保证保留时间的语法是:
1 2 | SQL>ALTER TABLESPACE undotbs1 RETENTION GUARANTEE; |
要将有保留时间保证的还原表空间返回到其常规设置,请使用以下命令:
1 2 | SQL>ALTER TABLESPACE undotbs1 RETENTION NOGUARANTEE; |
查询保留时间状态:
1 2 | SQL> SELECT RETENTION FROM DBA_TABLESPACES WHERE TABLESPACE_NAME LIKE 'UNDO%'; |
如果设置UNDO_RETENTION为0,那么Oracle启用自动调整UNDO_RETENTION(auto tuning of undo_retention)以满足最长运行查询的需要,在告警日志文件中可以看到如下信息:
Autotune of undo retention is turned on.
可以通过设置“"_undo_autotune"=FALSE”来显式的关闭自动调整UNDO_RETENTION功能。
UNDO_MANAGEMENT参数用于指定Undo数据的管理方式,分为自动Undo管理(AUM,Automatic Undo Management)和手动Undo管理(MUM,Manual Undo Management)。如果要使用AUM,那么必须设置为AUTO;如果要使用MUM,那么必须设置为MANUAL。在使用AUM时,Oracle会使用Undo表空间管理Undo数据;在使用MUM时,Oracle会使用回滚段管理Undo数据。需要注意的是,在使用AUM时,如果没有配置初始化参数UNDO_TABLESPACE,那么Oracle会自动选择第一个可用的Undo表空间存放Undo数据,如果没有可用的Undo表空间,那么Oracle会使用SYSTEM回滚段存放Undo记录,并在告警文件中记录警告。
UNDO_TABLESPACE 在使用AUM时,该参数用于指定实例所要使用的Undo表空间。在RAC结构中,因为一个Undo表空间不能由多个实例同时使用,所以必须为每个实例配置一个独立的Undo表空间。
有关Undo表空间的恢复请参考:http://blog.itpub.net/26736162/viewspace-1458787/,这里不再详述。有关Undo表空间常用的几个视图如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | SELECT OWNER,SEGMENT_NAME,BYTES/1024/1024 MB FROM DBA_SEGMENTS WHERE TABLESPACE_NAME='UNDOTBS1'; SELECT * FROM DBA_ROLLBACK_SEGS; SELECT * FROM V$ROLLNAME; SELECT * FROM DBA_UNDO_EXTENTS; SELECT TABLESPACE_NAME, STATUS, SUM(BYTES) / 1024 / 1024 "Bytes(M)" FROM DBA_UNDO_EXTENTS GROUP BY TABLESPACE_NAME, STATUS; SELECT R.STATUS "Status", R.SEGMENT_NAME "Name", R.TABLESPACE_NAME "Tablespace", S.EXTENTS "Extents", TO_CHAR((S.BYTES / 1024 / 1024), '99999990.000') "Size" FROM SYS.DBA_ROLLBACK_SEGS R, SYS.DBA_SEGMENTS S WHERE R.SEGMENT_NAME = S.SEGMENT_NAME AND S.SEGMENT_TYPE IN ('ROLLBACK', 'TYPE2 UNDO') ORDER BY 5 DESC; SELECT R.NAME 回滚段名, S.SID SID, S.SERIAL# SERIAL, S.USERNAME 用户名, S.MACHINE 机器名, T.START_TIME 开始时间, T.STATUS 状态, T.USED_UBLK 撤消块, USED_UREC 撤消记录, T.CR_GET 一致性取, T.CR_CHANGE 一致性变化, T.LOG_IO "逻辑I/O", T.PHY_IO "物理I/O", T.NOUNDO NOUNDO, G.EXTENTS EXTENTS, SUBSTR(S.PROGRAM, 1, 50) 操作程序 FROM V$SESSION S, V$TRANSACTION T, V$ROLLNAME R, V$ROLLSTAT G WHERE T.ADDR = S.TADDR AND T.XIDUSN = R.USN AND R.USN = G.USN ORDER BY T.USED_UBLK DESC; |
系统回滚段(System Rollback Segment)与延迟回滚段(Deferred Rollback Segment)
SYSTEM回滚段创建在系统表空间中,当手工创建数据库后,在创建普通回滚段之前必须首先创建系统回滚段。但正常情况下,系统回滚段主要用于两个方面:一是系统事务,另一个就是延迟回滚段。延迟回滚段表示的是,当使一个表空间OFFLINE之后,由于表空间不可用(不能进行读写),这个时候若有事务数据位于该表空间并且执行了回滚命令,回滚完成将显示给Client,对于Client看起来该事务已经回滚,但是对于数据库来说该回滚并没有真正完成,这个时候数据库将该回滚信息写入系统回滚段(这就是延迟回滚段),等表空间重新ONLINE的时候,数据库从系统回滚段中将回滚信息写入表空间。
为什么会“ORA-01555”
在告警日志中记录的ORA-01555(snapshot too old,快照过旧)报错信息类似:
1 | ORA-01555: snapshot too old: rollback segment number 107 with name "_SYSSMU107_1253191395$" too small |
默认情况,ORA-01555错误发生时不会自动生成跟踪日志文件,但是可以在系统里设置下面的事件,让它在错误发生时同时生成跟踪日志文件:
1 | alter system set events '1555 trace name errorstack level 3'; |
由于回滚段是循环使用的,当事务提交以后,该事务占用的回滚段事务会被标记为非活动,此时的回滚段空间可以被覆盖重用。那么问题就出现了,如果一个查询需要使用被覆盖的回滚段构造前镜像(Before Image)实现一致性读,那么此时就会出现Oracle著名的ORA-01555(snapshot too old,快照过旧)错误。需要注意的是,ORA-01555错误是一个安全的错误,它不会造成数据丢失或者损坏,只是会让收到该错误的查询无法继续。
ORA-01555错误的另外一个原因是延迟块清除(Delayed Block Clean out)。当一个查询触发延迟块清除时,Oracle需要去查询回滚段获得该事务的提交SCN。如果事务的前镜像(Before Image)信息已经被覆盖,并且查询SCN也小于回滚段中记录的最小提交SCN,那么Oracle将无从判断查询SCN和事务提交SCN的大小,此时出现延迟块清除会导致ORA-01555错误。
还有一种导致ORA-01555错误的情况出现在使用SQL*Loader直接方式加载(direct=true)数据时,由于不产生Redo和Undo信息,Oracle直接指定CACHED COMMIT SCN进行加载数据,在访问这些数据时,有时会产生ORA-01555错误。
知道了ORA-01555错误产生的原因就可以总结出以下方法来解决ORA-01555错误问题:
(1)扩大回滚段
因为回滚段是循环使用的,如果回滚段足够大,那么那些被提交的数据信息就能保存足够长的时间,而那些大事务就可以完成一致性读取。
(2)增加UNDO_RETENTION时间
在UNDO_RETENTION规定的时间内,任何其它事务都不能覆盖这些数据。
(3)优化相关查询语句,减少一致性读
减少查询语句的一致性读,也可以降低读取不到回滚段数据的风险。
(4)减少不必要的事务提交
提交的事务越少,产生的回滚段信息就越少。
(5)对大事务指定回滚段
通过以下语句可以指定事务的回滚段:
1 | SET TRANSACTION USE ROLLBACK SEGMENT rollback_segment; |
给大事务指定回滚段,即降低大事务回滚信息覆盖其它事务的回滚信息的几率,又降低了它自身的回滚信息被覆盖的几率。大事务的存在,往往是ORA-01555错误产生的诱因。
(6)使用游标时尽量使用显式游标,并且只在需要的时候打开游标,同时将所有可以在游标外做的操作从游标循环中拿出。当游标打开时,查询就开始了,直到游标关闭。减少游标的打开时间,就减少了ORA-01555错误发生的几率。
(7)使用回滚表空间自动管理
回滚表空间自动管理是Oracle 9i后的特性,Oracle自动管理回滚段的创建和回收,并且Oracle 10g中,这一特性大大增强了,而在大型的数据仓库或者报表系统中,会有一些很大的查询作业存在,这时可以考虑使用手动管理,为某些大作业创建单独的回滚段。
ORA-01555错误是一种在oracle数据库中非常常见的错误,甚至也可以说是一个非常经典的错误,只是由于oracle的发展越来越自动化(UNDO自动管理+加强),这个错误已经越来越少见,可能很多使用10g的DBA都没有遇到过这个错误。
这个错误在9i之前的版本(UNDO手工管理)出现的最多,也是最常见的,甚至可以说怎么样处理和避免ORA-01555 错误是令每一个DBA曾头痛,但是又必须面对的问题。从9i的undo自动管理,至现在的10g、11g中的undo auto tuning,使得ORA-01555错误越来越少,但是这个错误仍然不可避免,特别是那些分析型的系统中(OLTP)。
错误原因(一般有两种)
- SQL语句执行时,需要读取UNDO(前映像数据)来构造CR数据块,完成一致性读取。但是在读取undo前映像数据时发现,undo信息已经被覆盖(undo空间循环使用),不能构造一致性读的CR块,抛出ORA-01555错误
- SQL语句执行时,访问到的数据块,需要进行延迟块清除,但是在进行延迟块清除时,不能确定这个数据块的事务提交时间与SQL执行开始时间的先后次序,从而抛出ORA-01555错误
备注:延迟块清除是指前一个事务完成提交时(commit),由于修改块已经刷新至磁盘等原因,未完成块事务信息的清除(ILT,LB信息等等),在后续的SQL语句访问该块时,需要清除这些信息,这个动作即延迟块清除。
第一种情况的解决方法(仅供参考)
- 增加UNDO空间,延缓UNDO信息被覆盖,也可以理解为增加undo空间循环使用一次的时间。
- 优化抛出错误的SQL语句,缩短SQL语句执行的时间,也可以避免语句需要访问undo信息被覆盖。
- 避免频繁的提交也是一种可行方法,不过需要改动的量较前两个都要大。提交频率降低后,导致undo 信息被覆盖的可能性也降低了(oracle数据库中未提交的undo是不可能被覆盖),或者undo 事务表被覆盖的可能性也降低了。
第二种情况的解决方法(仅供参考)
- 增加UNDO空间,延缓UNDO信息被覆盖,也可以理解为增加undo空间循环使用一次的时间,尽量避免延迟块清除时需要的undo信息被覆盖。
- 优化抛出错误的SQL语句,减少SQL语句需要访问的数据块,可能避免出现ORA-01555,但是这个方法治标不治本,任何后续访问该块的SQL,都会抛出ORA-01555错误。
- 加载buffer cache,尽量使事务提交时,能够完成fast commit(直接清理快上的事务信息),这个方法基本也是过于理想,很难在实际中发挥作用。
- 缩减事务大小,也尽量使事务提交时,执行fast commit。由于需要改造业务逻辑,基本也不现实,很难在实际中发挥作用。
- 我们知道这类错误一般出现在OLAP类型的业务系统中,针对这种情况,最有效的方法是收集可能出现延迟块清除并抛出ORA-01555错误的表,在业务逻辑中,完成事务后,针对这些表立即进行一次全表扫描(清理块上的事务信息),避免后续访问清理时出现的ORA-01555错误。如果业务逻辑修改较困难,可以根据业务规则,指定一个定时针对这些表的全表扫面任务,来规避延迟块清除导致的ORA-01555错误
首先,我们来看下错误说明:
ORA-01555: snapshot too old: rollback segment number XX with name "XX XX XX XX XX" too small
ORA-1555 报错发生在一个查询无法访问相关的 UNDO 信息来创建它开始的时间点的数据拷贝。Oracle 数据库同时维护已提交“版本”的数据块与未提交“版本”的数据块,所以查询可以访问到数据在它起始时间点时在数据库中的状态。这些被称为“一致性读”,并通过 Oracle UNDO 来维护。
让我们通过以下示例的时间线来解释这个错误:
时间 | 细节 |
---|---|
T1 | 会话 #1 在表 A 上发起一个查询 |
T2 | 会话 #2 更新表 A 中的行 X |
T3 | 会话 #1 的查询到达行 X 并发现行 X 最后的更新时间(基于 SCN 号)晚于时间点1。于是 UNDO 或 LOB 段(取决于段类型)被用来获取该行的一致性读视图(前映像)来满足查询。 |
T4 | 会话 #2 更新表 A 中的行 Y 并接着提交(这会使这个事务槽可被覆盖)。 |
T5 | 会话 #2 更新表 B 中的行 Z 并提交了这个记录。由于非优化的配置,主要是缺乏空间,时间点4上对表 A 中行 Y 更新操作的一致性读视图被覆盖(我们在这里不会去检查覆盖发生的原因)。 |
T6 | 会话 #1 到达行 Y 并发现其最后更新时间(基于 SCN 号)晚于时间点1。所以 UNDO 或 LOB 段会被检查,以找到一致性读视图。然而包含这个视图的事务槽已经在时间点5被覆盖了,所以没有可用的一致性读视图并报出 ORA-1555。 |
注:UNDO 是一个通用的术语,既指系统管理的 UNDO(或自动 UNDO 管理 AUM),又指回滚段。
错误记录的日志文件
这个报错通常在如下文件中列出:
告警日志文件
生成的消息如以下示例:
ORA-01555: snapshot too old: rollback segment number 107 with name "_SYSSMU107_1253191395$" too small事件发生时生成的 trace 文件
默认地,ORA-1555 是没有 trace 文件生成的,除非在报错发生时已经设置了 1555 event:本人提供Oracle(OCP、OCM)、MySQL(OCP)、PostgreSQL(PGCA、PGCE、PGCM)等数据库的培训和考证业务,私聊QQ646634621或微信dbaup66,谢谢!