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