Flashback Query
Flashback 是ORACLE 自9i 就开始提供的一项特性,在9i 中利用oracle 查询多版本一致的特点,
实现从回滚段中读取表一定时间内操作过的数据,可用来进行数据比对,或者修正意外提交造成的错误数据,该项特性也被称为Flashback Query。
一、Flashback Query
正如前言中所提,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 后才开始提供支持。
----实例测试:
17:05:08 scott@ORCL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
Session altered.
17:08:00 scott@ORCL> select sysdate from dual;
SYSDATE
-------------------
2012-12-02 17:08:09
17:08:09 scott@ORCL> select * from test_ext;
EMPLOYEE_ID FIRST_NAME LAST_NAME JOB_ID MANAGER_ID HIRE_DATE SALARY COMMISSION_PCT DEPARTMENT_ID EMAIL
----------- ------------------------- ---------- ---------- ------------------- ---------- -------------- ------------- -------------------------
360 Jane Janus ST_CLERK 121 2001-05-17 00:00:00 3000 0 50 jjanus
361 Mark Jasper SA_REP 145 2001-05-17 00:00:00 8000 .1 80 mjasper
362 Brenda Starr AD_ASST 200 2001-05-17 00:00:00 5500 0 10 bstarr
363 Alex Alda AC_MGR 145 2001-05-17 00:00:00 9000 .15 80 aalda
401 Jesse Cromwell HR_REP 203 2001-05-17 00:00:00 7000 0 40 jcromwel
402 Abby Applegate IT_PROG 103 2001-05-17 00:00:00 9000 .2 60 aapplega
403 Carol Cousins AD_VP 100 2001-05-17 00:00:00 27000 .3 90 ccousins
404 John Richardson AC_ACCOUNT 205 2001-05-17 00:00:00 5000 0 110 jrichard
---模拟删除数据
17:08:23 scott@ORCL> delete from test_ext;
8 rows deleted.
17:08:52 scott@ORCL> commit;
Commit complete.
17:08:55 scott@ORCL> select * from test_ext;
no rows selected
---查看删除之前的状态
--假设当前举例删除数据已经有5分钟左右的时间(从实际测试的时间来看过了不到一分钟)
17:11:10 scott@ORCL> select * from test_ext as of timestamp sysdate-3/1440;
EMPLOYEE_ID FIRST_NAME LAST_NAME JOB_ID MANAGER_ID HIRE_DATE SALARY COMMISSION_PCT DEPARTMENT_ID EMAIL
----------- ------------ ------------ ---------- ---------- ------------------- ---------- -------------- ------------- -------------------------
360 Jane Janus ST_CLERK 121 2001-05-17 00:00:00 3000 0 50 jjanus
361 Mark Jasper SA_REP 145 2001-05-17 00:00:00 8000 .1 80 mjasper
362 Brenda Starr AD_ASST 200 2001-05-17 00:00:00 5500 0 10 bstarr
363 Alex Alda AC_MGR 145 2001-05-17 00:00:00 9000 .15 80 aalda
401 Jesse Cromwell HR_REP 203 2001-05-17 00:00:00 7000 0 40 jcromwel
402 Abby Applegate IT_PROG 103 2001-05-17 00:00:00 9000 .2 60 aapplega
403 Carol Cousins AD_VP 100 2001-05-17 00:00:00 27000 .3 90 ccousins
404 John Richardson AC_ACCOUNT 205 2001-05-17 00:00:00 5000 0 110 jrichard
8 rows selected.
----或者如下:
17:13:35 scott@ORCL> select * from test_ext as of timestamp to_timestamp('2012-12-02 17:07:00','yyyy-mm-dd hh24:mi:ss');
EMPLOYEE_ID FIRST_NAME LAST_NAME JOB_ID MANAGER_ID HIRE_DATE SALARY COMMISSION_PCT DEPARTMENT_ID EMAIL
----------- ------------------------ ---------- ---------- ------------------- ---------- -------------- ------------- -------------------------
360 Jane Janus ST_CLERK 121 2001-05-17 00:00:00 3000 0 50 jjanus
361 Mark Jasper SA_REP 145 2001-05-17 00:00:00 8000 .1 80 mjasper
362 Brenda Starr AD_ASST 200 2001-05-17 00:00:00 5500 0 10 bstarr
363 Alex Alda AC_MGR 145 2001-05-17 00:00:00 9000 .15 80 aalda
401 Jesse Cromwell HR_REP 203 2001-05-17 00:00:00 7000 0 40 jcromwel
402 Abby Applegate IT_PROG 103 2001-05-17 00:00:00 9000 .2 60 aapplega
403 Carol Cousins AD_VP 100 2001-05-17 00:00:00 27000 .3 90 ccousins
404 John Richardson AC_ACCOUNT 205 2001-05-17 00:00:00 5000 0 110 jrichard
- --使用flashback query恢复delete数据
17:14:00 scott@ORCL> insert into test_ext select * from test_ext as of timestamp sysdate-10/1440;
---或者insert into test_ext select * from test_ext as of timestamp to_timestamp('2012-12-02 17:07:00','yyyy-mm-dd hh24:mi:ss');
8 rows created.
17:15:45 scott@ORCL> commit;
Commit complete.
如上述示例中所表示的,as of timestamp 的确非常易用,但是在某些情况下,我们建议使用as of scn 的方式执行flashback query,
比如需要对多个相互有主外键约束的表进行恢复时,如果使用as of timestamp 的方式,可能会由于时间点不统一的缘故造成数据选择或插入失败,
通过scn 方式则能够确保记录的约束一致性。
2.as of scn 示例
--查看SCN:
17:15:48 scott@ORCL> select current_scn from v$database;
CURRENT_SCN
-----------
788923
17:18:52 scott@ORCL> select dbms_flashback.get_system_change_number from dual;
GET_SYSTEM_CHANGE_NUMBER
------------------------
788936
17:19:16 scott@ORCL> delete from test_ext;
8 rows deleted.
17:20:29 scott@ORCL> commit;
---查看删除之前的状态:
17:21:04 scott@ORCL> select * from test_ext as of scn 788936;
EMPLOYEE_ID FIRST_NAME LAST_NAME JOB_ID MANAGER_ID HIRE_DATE SALARY COMMISSION_PCT DEPARTMENT_ID EMAIL
----------- ------------- ------------- ---------- ---------- ------------------- ---------- -------------- ------------- -------------------------
360 Jane Janus ST_CLERK 121 2001-05-17 00:00:00 3000 0 50 jjanus
361 Mark Jasper SA_REP 145 2001-05-17 00:00:00 8000 .1 80 mjasper
362 Brenda Starr AD_ASST 200 2001-05-17 00:00:00 5500 0 10 bstarr
363 Alex Alda AC_MGR 145 2001-05-17 00:00:00 9000 .15 80 aalda
401 Jesse Cromwell HR_REP 203 2001-05-17 00:00:00 7000 0 40 jcromwel
402 Abby Applegate IT_PROG 103 2001-05-17 00:00:00 9000 .2 60 aapplega
403 Carol Cousins AD_VP 100 2001-05-17 00:00:00 27000 .3 90 ccousins
404 John Richardson AC_ACCOUNT 205 2001-05-17 00:00:00 5000 0 110 jrichard
------使用flashback query恢复delete数据
17:21:29 scott@ORCL> insert into test_ext select * from test_ext as of scn 788936;
8 rows created.
事实上,Oracle 在内部都是使用scn,即使你指定的是as of timestamp,oracle 也会将其转换成scn,
系统时间标记与scn 之间存在一张表,即SYS 下的smon_scn_time
17:24:26 scott@ORCL> 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
select * from (
select num_mappings,orig_thread,scn,scn_bas,scn_wrp,thread,time_dp,time_mp
from sys.smon_scn_time
order by time_dp desc
)where rownum<11;
NUM_MAPPINGS ORIG_THREAD SCN SCN_BAS SCN_WRP THREAD TIME_DP TIME_MP
------------ ----------- ---------- ---------- ---------- ---------- ------------------- ----------
85 0 789025 789025 0 0 2012-12-02 17:22:44 800990564
100 0 788860 788860 0 0 2012-12-02 17:17:20 800990240
83 0 788746 788746 0 0 2012-12-02 17:12:50 800989970
100 0 788091 788091 0 0 2012-12-02 17:07:19 800989639
97 0 787894 787894 0 0 2012-12-02 17:01:58 800989318
79 0 787650 787650 0 0 2012-12-02 16:57:25 800989045
100 0 787379 787379 0 0 2012-12-02 16:51:58 800988718
89 0 787257 787257 0 0 2012-12-02 16:47:16 800988436
100 0 787092 787092 0 0 2012-12-02 16:41:58 800988118
82 0 786980 786980 0 0 2012-12-02 16:37:30 800987850
每隔5 分钟,系统产生一次系统时间标记与scn 的匹配并存入sys.smon_scn_time 表,该表中记录了最近1440个系统时间标记与scn 的匹配记录,
由于该表只维护了最近的1440 条记录,因此如果使用as of timestamp 的方式则只能flashback 最近5 天内的数据(假设系统是在持续不断运行并无中断或关机重启之类操作的话)。
注意理解系统时间标记与scn 的每5 分钟匹配一次这句话,举个例子,比如scn:339988,339989 分别匹配2008-05-30 13:52:00 和2008-05-30 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 之间的对应关系:
17:40:00 2 where rownum<11;
SCN TO_CHAR(TIME_DP,'YY
---------- -------------------
4 2005-06-30 19:09:55
10569 2005-06-30 19:10:41
104863 2005-06-30 19:15:57
142704 2005-06-30 19:20:13
187891 2005-06-30 19:25:24
232400 2005-06-30 19:28:00
306630 2005-06-30 19:33:33
322553 2005-06-30 19:35:45
360021 2005-06-30 19:40:57
361506 2005-06-30 19:41:10