前言
本文主要介绍利用UNDO表空间的闪回技术,主要包括:闪回表,闪回版本查询,闪回事务查询,闪回查询。这些闪回技术实现从回滚段中读取表中一定时间内操作过的数据,可用来进行数据比对,或者修正意外提交造成的错误数据。由于利用的是UNDO表空间里记录的数据被改变前的值,因此数据在UNDO空间中保留多久就尤为重要,其中与之关系最紧密的是UNDO_RETENTION参数。
关于UNDO_RETENTION
UNDO_RETENTION 通常默认是900 秒,也就是15 分钟。值得注意是,undo_retention 只是指定undo 数据的过期时间,并不是说,undo 中的数据一定会在undo表空间中保存15 分钟,比如说刚一个新事务开始的时候,如果undo 表空间已经被写满,则新事务的数据会自动覆盖已提交事务的数据,而不管这些数据是否已过期。因此,当你创建一个自动管理的undo 表空间时,还要注意其空间大小,要尽可能保证undo 表空间有足够的存储空间。
同时还要注意,也并不是说,undo_retention 中指定的时间一过,已经提交事务中的数据就立刻无法访问,它只是失效,只要不被别的事务覆盖,它会仍然存在,并可随时被flashback 特性引用。如果你的undo表空间足够大,而数据库又不是那么繁忙,那么其undo_retention 参数的值并不会影响到你,哪怕你设置成1,只要没有事务去覆盖undo 数据,它就会持续有效。因此呢,这里还是那句话,要注意undo 表空间的大小,保证其有足够的存储空间。因此如果闪回表时所需要的UNDO数据,由于保留的时间超过了UNDO_RETENTION的所指定的值,从而导致该UNDO数据被其他事务覆盖的话,那么就不能闪回到指定时间了。 表空间上指定了retention guarantee选项使UNDO数据在一定时间内不被覆盖。
修改UNDO_RETETION的值命令如下:
SQL> alter system set undo_retention=600 scope=both;
启用undo guarantee
SQL> alter tablespace undotbs1 retention guarantee;
禁用undo guarantee
SQL> alter tablespace undotbs1 retention noguarantee;
实验环境
SQL> create table flash_test(id int,name varchar2(10));
Table created.
SQL> declare
2 v_int int :=1;
3 begin
4 for v_int in 1..10 loop
5 insert into flash_test values(v_int,'oracle');
6 end loop;
7 commit;
7 end;
8 /
PL/SQL procedure successfully completed.
SQL> select * from flash_test;
ID NAME
---------- ------------------------------
1 oracle
2 oracle
3 oracle
4 oracle
5 oracle
6 oracle
7 oracle
8 oracle
9 oracle
10 oracle
闪回查询
正如前言中所提,Flashback Query 是利用多版本读一致性的特性从UNDO 表空间读取操作前的记录数据!
什么是多版本读一致性
Oracle 采用了一种非常优秀的设计,通过undo 数据来确保写不堵塞读,简单的讲,不同的事务在写数据时,会将数据的前映像写入undo 表空间,这样如果同时有其它事务查询该表数据,则可以通过undo 表空间中数据的前映像来构造所需的完整记录集,而不需要等待写入的事务提交或回滚。
flashback query 有多种方式构建查询记录集,记录集的选择范围可以基于时间或基于scn,甚至可以同时查询出记录在undo 表空间中不同事务时的前映象。用法与标准查询非常类似,要通过flashback query 查询undo 中的撤销数据,最简单的方式只需要在标准查询语句的表名后面跟上as of timestamp(基于时间)或as of scn(基于scn)即可。as of timestamp|scn 的语法是自9iR2 后才开始提供支持。具体操作如下 :
SQL> select sysdate from dual;
SYSDATE
-------------------
2011-03-23 08:53:18
SQL> conn / as sysdba;
Connected.
SQL> grant select on v_$database to hr;
Grant succeeded.
SQL> grant select on flashback_transaction_query to hr;
Grant succeeded.
SQL> select GRANTEE,TABLE_NAME,PRIVILEGE from user_tab_privs;
GRANTEE TABLE_NAME PRIVILEGE
---------- ------ --------------- ----------- ----------
HR V_$DATABASE SELECT
HR FLASHBACK_TRANS SELECT
ACTION_QUERY
HR DBMS_STATS EXECUTE
OE COUNTRIES REFERENCES
OE COUNTRIES SELECT
OE LOCATIONS REFERENCES
OE LOCATIONS SELECT
OE DEPARTMENTS SELECT
OE JOBS SELECT
OE EMPLOYEES REFERENCES
OE EMPLOYEES SELECT
OE JOB_HISTORY SELECT
查询当前SCN
SQL> select current_scn from v$database;
CURRENT_SCN
---------------
851281
亦可用如下命令查询当前SCN
select dbms_flashback.get_system_change_number from dual;
对表进行DML操作并提交
SQL> delete from flash_test where id <3;
2 rows deleted.
SQL> commit;
Commit complete.
SQL> select * from flash_test;
ID NAME
---------- ------------------------------
3 oracle
4 oracle
5 oracle
6 oracle
7 oracle
8 oracle
9 oracle
10 oracle
8 rows selected.
查询当前时间
SQL> select sysdate from dual;
SYSDATE
-------------------
2011-03-23 08:54:38
基于时间点的闪回查询
SQL> select * from flash_test as of timestamp (sysdate - 2/1440);
ID NAME
---------- ------------------------------
3 oracle
4 oracle
5 oracle
6 oracle
7 oracle
8 oracle
9 ora cle
10 oracle
1 oracle
2 oracle
10 rows selected.
基于SCN的闪回查询
SQL> select * from flash_test as of scn 851281;
ID NAME
---------- ------------------------------
3 oracle
4 oracle
5 oracle
6 oracle
7 oracle
8 oracle
9 oracle
10 oracle
1 oracle
2 oracle
10 rows selected.
事实上,Oracle 在内部都是使用scn,即使你指定的是as of timestamp,oracle 也会将其转换成scn,系统时间标记与scn 之间存在一张表,即SYS 下的SMON_SCN_TIME
SQL> desc sys.smon_scn_time;
Name Null? Type
----------------------------------------- -------- ----------------------------
THREAD NUMBER
TIME_MP NUMBER
TIME_DP DATE
SCN_WRP NUMBER
SCN_BAS NUMBER
NUM_MAPPINGS NUMBER
TIM_SCN_MAP RAW(1200)
SCN NUMBER
ORIG_THREAD NUMBER
每隔5 分钟,系统产生一次系统时间标记与scn 的匹配并存入sys.smon_scn_time 表,该表中记录了最近1440个系统时间标记与scn 的匹配记录,由于该表只维护了最近的1440 条记录,因此如果使用as of timestamp 的方式则只能flashback 最近5 天内的数据(假设系统是在持续不断运行并无中断或关机重启之类操作的话)。
注意理解系统时间标记与scn 的每5 分钟匹配一次这句话,举个例子,比如scn:339988,339989 分别匹配08-05-3013:52:00 和2008-13:57:00,则当你通过as of timestamp 查询08-05-30 13:52:00 或08-05-30 13:56:59 这段时间点
内的时间时,oracle 都会将其匹配为scn:339988 到undo 表空间中查找,也就说在这个时间内,不管你指定的时间点是什么,查询返回的都将是08-05-30 13:52:00 这个时刻的数据。
具体可查看SCN 和 timestamp 之间的对应关系,读者仔细观察即可知道他们直接的对应关系:
select scn,to_char(time_dp,'yyyy-mm-dd hh24:mi:ss')from sys.smon_scn_time;
闪回版本查询
所谓版本指的是每次事务所引起的数据行变化情况,每次变化就是一个版本。这些变化都是已经提交了的事务 引起的变化,没有提交的变化不会显示。Oracle 的闪回版本查询功能(Flashback Version Query)提供了一个 审计行改变的查询功能 ,它能找到所有 已提交行的记录 。借助这个特殊的功能,我们可以看到什么时间执行了什么操作。使用该功能,可以很轻松地实现对应用系统进行审计,而没有必要使用细粒度的审计功能或者是使用LOGMNR了。
闪回版本查询功能依赖于AUM(Automatic Undo Management),AUM指的是采用撤销表空间记录来增、删、改数据的方法。
要用Flashback Version Query实现对数据行改变记录进行查询,主要采用SELECT 语句带flashback_query子语句来实现,Flashback_query子语句的语法格式如下:
SELECT [Pseudocolums]… FROM …
VERSION BETWEEN
[ SCN | TIMESTAMP ]
[ <expr> | MAXVALUE] AND <expr> | MINVALUE]
| AS OF [SCN |TIMESTAMP ] <expr>
where [Pseudocolums]
其中各项参数的说明如下。
l AS OF:表示恢复单个版本;
l SCN:系统更改号;
l TIMESTAMP:时间。
Pseudocolumns为伪列,闪回版本查询中的 伪列有
Versions_starttime :事务开始时间
Versions_startscn :事务开始SCN
Versions_endtime :事务结束时间
Versions_endscn :事务结束SCN
Versions_xid :事务的ID号
Versions_operation :事务所进行的操作类型,包括插入(I)、删除(D)和更新(U)
下面构造两个事务并通过闪回版本查询查询相关信息
SQL> select current_scn from v$database;
CURRENT_SCN
-----------
854038
SQL> insert into flash_test values(21,'Linux');
1 row created.
SQL> update flash_test set name='DBA' where id=5;
1 row updated.
SQL> commit;
Commit complete.
SQL> delete from flash_test where id >8;
3 rows deleted.
SQL> commit;
Commit complete.
SQL> select current_scn from v$database;
CURRENT_SCN
-----------
854093
SQL> select versions_starttime vss,versions_startscn ves,versions_endtime vet,versions_endscn ves,versions_xid,versions_operation,id,name
2 from flash_test
3 versions between scn 854038 and 854093
VSS VES VET VES VERSIONS_XID VER ID NAME
----------------------- --------- ----------------------- ---------- -------------------------- --- -------- ---------
23-MAR-11 10.43.01 AM 854088 020012009F010000 D 21 Linux
23-MAR-11 10.43.01 AM 854088 020012009F010000 D 10 oracle
23-MAR-11 10.43.01 AM 854088 020012009F010000 D 9 oracle
23-MAR-11 10.42.46 AM 854082 0A0028006F010000 U 5 DBA
23-MAR-11 10.42.46 AM 854082 23-MAR-11 854088 0A0028006F010000 I 21 Linux
10.43.01 AM
3 oracle
4 oracle
(省略若干行)
基于时间的闪回版本查询命令如下:
SQL> select versions_starttime vss,versions_startscn ves,versions_endtime vet,versions_endscn ves,versions_xid,versions_operation,id,name
2 from flash_test
3 versions between timestamp to_date('2011-03-23 18:27:40','yyyy-mm-dd hh24:mi:ss') and to_date('2011-03-23 18:30:00','yyyy-mm-dd hh24:mi:ss');
亦可使用versions between scn/timestamp minvalue and maxvalue where .....来查询数据行的所有变化,注意这里一定要加上where子句,否则无法成功
SQL>select * from flash_test versions between scn minvalue and maxvalue where name='oracle';
SQL>select * from flash_test versions between timestamp minvalue and maxvalue where name='oracle';
闪回事务查询
结合之前应用闪回版本查询得出的事务号可进行闪回事务查询获取撤销操作的SQL,同样的该查询也是利用UNDO表空间中的UNDO数据。注意,无论该事务提交与否,都能进行闪回事务查询
SQL>select xid,start_scn,operation,undo_sql,row_id
2 from flashBack_transaction_query
3* where xid='020012009F010000'
XID STARTSCN OPERATION UNDO_SQL ROW_ID
---------------- ---------- ------------------- ----------------------------------------------------------- ---------------
020012009F010000 854086 DELETE insert into "HR"."FLASH_TEST"("ID","NAME") values ('21','Linux'); AAAM6gAAEAAAAJEAAN
020012009F010000 854086 DELETE insert into "HR"."FLASH_TEST"("ID","NAME") values ('10','oracle'); AAAM6gAAEAAAAJEAAM
020012009F010000 854086 DELETE insert into "HR"."FLASH_TEST"("ID","NAME") values ('9','oracle'); AAAM6gAAEAAAAJEAAJ
020012009F010000 854086 BEGIN
复制UNDO_SQL中的数据执行即可得出撤销刚才进行的DML操作
TIPs:查询flashback_transaction_query这个数据字典需要DBA角色或SELECT ANY TRANSACTION权限
闪回表
所谓闪回表,就是将表里的数据回退到历史上的某个时间点,比如回退用户误删除数据之前的时间点,从而将误删除的数据恢复回来。在这个操作过程中,数据库仍然可用,而且不需要额外的空间。
由于闪回表的操作会修改表里的数据,从而有可能引起数据行的移动。比如某一行数据当前在A数据块里,而在把表闪回到以前的某个时间点时,在那个时间点上,该行数据在B数据块里。于是闪回表操作中,数据行从A数据块转移到了B数据块,因此,在闪回表之前,必须启用行迁移。
SQL> select * from flash_test;
ID NAME
---------- --------------
3 oracle
4 oracle
5 DBA
6 oracle
7 oracle
8 oracle
6 rows selected.
开启行移动
SQL> alter table flash_test enable row movement;
Table altered.
关闭行移动
SQL> alter table flash_test disable row movement;
确认是否开启行移动
SQL>select table_name,row_movement from user_tables where table_name='FLASH_TEST'
TABLE_NAME ROW_MOVEMENT
--------------- ------------------------
FLASH_TEST ENABLED
当前SCN
SQL> select current_scn from v$database;
CURRENT_SCN
-----------
868741
SQL> insert into flash_test values(100,'OCP');
1 row created.
SQL> COMMIT;
Commit complete.
SQL> select current_scn from v$database;
CURRENT_SCN
-----------
868752
SQL> select * from flash_test;
ID NAME
---------- --------------
100 OCP
3 oracle
4 oracle
5 DBA
6 oracle
7 oracle
8 oracle
7 rows selected.
进行基于SCN的闪回表
SQL> flashback table flash_test to scn 868741;
Flashback complete.
基于时间的闪回表命令如下
SQL> flashback table flash_test to timestamp to_date('2011-03-23 18:27:40‘,'yyyy-mm-dd hh24:mi:ss');
SQL> select * from flash_test;
ID NAME
---------- --------------
3 oracle
4 oracle
5 DBA
6 oracle
7 oracle
8 oracle
6 rows selected.
成功执行
如果在闪回的两个SCN直接存在DDL操作,那么闪回表将不能成功闪回 。具体操作如下
SQL> select current_scn from v$database;
CURRENT_SCN
-----------
869565
SQL> select * from flash_test;
ID NAME
---------- --------------
3 oracle
4 oracle
5 DBA
SQL> delete from flash_test where id=3;
1 row deleted.
执行DDL操作
SQL> alter table flash_test drop column name;
Table altered.
闪回到指定SCN
SQL> flashback table flash_test to scn 869565;
flashback table employees,flash_test to scn 870516
*
ERROR at line 1:
ORA-01466: unable to read data - table definition has changed
修改并提交过数据之后,对表做过DDL 操作,包括:
drop/modify 列, move 表, drop 分区(如果有的话), truncate table/partition,这些操作会另undo 表空间中的撤销数据失效,对于执行过这些操作的表应用flashback query 会触发ORA-01466 错误。另外一些表结构修改语句虽然并不会影响到undo 表空间中的撤销记录,但有可能因表结构修改导致undo 中重做记录无法应用的情况,比如对于增加了约束,而flashback query 查询出的undo 记录已经不符合新建的约束条件,这个时候直接恢复显然不可能成功,你要么暂时disable 约束,要么通过适当逻辑,对要恢复的数据进行处理之后,再执行恢复。
几点需要注意的:
1、 flashback query 对v$tables,x$tables 等动态性能视图无效,不过对于dba_*,all_*,user_*等数据字典是有效的。同时该特性也完全支持访问远端数据库,比如select * from tbl@dblink as of scn 360; 的形式。
2、基于undo 的表恢复,flashback table 实际上做的也是dml 操作(会在被操作的表上加dml 锁),因此还需要注意triggers 对其的影响,默认情况下,flashback table to scn/timestamp 在执行时会自动disable 掉与其操作表相差的triggers,如果你希望在此期间trigger 能够继续发挥做用,可以在flashback table 后附加ENABLE TRIGGERS 子句。
3、Flashback table 命令支持同时操作多个表,表名中间以逗号分隔即可,如果你执行一条 flashback table 命令时同时指定了多个表,要记住单个 flashback table 是在同一个事务中,因此这些表的恢复操作要么都成功,要么都失败。
如: flashback table a,b ,c to scn 1103864;
4、SYS用户不支持闪回表