有没有因为误删除重要数据而手忙脚乱、不知所措的经历?
Oracle9i Database 推出了闪回查询选项的概念,以便检索过去某个时间点的数据,但它不能闪回 DDL 操作,如删除表的操作。唯一的恢复方法是在另一个数据库中使用表空间的时间点恢复,然后使用导出/导入或其他方法。以上内容我只能说是“据说”,因为并没有用过Oracle9i,没有在其下进行测试。
但是,Oracle Database 10g 中的闪回表特性,可以毫不费力地恢复被意外删除的表。
下面就通过一些简单的测试,了解一下闪回表的惊人之处。
--删除数据并提交后恢复数据--
--查看当前模式中的所有表
SELECT * FROM TAB;
TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
TEST TABLE
--将表test删除
DROP TABLE TEST;
--drop掉test后再次查看当前模式中的所有表
SELECT * FROM TAB;
TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
BIN$3OBHItXyRzah8A5iZe8E8A==$0 TABLE
表 TEST已不存在,但是出现了新表 BIN$3OBHItXyRzah8A5iZe8E8A==$0。被删除的表 TEST并没有完全消失,而是被重命名为一个由系统定义的名称。它与原表存在于同一个表空间中,具有与原始表相同的结构。如果在该表上定义了索引或触发器,则它们也被重命名,使用与该表相同的命名规则。任何相关源(如过程)都将会失效;原始表的触发器和索引都被改为放置在重命名的表BIN$yqtbmilxrxy0fkiefmfvbw==$0上,保持被删除表的完整对象结构。
被删除的表及其相关对象被放置在一个称为“回收站”的逻辑容器中,它类似PC 机中的回收站。但是,对象并没有从它们原先所在的表空间中被真正删除。所以,回收站只是一个列出被删除对象目录的逻辑结构。
执行如下命令:
show recyclebin;
显示回收站被删除对象目录:
ORIGINAL NAME RECYCLEBIN NAME OBJECT TYPE DROP TIME
---------------- ------------------------------ ------------ -------------------
TEST BIN$3OBHItXyRzah8A5iZe8E8A==$0 TABLE 2009-06-03:14:49:29
--恢复表test
FLASHBACK TABLE TEST TO BEFORE DROP;
--恢复表test后再次查看当前模式中的所有表
SELECT * FROM TAB;
TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
TEST TABLE
可以发现drop掉的表恢复成功了。
现在我们测试一下表被删除后在回收站中的被重新命的名字是否总是唯一的:
再次执行
DROP TABLE TEST;
--drop掉test后再次查看当前模式中的所有表
SELECT * FROM TAB;
TNAME TABTYPE
CLUSTERID
------------------------------ ---
---- ----------
BIN$gPwwk/5lTY+Cjds1dFOWBg==$0 TABLE
可以发现BIN$gPwwk/5lTY+Cjds1dFOWBg与上次重新命的名字
BIN$3OBHItXyRzah8A5iZe8E8A==$0并不相同。
如果希望完全删除该表而不需要使用闪回特性,可以使用以下命令:
DROP TABLE TABLENAME PURGE;
再创建一张测试表
CREATE TABLE TEST2 AS SELECT * FROM TEST;
--创建表test2后再次查看当前模式中的所有表
SELECT * FROM TAB;
TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
TEST TABLE
TEST2 TABLE
执行永久删除表TEST2的命令:
DROP TABLE TEST2 PURGE;
--永久删除表TEST2后再次查看当前模式中的所有表
SELECT * FROM TAB;
TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
TEST TABLE
可以发现表TEST2被永久删除了,而没有在回收站中出现重新命名的新表。
如果在删除表的过程中没有实际删除表 — 因而没有释放表空间 — 那么当被删除的对象占用了所有空间时,会发生什么事?
答案很简单:这种情况根本不会出现。当表空间被回收站数据完全占满,以至于必须扩展数据文件来容纳更多数据时,可以说表空间处于“空间压力”情况下。此时,对象以先进先出的方式从回收站中自动清除。在删除表之前,相关对象(如索引)被删除。
同样,空间压力可能由特定表空间定义的用户限额而引起。表空间可能有足够的空余空间,但用户可能将其在该表空间中所分配的部分用完了。在这种情况下,Oracle 自动清除该表空间中属于该用户的对象。
此外,有几种方法可以手动控制回收站。
创建测试表TEST2
CREATE TABLE TEST2 AS SELECT * FROM TEST;
DROP TABLE TEST2;
--drop掉表test2后再次查看当前模式中的所有表
SELECT * FROM TAB;
TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
TEST TABLE
BIN$ZugeZyQZQYGPau4oeCY4cg==$0 TABLE
再次执行
PURGE TABLE TEST2;
彻底删除表TEST2。
或者使用其回收站中的名称,执行
PURGE TABLE " BIN$ZugeZyQZQYGPau4oeCY4cg==$0";
彻底删除表TEST2。
此命令将从回收站中删除表 TEST 及所有相关对象,如索引、约束等,从而节省了空间。但是,如果仅仅是从回收站中永久删除索引,则可以使用以下命令来完成工作:
PURGE INDEX INDEX_NAME;
创建测试表TEST2和
CREATE TABLE TEST2 AS SELECT * FROM TEST;
为表TEST2创建索引IDX_TEST2:
CREATE INDEX IDX_TEST2 ON T
EST2(ID);
SELECT INDEX_NAME FROM USER_INDEXES;
INDEX_NAME
---------------
---------------
IDX_TEST2
执行删除表TEST2:
DROP TABLE TEST2;
再次执行如下查询语句:
SELECT INDEX_NAME FROM USER_INDEXES;
未选定行
说明drop表TEST2后该表上的索引也自动删除。
现在执行仅仅在回收站中彻底删除该索引的语句:
PURGE INDEX IDX_TEST2;
索引已清除。
下面再做一个测试:
先把表Test2 drop掉然后利用闪回表进行恢复,观察一下表恢复后索引的变化:
依次执行如下语句:
PURGE TABLE TEST2;
CREATE TABLE TEST2 AS SELECT * FROM TEST;
CREATE INDEX IDX_TEST2 ON TEST2(ID);
DROP TABLE TEST2;
执行恢复表命令:
FLASHBACK TABLE TEST2 TO BEFORE DROP;
执行查询索引的语句:
SELECT INDEX_NAME FROM USER_INDEXES;
INDEX_NAME
------------------------------
BIN$BFl7lTEgRz2ZzK+/xTeNCw==$0
可以发现恢复表TEST2后,该表上的索引已被系统重命名。(不知道有没有在利用闪回表恢复表后,该表上索引名称不改变的方法???)
现在把表TEST2再次drop掉:
DROP TABLE TEST2;
然后再次执行仅仅在回收站中彻底删除该索引的语句
PURGE INDEX IDX_TEST2;
还可行吗?
结果为:
SQL> PURGE INDEX IDX_TEST2;
PURGE INDEX IDX_TEST2
*
第 1 行出现错误:
ORA-38307: 对象不在回收站中
为什么不行了呢?因为在利用闪回表把TEST2恢复时(红色标注的语句),索引IDX_TEST2的名称已经变化为:BIN$BFl7lTEgRz2ZzK+/xTeNCw==$0
现在试一下下面的语句:
PURGE INDEX BIN$BFl7lTEgRz2ZzK+/xTeNCw==$0;
结果为:
SQL> PURGE INDEX BIN$BFl7lTEgRz2ZzK+/xTeNCw==$0;
PURGE INDEX BIN$BFl7lTEgRz2ZzK+/xTeNCw==$0
*
第 1 行出现错误:
ORA-00933: SQL 命令未正确结束
看来是行不通,我测试到这个程度,深层次的原因目前还不清楚。
有时在更高级别上进行清除可能会有用。例如,可能希望清除表空间
USERS 的回收站中的所有对象。可以执行:
PURGE TABLESPACE USERS;
如果希望只为该表空间中特定用户清空回收站。在数据仓库类型的环境中,用户创建和删除许多临时表,此时这种方法可能会有用。更改上述命令,限定只清除特定的用户:
PURGE TABLESPACE USERS USER SCOTT;
例如SCOTT 等用户可以使用以下命令来清空自己的回收站:
PURGE RECYCLEBIN;
DBA 可以使用以下命令清除任何表空间中的所有对象:
PURGE DBA_RECYCLEBIN;
通过以上测试可以发现通过多种不同方法都能管理回收站,以满足特定的需要。
下面进行测试删除TEST的实验。
DROP TABLE TEST PURGE;
依次执行下面3组语句:
第1组:
CREATE TABLE TEST (ID NUMBER);
INS
ERT INTO TEST VALUES (1);
COMMIT;
DROP TABLE TEST;
第2组
:
CREATE TABLE TEST (ID NUMBER);
INSERT INTO TEST VALUES (2);
COMMIT;
DROP TABLE TEST;
第3组:
CREATE TABLE TEST (ID NUMBER);
INSERT INTO TEST VALUES (3);
COMMIT;
DROP TABLE TEST;
查看当前模式中的所有表
SELECT * FROM TAB;
结果为:
TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
BIN$nTqd5QZfTCKYuua0QlNzDw==$0 TABLE
BIN$xEXO3tErQSanuccwfq3OhQ==$0 TABLE
BIN$VMvc5HpzREKopxYRFTUuUA==$0 TABLE
下面利用闪回表进行恢复表TEST,执行如下语句:
Flashback table TEST TO BEFORE DROP;
对表TEST执行查询操作,字段ID的值应该是什么?
SELECT ID FROM TEST;
结果为:
SQL> SELECT ID FROM TEST;
ID
----------
3
如果想要恢复第1组合第2组中的TEST表应该怎么操作呢?
执行如下语句把表TEST彻底删除:
DROP TABLE TEST PURGE;
然后再次执行恢复表操作:
FLASHBACK TABLE TEST TO BEFORE DROP;
下面再次查询表TEST:
SELECT * FROM TEST;
结果为:
SQL> Select * from test;
ID
----------
2
可以得出结论,相同名称的表执行多次创建和删除操作后,利用闪回表进行恢复时总是恢复最近删除的那张表。
同理,利用上面的方法也可以恢复第1组的TEST表。
但是,这种方法最大的缺憾时等成功恢复第1组的TEST表后,第2组和第3组的TEST表全都彻底删除了。
在不删除当前TEST表的情况下,如果恢复第1组的TEST表?
可不可以将第1组的TEST表恢复后进行重命名?
试一下再说。
执行如下语句:
FLASHBACK TABLE TEST TO BEFORE DROP RENAME TO TEST1;
结果为:
SQL> FLASHBACK TABLE TEST TO BEFORE DROP RENAME TO TEST1;
闪回完成。
对TEST1执行查询操作:
SELECT * FROM TEST1;
结果为:
SQL> SELECT * FROM TEST1;
ID
----------
1
在测试PURGE INDEX INDEX_NAME; 使用时时曾提到利用闪回表恢复表后,该表上的索引(也包括触发器)等相关对象并没有恢复原始名称,它们仍然使用回收站的名称。在表上定义的源(如视图和过程)没有重新编译,仍然保持无效状态。必须手动得到这些原有名称并应用到闪回表。
依次执行如下语句:
CREATE TABLE TEST2 AS SELECT * FROM TEST ;
CREATE INDEX IDX_TEST2 ON TEST2(ID);
DROP TABLE TEST2;
信息保留在名为 USER_RECYCLEBIN 的视图中。在对表进行闪回操作前,可以使用以下查询来检索原有名称。
SELECT OBJECT_NAME, ORIGINAL_NAME, TYPE
from USER_RECYCLEBIN;
结果为:
SQL> SELECT OBJECT_NAME, ORIGINAL_NAME, TYPE from USER_RECYCLEBIN;
------------------------------ --------------------------------
OBJECT_NAME
ORIGINAL_NAME TYPE
BIN$zTYTcd0G
TFqGcuc5q3hr/A==$0 IDX_TEST2 INDEX
BIN$GGO77ZCFQW6jeRDp1PACGw==$0 TEST2 TABLE
查询出原有名称后进行手工修改(再次发出之前发出过的疑问:不知道有没有在利用闪回表恢复表后,该表上索引名称不改变的方法???)。
下面再做一个测试:
先把表TEST2彻底删除。
SQL> purge table test2;
表已清除。
依次执行如下语句:
CREATE TABLE TEST2 AS SELECT * FROM TEST ;
CREATE BITMAPINDEX IDX_TEST2 ON TEST2(ID);
DROP TABLE TEST2;
再次执行如下语句进行查询:
SELECT OBJECT_NAME, ORIGINAL_NAME, TYPE
from USER_RECYCLEBIN;
结果为:
SQL> SELECT OBJECT_NAME, ORIGINAL_NAME, TYPE from USER_RECYCLEBIN;
------------------------------ --------------------------------
OBJECT_NAME ORIGINAL_NAME TYPE
BIN$mSM75pY5SBi+pauVpOv/Kg==$0 TEST2 TABLE
可以得出结论:当删除位图索引时,它们并不放置在回收站中 — 因此无法检索它们。约束名称也无法从视图中检索(具体不再测试)。
下面进行把数据恢复到某个点上的实验。
要实现把数据闪回到过去的某个点上,必须确保与撤销的表空间有关的参数设置合理。
已DBA身份登录数据库,查看撤销表空间有关参数:
SQL> conn sys/admin_0522 as sysdba;
已连接。
SQL> show parameter undo;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_management string AUTO
undo_retention integer 900
undo_tablespace string UNDOTBS1
undo_management 表示系统采用撤销表空间记录增、删、该数据。
undo_tablespace 表示撤销表空间名称是UNDOTBS1
undo_retention 表示记录在用撤销表空间内的记录增、删、该数据保留的时间,这里是900秒。也可以修改该参数,使保留时间更长。
SQL> alter system set undo_retention=1800 scope=both;
系统已更改。
下面测试一下查看闪回版本以及根据时间点恢复相应数据。
SQL> conn zygdemo/fhwlcatv@zygora
已连接。
SQL> select * from test;
ID
----------
1
2
3
下面依次执行如下操作:
第1组
DELETE FROM TEST WHERE ID=3;
Commit;
select to_char(sysdate,'yyyymmdd hh24:mi:ss') from dual;
结果为:
SQL> DELETE FROM TEST WHERE ID=3;
已删除 1 行。
SQL> Commit;
提交完成。
SQL> select to_char(sysdate,'yyyymmdd hh24:mi:ss') from dual;
TO_CHAR(SYSDATE,'
-----------------
20090604 10:57:35
第2组
DELETE FROM TEST WHERE ID=2;
Commit;
select to_char(sysdate,'yyyymmdd hh24:mi:ss') from dual;
结果为:
SQL> DELETE FROM TEST WHERE ID=2;
已删除 1 行。
SQL> Commit;
提交完成。
SQL> select to_char(sysdate,'yyyymmdd hh24:mi:ss') from dual;
TO_CHAR(SYSDATE,'
-----------------
20090604 10:59:44
在20090604 10:57:35和20090604 10:59:44分别删除了ID=3和ID=2的记录。
查询当前TEST表中记录:
Select * from test;
SQL> Select * from test;
ID
--------------------------------------------------------
1
查询10:59:43时刻TEST表中记录:
SQL> select * from test
2 as of timestamp
3 to_timestamp('20090604 10:59:43','yyyymmdd hh24:mi:ss');
ID
----------
1
2
查询10:57:34时刻TEST表中记录:
SQL> select * from test
2 as of timestamp
3 to_timestamp('20090604 10:57:34','yyyymmdd hh24:mi:ss');
ID
----------
1
2
3
现在想把数据恢复到只有ID为1和2时的状态,可以如下操作:
alter table test enable row movement;
flashback table test to timestamp
to_timestamp('20090604 10:59:43','yyyymmdd hh24:mi:ss');
select * from test;
结果为:
SQL> alter table test enable row movement;
表已更改。
SQL> flashback table test to timestamp
2 to_timestamp('20090604 10:59:43','yyyymmdd hh24:mi:ss');
闪回完成。
SQL> select * from test;
ID
----------
1
2