环境:
23:05:08 hr@ORCL (^ω^) select * from v$version where rownum=1; BANNER -------------------------------------------------------------------------------- Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
1 原理介绍
原理一
多版本读一致性
flashback query依赖于undo中存储的数据的前镜像,在AUM下,我们通过undo_retention及undo表空间的大小,来设置undo数据的保留时间,只要before image没有被覆盖,那么flashback query就是可能的。
oracle通过undo来确保写不阻塞读,当一个事务在写数据时,另一个事务可以通过undo表空间中的数据的前镜像来构造所需的记录集,无需等待commit或rollback。而且undo数据占用的空间并非一次性消费,而是通过AUM动态分配和回收,这样在不同时间点,同一条记录可能被多次修改,也就可能在undo表空间存在多条对应的记录,也就是对应多个版本,flashback便是利用多版本读一致性,通过查询不同时间点的数据和前镜像,获得查询指定时间点(而不仅仅是当前)数据的能力。
flashback query这一特性,最常被应用的就是修复误操作的数据,也就是基于select的扩展:select .. as of timestamp | scn,让user拥有了“穿越”过去的能力,至于恢复,便是insert tasble ..select或create table as select。
原理二
SMON_SCN_TIME
SMON_SCN_TIME是SCN和时间的映射表,也是flashback 时间的限制。从oracle 10g开始,LGWR首先会在SGA中记录SCN与时间的映射关系,SMON则定期检查SGA是否内存中记录的映射大于磁盘上的,如果有就刷新记录到磁盘。由于LGWR至少每3秒就会被激活一次,所以现在SMON_SCN_TIME能够支持大于3秒flashback。
oracle 10g提供了两个新的函数对SCN和timestamp进行相互转换:scn_to_timestamp和timestamp_to_scn.这两个函数的时间转换正是依赖于SMON_SCN_TIME表,oracle能够转换的最小SCN,也就是这个表中记录的最小记录。即使执行flashback query时指定as of timestamp查询undo中的数据,实际获取的数据是以指定的时间对应的SCN时的数据为基准。例如,SCN:339988,339989分别匹配2009-05-30 13:52:00和2009-05-30 13:57:00,则当你通过as of timestamp查询2009-05-30 13:52:00或者2009:05-30 13:56:59这段时间点内的任何时间,oracle都会将其匹配为SCN:339988到undo表空间中查找,也就是说在这个时间内,不管你指定的时间点是什么,查询返回的都将是2009-05-30 13:52:00这个时刻对应的SCN的数据。
16:11:47 sys@ORCL (^ω^) select dbms_flashback.get_system_change_number from dual; GET_SYSTEM_CHANGE_NUMBER ------------------------ 3898458 16:12:11 sys@ORCL (^ω^) alter session set nls_date_format='yyyy/mm/dd hh24:mi:ss'; 会话已更改。 16:12:45 sys@ORCL (^ω^) select scn_to_timestamp(3898458) scn from dual; SCN --------------------------------------------------------------------------- 10-9月 -12 04.12.11.000000000 下午 16:13:13 sys@ORCL (^ω^) select timestamp_to_scn(sysdate-5/1440) scn from dual; SCN ---------- 3898333 16:31:22 hr@ORCL (^ω^) select scn,to_char(time_dp,'yyyy/mm/dd hh24:mi:ss') from sys.smon_scn_time; SCN TO_CHAR(TIME_DP,'YYYY/MM/DDHH24:MI:SS' ---------- -------------------------------------- 3361371 2012/09/03 18:23:53 3361948 2012/09/03 18:33:52 3362176 2012/09/03 18:38:53 3362674 2012/09/03 18:48:52 3363615 2012/09/03 19:03:52 3363792 2012/09/03 19:08:53 3364474 2012/09/03 19:18:43 3364715 2012/09/03 19:23:47 3365034 2012/09/03 19:28:48 3365400 2012/09/03 19:38:45
2 实验
① 基于时间的查询与恢复
22:47:40 hr@ORCL (^ω^) select count(*) from t1; COUNT(*) ---------- 17 22:47:49 hr@ORCL (^ω^) delete t1 where rownum<5; 已删除4行。 22:49:15 hr@ORCL (^ω^) commit; 提交完成。 22:49:21 hr@ORCL (^ω^) select count(*) from t1; COUNT(*) ---------- 13 22:49:39 hr@ORCL (^ω^) select count(*) from t1 as of timestamp sysdate-3/1440; COUNT(*) ---------- 17 22:50:47 hr@ORCL (^ω^) create table t1_recov as select * from t1 as of timestamp sysdate-3/1440; 表已创建。 22:52:17 hr@ORCL (^ω^) select count(*) from t1_recov; COUNT(*) ---------- 17 22:52:30 hr@ORCL (^ω^) commit; 提交完成。 22:52:39 hr@ORCL (^ω^)
② 基于SCN的查询与恢复
22:52:39 hr@ORCL (^ω^) select dbms_flashback.get_system_change_number from dual; GET_SYSTEM_CHANGE_NUMBER ------------------------ 3928361 22:56:35 hr@ORCL (^ω^) select count(*) from t1; COUNT(*) ---------- 13 22:56:58 hr@ORCL (^ω^) delete t1 where rownum<5; 已删除4行。 22:58:20 hr@ORCL (^ω^) commit; 提交完成。 22:58:25 hr@ORCL (^ω^) select count(*) from t1; COUNT(*) ---------- 9 22:58:37 hr@ORCL (^ω^) select count(*) from t1 as of scn 3928361; COUNT(*) ---------- 13 22:59:04 hr@ORCL (^ω^) create table t1_recov_scn as select * from t1 as of scn 3928361; 表已创建。 23:00:12 hr@ORCL (^ω^) select count(*) from t1_recov_scn; COUNT(*) ---------- 13 23:00:29 hr@ORCL (^ω^) commit; 提交完成。