undo 和 redo
undo 用于撤销修改的操作(rollback,事务回滚)。
redo 用于将数据的修改重演一遍(rollforward,数据前滚,数据恢复)。
undo
目的:
- 事务的回滚
- 实例的恢复
- 提供查询的一致性读
回滚段能够实现数据库的读不会受到写的阻塞。
回滚段有自身无法解决的问题,例如 ORA-01555。
回滚段可以实现对历史数据的查询(flashback)。
回滚段和事务的关系:
查看事务信息:
-- 插入一条数据
insert into t values(1);
-- 查看当前存活的事务
select xid, xidUsn, xidSlot, xidSqn, ubaFil, ubaBlk, ubaSqn from v$transaction;
查询的结果:
xid | xidUsn | xidSlot | xidSqn | ubaFil | ubaBlk | ubaSqn |
---|---|---|---|---|---|---|
08001B0087390000 | 8 | 27 | 14727 | 3 | 4357 | 2844 |
事务编号 oracle中, X 通常表示事务 |
回滚段 | 事务信息的槽位 | 序号 | 回滚段所在的文件 | 所在的数据块 | 数据块的地址序号 |
事务的xid
由 xidUsn
、xidSlot
、xidSqn
的16进制拼接而成:
to_number('08','xxxx') == 8;
to_number('001B', 'xxxx') == 27;
to_number('3987','xxxx') == 14727;
通过xid就可以定位到一个事务:用户修改一个数据块后,修改的数据块存在回滚段上,其他用户如果要用到这个回滚段,就可以通过xid找到这个回滚段。
回滚段工作流程
数据块42上有两个数据槽,存放着表 score 的数据:col0 存放字段team
,col2存放字段wickets
。
第一条记录:
block 42 slot 0
col0: ENG
col1: 841
col2: 3
第二条记录:
block 42 slot 1
col0: AUS
col1: 77
col2: 8
发出一条SQL:
update score set wickets=9 where team='AUS';
此时oracle执行步骤为:
- 在 Log Buffer 新增一条记录: | 5.2 | slot 22: 10 | | —- | —- |
5.2
表示对 undo header的操作。oracle的log buffer中通过op代码记录相关操作类型。
5
开头的op code表示对回滚段的操作:5.2
表示对回滚段头的操作,5.1
表示对回滚段数据块的操作。
slot 22:10
表示要操作的槽位。因为要对undo header操作,所以产生了一条redo。
在回滚段头上增加记录:slot 22: 10
在Log Buffer 新增一条记录: | 5.1 | block 42 slot 1 | | —- | —- | | | col2: 8 |
此时要将原始记录中的字段值记录到回滚段数据块中,所以会产生一条undo的redo。
- 将原始值记录到回滚段数据块中 | 5.1 | block 42 slot 1 | | —- | —- | | | col2: 8 |
- 在 Log Buffer新增一条记录: | 11.5 | block 42 slot 1 | | —- | —- | | | col2: 9 |
op code 为
11.x
表示的是对数据行的操作。将要变更的值记录到 redo 日志中
- 修改字段值为9
此时在不进行commit
或rollback
的情况下,继续发出一条改变的 SQL:
update score set wickets=10 where team='AUS';
Oracle的执行过程同上面的update语句大体一致,但是因为是同一个回滚段,所以不再操作 undo header,也不再生成 undo header对应的redo日志。
此时如果发出 rollback
撤销上面的两条update,则oracle执行过程为:
从回滚段倒着开始读,获取到wickets字段的上次值9。
因为要操作数据块,所以此时会产生redo | 11.5 | block 42 slot 1 | | —- | —- | | | col2: 9 |
将数据块上wickets字段值改为9
此时oracle需要对 undo header 的结构进行一下维护,因为要做回滚段的头,所以会产生一条redo: | 5.6 | slot 22: 10 | | —- | —- |
维护 undo header
继续读回滚段,获取到第一次update之前的wickets的值8
同样的生成一条 redo 日志
将数据块上的wickets的值改为8
生成一条维护 undo header 的redo: | 5.11 | slot 22: 10 | | —- | —- |
维护 undo header
操作完成,生成一条
commit
的 redo日志: | 5.4 | slot 22: 9 | | —- | —- |
5.4
表示操作完成,不管是commit
还是rollback
最后都会生成这样一条提交的日志,表示事务的结束。
- 维护 undo header的槽位
slot 22: 9
回滚段的逻辑结构
回滚段是允许重用的,一旦事务提交或撤销,这个回滚段数据块就可以被其他用户使用。
回滚段的使用遵循 LRU(最近没有被使用)算法:在申请回滚段空间时,会去寻找最近最没有被使用过的回滚段数据块作为 extent。
一致性读
DML操作不再阻塞读。
- 用户A发出一条查询SQL
- A在查询的过程中,B修改了一条数据,但是没有commit
- A遇到B未提交的数据,从 undo段中读取查询开始时的快照,不需要等待
- A查询结束
一致性读并非用要去读取回滚段。
读的两种方式:
- consistent read(一致性读)
查看执行计划统计信息时,虽然对表没有任何修改,依然会查看select该表时出现了
consistent read
一致性读。所以,不管有没有读回滚段,读的操作都叫做一致性读。
- current read(读当前状态)
执行update语句时,需要操作的是数据块当前的状态,所以不能进行一致性读。
此时看执行计划的统计信息时,出现的是
db block gets
。即使别人也在操作该表,也不能到回滚段上去读,此时需要等待别的用户(即释放锁)才能进行update操作。
缺点:select查询时可能会出现 ORA-01555
错误
ORA-01555: snapshot too old: rollback segment number string with name "string" too small
原因:从回滚段读取数据时,可能因为执行的时间过长,导致这部分回滚段上的数据已经被覆盖掉了,报出01555错误。
即使是读已提交的数据,也可能需要读取回滚段:oracle事务在提交或回滚之后,并不会直接取将对应数据块进行消除,因为这样代价太大。而是在查询访问这些数据块时,才进行 块消除 的操作,这个块消除操作也会产生回滚,所以查询已提交的数据时也可能需要访问回滚段上的数据。
自动管理UNDO—AUM
在Oracle 9i 之前,需要手工创建回滚段:
create rollback segment rbs1 tablespace xxx;
弊端:
- 考虑回滚段的个数(系统的事务数量,并发数量)
- 考虑回滚段的存储设置
Oracle 9i ,开始使用自动管理回滚段(Automatic Undo Management)。
Oracle 11g,开始创建 undo 表空间变成数据库创建的一部分。
优点:
- Oracle根据业务需求,自动创建新的回滚段或者将多的回滚段离线
- Oracle自动管理 undo 数据的保留时间(需要预先设置)
undo相关参数
查看undo相关参数:
show parameter undo;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------
undo_management string AUTO
undo_retention integer 900
undo_tablespace string UNDOTBS1
undo_management:管理方式,默认为AUTO
自动管理,也可以改为手动管理,建议自动管理。
undo_retention:undo保留时间,单位秒。是一个动态调整的参数,同时,oracle无法保证在这个保留时间内的undo数据不被覆盖,当undo空间不足时,oracle将覆盖即使未过保留期的数据以释放空间(未提交的数据不会被覆盖)。
undo_tablespace:undo表空间,当管理方式为自动管理时,需要有一个undo表空间,oracle自动创建的回滚段会存放在这个undo表空间中。undo表空间可以创建多个,但“当前”状态的只会有一个。
强制保留 undo_retention 时间内的数据:
设置undo tablespace GUARANTEE属性
-- 即使oracle申请不到外部的undo空间,也不允许覆盖在retention时间内的undo数据
alter tablespace UNDOTBS1 retention GUARANTEE;
-- 取消GUARANTEE属性
alter tablespace UNDOTBS1 retention NOGUARANTEE;
undo相关视图
查看回滚段:
select usn,name from v$rollname;
usn:回滚段编号
name:回滚段名称
一个
SYSTEM
回滚段,用于oracle系统的一些操作的回滚。
默认情况下,AUM会另外再起10个回滚段
查看回滚段信息:
select usn,extents,rsSize, shrinks,wraps,extends,status from v$rollstat;
- usn:回滚段编号
- extents:extent数量
- rssize:尺寸
- shrinks:回收的次数
- wraps:extent的切换记录
- extends:申请的扩展次数
- status:状态(online/offline)
oracle 9i之后的回滚段统计信息视图:
select * from v$undostat;
保留4天的数据,每次快照10分钟。更早期的数据保存在 DBA_HIST_UNDOSTAT视图中。
- begin_time:快照起始时间
- end_time:快照结束时间
- undoBlks:回滚次数
- txnCount:事务数
- maxQueryLen:最大一次查询所用的时间(秒)
- maxQueryID:最大一次查询的ID(即
v$sql
中的sql_id
) - maxConcurrency:最大并发数
- ssOldErrCnt:出现
ORA-01555
回滚段过小错误的次数(一致性读、flashback等操作可能出现该错误) - noSpaceErrCnt:回滚段没有扩展空间的错误出现次数
- activeBlks:活动块的个数
- tuned_undoRetention:调整了的undo_retention时间(如果快照这段时间内,sql执行时间比较长,oracle就会将这个时间调整的长一些,防止出现 ora-01555错误)
- unxpStealCnt、unxpBlkRelCnt:覆盖未过期的undo块来扩展undo空间的次数。
undo tablespace
在 11g开始,创建数据库时必须创建undo表空间。最好将undo表空间设置为自动扩展,以免出现类似 ORA-01555的错误。