本篇介绍的是Oracle数据库中UNDO数据的基本概念
1 基本概念
UNDO数据就是“原始的”,“旧的”,“修改前”的数据。保留这些旧的数据的目的就是为了让数据库能够实现事务的回滚、读一致性、闪回技术等。
UNDO数据都是被保存在UNDO表空间中。当一个事务开始的时候,数据库会为这个事务分配一个UNDO段,这个事务的所产生的UNDO数据都将保持在这个段中(不会保持在其他段中)。并且一个UNDO段可以给多个事务来使用。
2 UNDO数据的状态
UNDO数据并不会永久的存储在UNDO表空间中,随着数据状态的改变,数据库会有选择的进行覆盖,以便节约空间。UNDO数据包括了3个状态:
- Active:当事务没有提交前,UNDO数据都处于Active的状态,并且这些数据“一定不会”被覆盖。
- Unexpired:在事务提交以后,并且还没有超过保留期限,数据就处于Unexpired状态,此时数据可以被覆盖。这个状态下的数据有可能会被“读一致性”、“闪回技术”等使用。通常,这个状态的数据不会被数据库优先选择覆盖,只有在Expired的数据都被覆盖,才考虑覆盖这些数据(可以设置为不能被覆盖,见“3.2 Retention Guarantee(保证保留)”)。
- Expired:在事务提交以后,并且数据超过保留期限,数据就处于Expired状态,此时数据可以被覆盖。通常这个状态下的数据一般就不再需要被使用了。数据库会预先选择覆盖这个状态的数据。
3 UNDO表空间的管理
在AUM中最主要的特性就是:数据库会自动调优UNDO的保留期限(这个特性可以通过隐藏参数来控制:_undo_retention=true)。数据库会根据UNDO表空间的大小和系统的活动情况来自动调优UNDO保留期限为一个合适的值。默认:数据库会每10分钟收集一次并计算这个保留期限的实际值。这个值我们可以通过V$UNDOSTAT视图来查看。
SQL> select tuned_undoretention from v$undostat;
TUNED_UNDORETENTION
-------------------
900
但是UNDO表空间的设置,也直接影响了Undo Retention,如果UNDO表空间大小是:
- 自动扩展,数据库一定会保留UNDO_RETENTION参数指定的时间(保留期限的实际值=UNDO_RETENTION参数的值)。在这种情况下,容易引起UNDO表空间的急剧增大。
- 固定大小,数据库会自动调优保留期限的值(会忽略UNDO_RETENTION参数的值,实际值=max(最长查询的时间+300s,UNDO_RETENTION参数))。这个只有在UNDO表空间没有启用retention guarantee的特性时,才有效。
只要启用了UNDO表空间的Retention Guarantee特性,就能让UNDO数据一定保留UNDO_RETENTION参数设置的时间。在这个时间没有到之前,将不会被允许覆盖。
SQL> alter tablespace undotbs1 retention guarantee;
Tablespace altered.
3.3 UNDO表空间的空间分配
如果没有启用Retention Guarantee的特性,空间分配方式如下:
- 会寻找不含有Active的状态数据的段。如果没有找到,就创建一个新的回滚段;否则就使用找到的回滚段。
- 但是如果段中空闲的空间不够大,不足以来存储UNDO数据,那么数据库将创建一个新的区。
- 如果不能创建新的区(通常就是表空间大小不可扩展的情况下),它会重用已使用的区(也就是覆盖之前的数据)
- 尝试使用自己段中的Expired的区
- 尝试偷取其它段中的Expired的区
- 尝试使用自己段中的Unexpired的区
- 尝试偷取其它段中的Unexpired的区
- 报告一个错误
4 UNDO表空间的监控
4.1 使用V$UNDOSTAT视图
需要关注这个视图的几个字段:
- undotsn:最近一个活动的UNDO表空间号
- undoblks:消耗的UNDO块总数量
- maxquerylen:最大的查询时间(s)
- unxpstealcnt:偷未过期区的次数
- unxpblkrelcnt:移除未过期块的数量
- unxpblkreucnt:重用未过期块的数量
- nospaceerrcnt:空间不足错误发生的次数
- ssolderrcnt:snapshot too old错误发生的次数
- activeblks:活动块的数量
- unexpiredblks:未过期块的数量
- expiredblks:过期块的数量
SQL> select sum(rssize/1024/1024) USED_MB
2 from v$rollstat r join dba_rollback_segs s
3 on r.usn = s.segment_id
4 where s.tablespace_name='UNDOTBS1';
USED_MB
----------
22.109375