分类: Oracle
2008-11-11 17:08:39
是自9i就开始提供的一项特性,在9i中利用oracle查询多版本一致的特点,实现从回滚段中读取表一定时间内操作过的数据,可用来进行数据比对,或者修正意外提交造成的错误数据,该项特性也被称为Flashback Query。在10g中Flashback又得到了相当大的增强,利用回收站和区的特性实现快速恢复删除表(Flashback Table)或做时间点恢复(Flashback Database)的功能。本文将分三个部分分别介绍!
提示:
关于flashback这个词的翻译呢,有很多:有叫回闪的,有叫闪回的。虽然三思个人认为相对来说闪回更通行一些,但在本系列文章中,我将坚持不做翻译,而直称为flashback。我觉着某些词汇不应做翻译,一方面是由于找不着合适的词来做转换,另一方面也有使用习惯的原因,比如oracle公司虽然大家都知道其中文名称叫甲骨文,但日常交流中大家都习惯说oracle就是这个道理。
一、Flashback Query
正如前言中所提,Flashback Query是利用多版本读一致性的特性从UNDO表空间读取操作前的记录数据!
提示:什么是多版本读一致性
Oracle采用了一种非常优秀的设计,通过undo数据来确保写不堵塞读,简单的讲,不同的事务在写数据时,会将数据的前映像写入undo表空间,这样如果同时有其它事务查询该表数据,则可以通过undo表空间中数据的前映像来构造所需的完整记录集,而不需要等待写入的事务提交或回滚。
flashback query有多种方式构建查询记录集(注意,要使用flashback的特性,必须启用自动撤销表空间,什么,你想问什么是自动撤销管理表空间?这个,说来话长啊~~~请注意关注本篇外传),记录集的选择范围可以基于时间或基于scn,甚至可以同时查询出记录在undo表空间中不同事务时的前映象。用法与标准查询非常类似,要通过flashback query查询undo中的撤销数据,最简单的方式只需要在标准查询语句的表名后面跟上as of timestamp(基于时间)或as of scn(基于scn)即可。
1、As of timestamp的示例:
先创建一个很简单的表并插入一些记录用于测试:
JSSWEB> create table jss_tb1 (id,vl) as
2 select rownum,oname from(select substr(object_name,1,1) oname from dba_objects
3 group by substr(object_name,1,1) order by 1)
4 where rownum<=20;
已创建20行。
JSSWEB> select * from jss_tb1;
ID VL
---------- --------------------
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
10 J
11 K
12 L
13 M
14 N
15 O
16 P
17 Q
18 R
19 S
20 T
已选择20行。
删除几条记录并提交:
JSSWEB> delete from jss_tb1 where id<10;
已删除9行。
JSSWEB> commit;
提交完成。
JSSWEB> select * from jss_tb1;
ID VL
---------- --------------------
10 J
11 K
12 L
13 M
14 N
15 O
16 P
17 Q
18 R
19 S
20 T
已选择11行。
你看,这个时候jss_tb1表中id<10的记录均已被删除,假设过了一会儿你发现删除操作执行有误,仍需找回那些被误删的记录该怎么办呢?使用恢复?如果是在8i,恐怕是需要这样,自9i之后,使用flashback query的特性,我们可以很轻松的恢复记录(注意并不是任何情况下都可以恢复哟,后面会讲到制约flashback query的一些因素,我们这里举的都是理想条件下的例子),假设当前距离删除数据已经有5分钟左右的话:
JSSWEB> select * from jss_tb1 as of timestamp sysdate-5/1440;
ID VL
---------- --------------------
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
10 J
11 K
12 L
13 M
14 N
15 O
16 P
17 Q
18 R
19 S
20 T
已选择20行。
你看,我们通过增加as of timestamp的语法,就可以到undo表空间中查找到5分钟之前的记录前镜像,使用它我们就可以很轻易的并且迅速的将记录恢复:
JSSWEB> insert into jss_tb1
2 select * from jss_tb1 as of timestamp sysdate-5/1440
3 where id<10;
已创建9行。
JSSWEB> commit;
提交完成。
JSSWEB> select * from jss_tb1;
ID VL
---------- --------------------
10 J
11 K
12 L
13 M
14 N
15 O
16 P
17 Q
18 R
19 S
20 T
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
已选择20行。
提示:
as of timestamp|scn的语法是自9iR2后才开始提供支持,如果是9iR1版本,需要使用DBMS_FLASHBACK包来应用flashback query的特性。
如上述示例中所表示的,as of timestamp的确非常易用,但是在某些情况下,我们建议使用as of scn的方式执行flashback query,比如需要对多个相互有主外键约束的表进行恢复时,如果使用as of timestamp的方式,可能会由于时间点不统一的缘故造成数据选择或插入失败,通过scn方式则能够确保记录的约束一致性。
全面学习oracle flashback特性(1.2)--闪回查询之As of scn
2、As of scn的示例
仍以前文中创建的表为例,既然是基于scn的查询,我们首先就需要得到scn,这里我们通过dbms_flashback.get_system_change_number函数来获取当前的scn,之后再执行数据的修改操作。
JSSWEB> select dbms_flashback.get_system_change_number from dual;
GET_SYSTEM_CHANGE_NUMBER
------------------------
344197
JSSWEB> delete jss_tb1 where id>10;
已删除10行。
JSSWEB> commit;
提交完成。
JSSWEB> select * from jss_tb1 as of scn 344197;
ID VL
---------- --------------------
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
10 J
11 K
12 L
13 M
14 N
15 O
16 P
17 Q
18 R
19 S
20 T
已选择20行。
执行insert,将删除的数据重新恢复回表jss_tb1
JSSWEB> insert into jss_tb1 select *from jss_tb1 as of scn 344197
2 where id not in(select id from jss_tb1);
已创建10行。
JSSWEB> commit;
提交完成。
事实上,Oracle在内部都是使用scn,即使你指定的是as of timestamp,oracle也会将其转换成scn,系统时间标记与scn之间存在一张表,即SYS下的SMON_SCN_TIME
JSSWEB> desc sys.smon_scn_time;
名称 是否为空? 类型
---------------------------------------- -------- ------------------------
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的方式则只能最近5天内的数据(假设系统是在持续不断运行并无中断或关机重启之类操作的话)。注意理解系统时间标记与scn的每5分钟匹配一次这句话,举个例子,比如scn:339988,339989分别匹配08-05-30 13: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这个时刻的数据。
当然,具体的情况,我想你亲自执行一下select scn,to_char(time_dp,'yyyy-mm-dd hh24:mi:ss') from sys.smon_scn_time,会理解的更深刻一些。
全面学习oracle flashback特性(1.3)--闪回查询之Versions between
3、Versions between版本查询
功能更加强大,通过versions between能够查看指定时间段内undo表空间中记录的不同版本(注意,只包括被提交的记录)。
版本查询的用法并不比as of复杂,与其类似,你只需要在标准查询后面附加versions between timestamp[/scn] t1 and t2即可。记录在版本查询中可能会是一对多的关系,比如某些记录如果被修改过多次,并分别提交,那么你在查询的时候,如果修改的操作是在你指定的时间段(或scn),则记录每次修改的结果都会被选择出来,这比较有利于我们做数据的对比,比如看看数据究竟是怎么变化的。
版本查询过程中提供了多个伪列如下:
VERSIONS_STARTSCN |
该记录操作时的scn或时间,如果为空,表示该行记录是在查询范围外创建的。 |
VERSIONS_ENDSCN VERSIONS_ENDTIME |
该记录失效时的scn或时间,如果为空,说明记录当前时间在当前表内存在,或者已经被删除了,可以配合着VERSIONS_OPERATION列来看,如果VERSIONS_OPERATION列值为D,说明该列已被删除,如果该列为空,则说明记录在这段时间无操作。 |
VERSIONS_XID |
该操作的事务ID |
VERSIONS_OPERATION |
对该行执行的操作:I表示insert,D表示delete,U表示update。 提示:对于索引键的update操作,版本查询可能会将其识别成两个操作:DELETE和INSERT。 |
举个例子(仍用前面创建的测试表):
--先来查询一下当前的scn,方面我们后面确认选择的范围
JSSWEB> select dbms_flashback.get_system_change_number from dual;
GET_SYSTEM_CHANGE_NUMBER
------------------------
372466
JSSWEB> update jss_tb1 set id=id+100 where id>15;
已更新5行。
JSSWEB> commit;
提交完成。
JSSWEB> delete jss_tb1 where id<5;
已删除4行。
JSSWEB> commit;
提交完成。
JSSWEB> insert into jss_tb1 values (201,'A1');
已创建 1 行。
JSSWEB> insert into jss_tb1 values (202,'B1');
已创建 1 行。
JSSWEB> commit;
提交完成。
--再来查询一下当前的scn,方面我们后面确认选择的范围
JSSWEB> select dbms_flashback.get_system_change_number from dual;
GET_SYSTEM_CHANGE_NUMBER
------------------------
372538
通过versions between,我们就可以看到表jss_tb1中的记录的变化情况:
JSSWEB> select id,vl,versions_startscn,versions_endscn,versions_operation
2 from jss_tb1 versions between scn 372466 and 372538 order by 2;
ID VL VERSIONS_STARTSCN VERSIONS_ENDSCN V
---------- -------------------- ----------------- --------------- -
1 A 372512 D
1 A 372512
201 A1 372523 I
2 B 372512 D
2 B 372512
202 B1 372523 I
3 C 372512 D
3 C 372512
4 D 372512
4 D 372512 D
5 E
6 F
7 G
8 H
9 I
10 J
11 K
12 L
13 M
14 N
15 O
116 P 372466 U
117 Q 372466 U
118 R 372466 U
119 S 372466 U
120 T 372466 U
已选择26行。
根据这个结果返回我们首先来看id<5的记录,每个记录各有两个版本,一行的VERSIONS_STARTSCN和VERSIONS_OPERATION有值,记录了开始时的SCN和执行的操作,另一行则是VERSIONS_ENDSCN有值,记录了该版本失效时的scn。
Id>200的有两条记录,是我们自己手工添加中,从VERSIONS_OPERATION列可以看出操作是insert。
另有5行id>100 and id<200的,从VERSIONS_OPERATION列可以看出操作是update,这是我们手工执行update set的结果。
全面学习oracle flashback特性(1.4)--闪回查询之Transaction query
4、Transaction query事务查询
的事务查询是通过查询flashback_transaction_query视图来实现的。通过查询该视图能够获得一些事务执行时的信息,甚至包括UNDO语句。
下面做个演示,首先删除一条数据:
JSSWEB> select *from jss_tb1;
ID VL
---------- --------------------
5 E
6 F
7 G
8 H
9 I
11 K
12 L
13 M
14 N
15 O
116 P
117 Q
118 R
119 S
120 T
10 J
已选择16行。
JSSWEB> select dbms_flashback.get_system_change_number from dual;
GET_SYSTEM_CHANGE_NUMBER
------------------------
413946
JSSWEB> delete jss_tb1 where id<3;
已删除0行。
JSSWEB> rollback;
回退已完成。
JSSWEB> delete jss_tb1 where id=5;
已删除 1 行。
JSSWEB> commit;
提交完成。
JSSWEB> select dbms_flashback.get_system_change_number from dual;
GET_SYSTEM_CHANGE_NUMBER
------------------------
413959
Flashback_transaction_query马上闪亮登场,大家鼓掌:
JSSWEB> select xid,commit_scn,commit_timestamp,operation,undo_sql
2 from flashback_transaction_query q where q.xid in(
3 select versions_xid from jss_tb1 versions between scn 413946 and 413959);
XID COMMIT_SCN COMMIT_TIMESTAMP OPERATION UNDO_SQL
---------------- ---------- ------------------- ------------ ------------------------------------------------------------
05002F0082000000 413958 2008-06-04 21:32:02 DELETE insert into "JSS"."JSS_TB1"("ID","VL") values ('5','E');
05002F0082000000 413958 2008-06-04 21:32:02 BEGIN
注:由于事务ID是保存在版本查询中,因此我们需要通过版本查询来关联出flashback的事务信息,这也是前面操作前要通过dbms_flashback包取scn的原因。
上述的查询结果已经清晰的向我们展示了我们所做的操作以及操作的时间等(实际执行的语句该视图并没有记录,只能通过undo_sql和operation推测),随着我们将事务范围的不断扩大,我们可以持续向前翻阅曾经做过的操作。当然,实际使用的时候需要注意,由于该视图存储记录量较大(究竟有多大呢,目前尚未找到相关文档有明确说明,初步预计,应该与smon_scn_time的存储规则有关系),查询的时候建议通过关键列过滤,比如logon_user啦,table_name或table_owner之类的,这么比起来这项操作倒确实与logminer非常想像,这简直就是个活的logminer啊,虽然只是一段时间内的。
全面学习oracle flashback特性(1.5)--闪回查询之制约因素
5、 Query的制约因素
制约该特性应用的有三方面的因素,
5.1 自动撤销表空间
这个是前面也提到了,要使用flashback的相关特性,必须启用自动撤销管理表空间,不仅是flashback query,也包括flashback table和flashback database,而对于后两项还会有些其它的附加条件,比如flashback table需要启用了recycle bin(回收站),flashback database还要求必须启用了flashback area(区)。
附A:
提示:什么是Automatic Undo Management(自动撤销管理表空间)
提到自动撤销管理表空间,就不得不提手动管理的回滚段,接触过9i之前版本的朋友对此一定有所了解(就俺看来,已经可以将其划归到历史的行列中了)。在9i之前,回滚段的管理和监控是需要dba手工介入的,创建合适的回滚段是件非常耗费dba精力的事情,你可能需要不断关注oracle运行状况很长一阵子时间后,通过不断的调整才能基本确认一段时期内回滚段的大小,一旦回滚段创建的不合适,就极有可能引起性能问题甚至错误,比如ora-1555就是典型的回滚段设置不合适触发的。
9i之后呢(含9i),oracle为了清晰它的整个概念,取消了回滚段这个说法(实际上并未取消回滚段),而完全以undo来代替,这也它正好与redo相对应,一个重做,一个撤销,对于新接触oracle的朋友来说也更加容易理解。回滚段可以不再由dba手工介入,而是完全由它自己在运行时自动分配,这在一定程度上即解放了dba,也确实起到了提高性能的作用,比如采用自动管理表空间就可以最大程序的降低ora-1555发生的机率(注意是降低,不是避免,我们不可能创建一个无限大的回滚段,ora-1555也并不完全是回滚段造成的,关于ora-1555的问题这里就不深入讨论了,互联网上已经有太多文章描述和介绍该问题及解决方案)
是否起用自动管理的撤销表空间由二个初始化参数决定:
UNDO_MANAGEMENT:值为AUTO表示使用了自动撤销管理表空间,MANUAL则表示手动管理
UNDO_TABLESPACE:当UNDO_MANAGEMENT值为AUTO时,该参数用来指定当前的undo表空间名称。
undo表空间的大小,直接影响到flashback query的查询能力,因为多版本查询所依赖的undo数据都存储在undo表空间中,该表空间越大,所能够存储的undo数据自然也越多,如果该表空间可用空间非常小,别说flashback了,恐怕正常查询都有可能触发ora-1555吧。
5.2 初始化参数
初始化参数UNDO_RETENTION的设置严格说起来也是与undo表空间有关系,但是思量再三,我觉着还是有必要单拎出来详细介绍。
该参数用来指定undo记录保存的最长时间,以秒为单位,是个动态参数,完全可以在实例运行时随时修改通常默认是900秒,也就是15分钟。
一定要注意,undo_retention只是指定undo数据的过期时间,并不是说,undo中的数据一定会在undo表空间中保存15分钟,比如说刚一个新事务开始的时候,如果undo表空间已经被写满,则新事务的数据会自动覆盖已提交事务的数据,而不管这些数据是否已过期,因此呢,这就又关联回了第一点,当你创建一个自动管理的undo表空间时,还要注意其空间大小,要尽可能保证undo表空间有足够的存储空间。
同时还要注意,也并不是说,undo_retention中指定的时间一过,已经提交事务中的数据就立刻无法访问,它只是失效,只要不被别的事务覆盖,它会仍然存在,并可随时被flashback特性引用。如果你的undo表空间足够大,而又不是那么繁忙,那么其实undo_retention参数的值并不会影响到你,哪怕你设置成1(这么说好像绝对了点,大家一定要注意理解,表钻牛角尖),只要没有事务去覆盖undo数据,它就会持续有效。因此呢,这里还是那句话,要注意undo表空间的大小,保证其有足够的存储空间。
提示:
只有在一种情况下,undo表空间能够确保undo中的数据在undo_retention指定时间过期前一定有效,就是为undo表空间指定Retention Guarantee,指定之后,oracle对于undo表空间中未过期的undo数据不会覆盖,例如:
SQL> Alter tablespace undotbs1 retention guarantee;
如果想禁止undo表空间retention guarantee,如例:
SQL> Alter tablespace undotbs1 retention noguarantee;
转了一圈,问题又回来了,既然它看起来有用又像没有用,为什么还要设置它呢,黑黑,就我理解,其存在的真实用途,就是提醒你undo表空间很重要,给它指定分配一个合适的大小,更重要哟。
5.3 DDL操作的影响
第三个就是修改并提交过数据之后,对表做过DDL操作,包括:
drop/modify列, move表, drop分区(如果有的话), truncate table/partition,这些操作会另undo表空间中的撤销数据失效,对于执行过这些操作的表应用flashback query会触发ORA-01466错误。另外一些表结构修改语句虽然并不会影响到undo表空间中的撤销记录,但有可能因表结构修改导致undo中重做记录无法应用的情况,比如对于增加了约束,而flashback query查询出的undo记录已经不符合新建的约束条件,这个时候直接恢复显然不可能成功,你要么暂时disable约束,要么通过适当逻辑,对要恢复的数据进行处理之后,再执行恢复。
另外,flashback query对v$tables,x$tables等动态性能视图无效,不过对于dba_*,all_*,user_*等数据字典是有效的。同时该特性也完全支持访问远端数据库,比如select * from tbl@dblink as of scn 360;的形式。