4 Flashback Query
Flashback 是Oracle 自9i 就开始提供的一项特性,在9i 中利用oracle 查询多版本一致的特点,
实现从回滚段中读取表一定时间内操作过的数据,可用来进行数据比对,
或者修正意外提交造成的错误数据,该项特性也被称为Flashback Query。
Flashback Query分
Flashback Query,
Flashback Version Query,
Flashback Transaction Query 三种。
4.1 Flashback Query
Flashback Query 是利用多版本读一致性的特性从UNDO 表空间读取操作前的记录数据。
flashback query 对v$tables,x$tables 等动态性能视图无效,
不过对于dba_*,all_*,user_*等数据字典是有效的。
该特性也完全支持访问远端数据库,比如select * from tbl@dblink as of scn 3600;的形式。
4.1.1 多版本读一致性
不同的事务在写数据时,会将数据的前映像写入undo 表空间,这样如果同时有其它事务查询该表数据,
则可以通过undo 表空间中数据的前映像来构造所需的完整记录集,
而不需要等待写入的事务提交或回滚。
Flashback query 有多种方式构建查询记录集,记录集的选择范围可以基于时间或基于scn,
甚至可以同时查询出记录在undo 表空间中不同事务时的前映象。
用法与标准查询非常类似,要通过flashback query 查询undo 中的撤销数据,
最简单的方式只需要在标准查询语句的表名后面跟上as of timestamp(基于时间)或as of scn
(基于scn)即可。
as of timestamp|scn 的语法是自9iR2 后才开始提供支持。
4.1.2 As of timestamp 的示例:
SQL> alter session set nls_date_format='YYYY-MM-DD hh24:mi:ss';
会话已更改。
SQL> select sysdate from dual;
SYSDATE
-------------------
2009-10-15 19:04:16
SQL> select * from A;
ID
----------
2
1
3
4
模拟用户误操作,删除数据
SQL> delete from A;
已删除4行。
SQL> commit;
提交完成。
SQL> select * from A;
未选定行
查看删除之前的状态:假设当前距离删除数据已经有5 分钟左右的话:
SQL> select * from A as of timestamp sysdate-5/1440;
ID
----------
2
1
3
4
或者:
SQL>select * from A as of timestamp to_timestamp('2009-10-15 19:04:16','YYYY-MM-DD hh24:mi:ss');
ID
----------
2
1
3
4
用Flashback Query恢复之前的数据:
SQL>Insert into A select * from A as of timestamp to_timestamp('2009-10-15 19:04:16','YYYY-MM-DD hh24:mi:ss');
已创建4行。
SQL> COMMIT;
提交完成。
SQL> select * from A;
ID
----------
2
1
3
4
如上述示例中所表示的,as of timestamp 的确非常易用,但是在某些情况下,
我们建议使用as of scn 的方式执行flashback query,
比如需要对多个相互有主外键约束的表进行恢复时,如果使用as of timestamp 的方式,
可能会由于时间点不统一的缘故造成数据选择或插入失败,通过scn 方式则能够确保记录的约束一致性。
4.1.3 As of scn 示例
查看SCN:
SQL>SELECT dbms_flashback.get_system_change_number FROM dual;
SQL> SELECT CURRENT_SCN FROM V$DATABASE;
CURRENT_SCN
-----------
1095782
删除数据:
SQL> delete from A;
已删除4行。
SQL> commit;
提交完成。
查看删除之前的状态:
SQL> select * from A as of scn 1095782;
ID
----------
2
1
3
4
用Flashback Query恢复之前的数据:
SQL> insert into A select * from A as of scn 1095782;
已创建4行。
SQL> commit;
提交完成。
SQL> select * from A;
ID
----------
2
1
3
4
4.1.4 SCN 与 timestamp 关系
Oracle 在内部都是使用scn,即使你指定的是as of timestamp,oracle 也会将其转换成scn,
系统时间标记与scn 之间存在一张表,即SYS 下的SMON_SCN_TIME
TEST@orcl2> 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 之间的对应关系:
TEST@orcl2> select scn,to_char(time_dp,'yyyy-mm-dd hh24:mi:ss')from sys.smon_scn_time;
SCN TO_CHAR(TIME_DP,'YY
---------- -------------------
999115 2014-06-28 07:22:51
1020280 2014-06-28 07:29:04
1133573 2014-07-01 00:42:16
1135161 2014-07-01 00:47:14
1137299 2014-07-01 00:51:50
1137910 2014-07-01 00:52:28
1138324 2014-07-01 00:57:29
1138782 2014-07-01 01:00:59
以下省略。
4.1.5 Flashback Query 函数,存储过程,包,触发器等对象
Flashback Drop 可以闪回与表相关联的对象, 如果是其他的对象,
比如function,procedure,trigger等。 这时候,
就需要使用到ALL_SOURCE 表来进行Flashback Query。
先看联机文档对该表的说明:
ALL_SOURCE describes the text source of the stored objects accessible to the current user.
Related Views
DBA_SOURCE describes the text source of all stored objects in the database.
USER_SOURCE describes the text source of the stored objects owned by the current user.
This view does not display the OWNER column.
Column Datatype NULL Description
OWNER VARCHAR2(30) NOT NULL Owner of the object
NAME VARCHAR2(30) NOT NULL Name of the object
TYPE VARCHAR2(12) Type of object: FUNCTION, JAVA SOURCE, PACKAGE,
PACKAGE BODY, PROCEDURE, TRIGGER, TYPE, TYPE BODY
LINE NUMBER NOT NULL Line number of this line of source
TEXT VARCHAR2(4000) Text source of the stored object
--可参见Oracle Database Reference 11g Release 2 (11.2)中ALL_SOURCE。
如果我们误删除了某些对象,如procedure,就可以使用all_source 表进行恢复。
TEST@orcl2> desc dba_source;
Name Null? Type
----------------------------------------- -------- ----------------------------
OWNER VARCHAR2(30)
NAME VARCHAR2(30)
TYPE VARCHAR2(12)
LINE NUMBER
TEXT VARCHAR2(4000)
查看dba_source 的所有type
TEST@orcl2> select type from dba_source group by type;
TYPE
------------
PROCEDURE
PACKAGE
PACKAGE BODY
LIBRARY
TYPE BODY
TRIGGER
FUNCTION
JAVA SOURCE
TYPE
9 rows selected.
基于timestamp恢复的语句
SQL>SELECT text
FROM dba_source
AS OF TIMESTAMP TO_TIMESTAMP ('XXXXX', 'YYYY-MM-DD HH24:MI:SS')
WHERE owner = 'XXXX' AND name = '你删除的对象名'
ORDER BY line;
示例:
创建函数:
SQL>
CREATE OR REPLACE function getdate return date
as
v_date date;
begin
select sysdate into v_date from dual;
return v_date;
end;
/
Function created.
查询函数:
SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
Session altered.
SQL> select getdate() from dual;
GETDATE()
-------------------
2011-04-07 21:02:09
查询dba_source 表:
SQL> select text from dba_source where order by line;
TEXT
--------------------------------------------------------------------------------
function getdate return date
as
v_date date;
begin
select sysdate into v_date from dual;
return v_date;
end;
7 rows selected.
drop 函数,再查询,记录不存在
SQL> drop function getdate;
Function dropped.
SQL> select text from dba_source where order by line;
no rows selected
使用我们的Flashback Query 查询:
SQL> select text from dba_source as of timestamp to_timestamp('2011-04-07 21:02:09','yyyy-mm-dd hh24:mi:ss') where order by line;
TEXT
--------------------------------------------------------------------------------
function getdate return date
as
v_date date;
begin
select sysdate into v_date from dual;
return v_date;
end;
7 rows selected.
这时候,又查看到了函数的代码,只需要把这些代码重新执行一下就ok了。 其他对象和这个类似。
这里就不演示了。
4.2 Flashback version Query
相对于Flashback Query 只能看到某一点的对象状态,
Oracle 10g引入的Flashback Version Query可以看到过去某个时间段内,
记录是如何发生变化的。 根据这个历史,DBA就可以快速的判断数据是在什么时点发生了错误,
进而恢复到之前的状态。
先看一个伪列 ORA_ROWSCN. 所谓的伪列,就是假的,不存在的数据列,用户创建表时虽然没有指定,
但是Oracle为了维护而添加的一些内部字段,这些字段可以像普通文件那样的使用。
最熟悉的伪列就是 ROWID, 它相当于一个指针,指向记录在磁盘上的位置。
ORA_ROWSCN 是Oracle 10g 新增的,暂且把它看作是记录最后一次被修改时的SCN。
Flashback Version Query 就是通过这个伪列来跟踪出记录的变化历史。
举个例子:
SQL> select * from A;
ID
----------
2
1
3
4
SQL> insert into A values(5);
已创建 1 行。
SQL> select * from A;
ID
----------
2
1
3
4
5
SQL> commit;
提交完成。
SQL> select ora_rowscn, id from A;
ORA_ROWSCN ID
---------- ----------
1098443 2
1098443 1
1098443 3
1098443 4
1098443 5
获取更多的历史信息
SQL>Select versions_xid,versions_startscn,versions_endscn,
DECODE(versions_operation,
'I','Insert',
'U','Update',
'D','Delete', 'Original') "Operation",
id
from A versions between scn minvalue and maxvalue;
或者
SQL>select xid,commit_scn,commit_timestamp,operation,undo_sql
from flashback_transaction_query q
where q.xid in(select versions_xid
from B versions between scn 413946 and 413959);
VERSIONS_XID VERSIONS_STARTSCN VERSIONS_ENDSCN Operatio ID
---------------- ----------------- --------------- -------- ----------
05001A0054020000 1099482 Update 3
05001A0054020000 1099482 Delete 3
05001A0054020000 1099482 Delete 2
05001A0054020000 1099482 Delete 1
0400150005020000 1098443 Insert 5
下面我们来讲下伪列, Flashback Version Query 技术其实有很多伪列,
但是ORA_ROWSCN是最重要。它记录的是最后一次被修改时的SCN, 注意是被提交的修改。
如果没有提交,这个伪列不会发生变化。
ORA_ROWSCN 缺省是数据块级别的,也就是一个数据块内的所有记录都是一个ORA_ROWSCN,
数据块内任意一条记录被修改,这个数据库块内的所有记录的ORA_ROWSCN都会同时改变。
上例的查询结果以证明。
不过我们可以在建表时使用关键字 rowdependencies, 可以改变这种缺省行为,
使用这个关键字后,每条记录都有自己的ORA_ROWSCN。
举例:
SQL> create table B (id number(2)) rowdependencies;
表已创建。
SQL> insert into B values(1);
已创建 1 行。
SQL> insert into B values(2);
已创建 1 行
SQL> insert into B values(3);
已创建 1 行。
SQL> commit;
提交完成。
SQL> select ora_rowscn, id from B;
ORA_ROWSCN ID
---------- ----------
1100560 1
1100560 2
1100560 3
此处SCN一样,一定很奇怪,这正好说明是最后一次被修改时的SCN,如果没有提交,
是不会变的,我们重做一下就清楚了。
SQL> analyze table B compute statistics;
表已分析。
SQL> select ora_rowscn, id from B;
ORA_ROWSCN ID
---------- ----------
1100560 1
1100560 2
1100560 3
SQL> delete from B;
已删除4行。
SQL> select ora_rowscn, id from B;
未选定行
SQL> insert into B values(1);
已创建 1 行。
SQL> commit;
提交完成。
SQL> insert into B values(2);
已创建 1 行。
SQL> commit;
提交完成。
SQL> select ora_rowscn, id from B;
ORA_ROWSCN ID
---------- ----------
1100723 1
1100729 2