Oracle數據庫在多版本數據管理的設計是非常優秀的,數據的誤刪恢復非常簡單。除了truncate以外,drop table語句和所有DML語句都是可以輕鬆恢復。
恢復DROPED TABLE
Oracle和windows類似,也有個‘回收站’的概念,查看回收站的方法很多,例如user_recyclebin
系統視圖是比較常用的,該視圖中的內容包括(恢復操作所需要的):
- 被刪除的表在回收站中叫什麼名字(OBJECT_NAME)
- 被刪除的表原來叫什麼名字(ORIGINAL_NAME)
- 表是什麼時候被刪除的(DROPTIME)
desc user_recyclebin;
名稱 空值? 類型
-------------- -------- ------------
OBJECT_NAME NOT NULL VARCHAR2(30)
ORIGINAL_NAME VARCHAR2(32)
OPERATION VARCHAR2(9)
TYPE VARCHAR2(25)
TS_NAME VARCHAR2(30)
CREATETIME VARCHAR2(19)
DROPTIME VARCHAR2(19)
DROPSCN NUMBER
PARTITION_NAME VARCHAR2(32)
CAN_UNDROP VARCHAR2(3)
CAN_PURGE VARCHAR2(3)
RELATED NOT NULL NUMBER
BASE_OBJECT NOT NULL NUMBER
PURGE_OBJECT NOT NULL NUMBER
SPACE NUMBER
- 示例:
先簡單創建一張測試表,然後刪除
SQL> conn test/test
Connected.
SQL> create table mytbl as select 1 as flag from dual;
Table created.
SQL> select * from mytbl;
FLAG
----------
1
SQL> drop table mytbl;
Table dropped.
然後可以在user_recyclebin
中找到我們剛纔刪除的表
SQL> col object_name for a30
SQL> col original_name for a30
SQL> col droptime for a30
SQL> set linesize 1000
SQL> select object_name,original_name,droptime from user_recyclebin;
OBJECT_NAME ORIGINAL_NAME DROPTIME
------------------------------ ------------------------------ ------------------------------
BIN$ksxWUEIJRM3gUADAhgIE7Q==$0 MYTBL 2019-09-17:23:21:47
可以使用OBJECT_NAME來查看錶中的數據,OBJECT_NAME是由系統自動生成的名字,需要用雙引號“”才能被識別爲表名。
SQL> select * from "BIN$ksxWUEIJRM3gUADAhgIE7Q==$0";
FLAG
----------
1
接下來就可以用flashback table to before drop
或flashback table to before drop rename to
命令將表找回或重命名爲新的表名
SQL> flashback table "BIN$ksxWUEIJRM3gUADAhgIE7Q==$0" to before drop;
Flashback complete.
SQL> select * from mytbl;
FLAG
----------
1
SQL> select * from user_recyclebin;
no rows selected
flashback table “XXX” to before drop rename to XXX
命令可以將表命名爲新的名字,而非ORIGINAL_NAME。恢復之後,回收站中該對象就看不到了。
由此我們可以看出,所謂的DROP TABLE表刪除本質上僅僅是將表重命名爲新的名字,而非物理清除。如果想物理清除需要使用PURGE關鍵字。
SQL> drop table mytbl purge;
Table dropped.
SQL> select * from user_recyclebin;
no rows selected
當然,被刪除的表不可能永遠保留,保留的時間和表空間的剩餘空間有關,當剩餘表空間不足時,則回收站裏的對象會被清理掉,優先清理最早放進回收站的(DROPTIME最小的)。
恢復CHANGED ROWS
和被刪除的表類似,被DML語句修改的行,也會在數據庫中保留一段時間,保留時間長短和UNDO的保留策略以及UNDO的實際使用情況有關,稍後會更詳細講解。先來看看如何對已經提交的DML語句影響的行恢復到語句執行之前的狀態(未提交的直接執行rollback
即可)。
方法一:VERSIONS BETWEEN
- 示例:
模擬誤刪場景,先查看一下EMP表中的數據,然後刪除表中的一行並提交,
SQL> conn scott/tiger
Connected.
SQL> set linesize 1000
SQL> set pagesize 1000
SQL> select * from emp;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10
7788 SCOTT ANALYST 7566 19-APR-87 3000 20
7839 KING PRESIDENT 17-NOV-81 5000 10
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
7876 ADAMS CLERK 7788 23-MAY-87 1100 20
7900 JAMES CLERK 7698 03-DEC-81 950 30
7902 FORD ANALYST 7566 03-DEC-81 3000 20
7934 MILLER CLERK 7782 23-JAN-82 1300 10
14 rows selected.
SQL> delete from emp where empno = 7369;
1 row deleted.
SQL> commit;
Commit complete.
SQL> select * from emp where empno = 7369;
no rows selected
利用VERSIONS子句查看被刪除的行相關的信息
SQL> select * from emp versions between scn minvalue and maxvalue where empno = 7369;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7369 SMITH CLERK 7902 17-DEC-80 800 20
我們明明只刪了一行,爲什麼查詢的結果中有兩行呢?加上versions between scn minvalue and maxvalue關鍵字後,實際上是顯示了表上數據行所有變化的“流水賬”,準確的說,並非所有變化,而是UNDO保留時間窗口內記錄的所有”流水賬“加上表在UNDO保留時間窗口的起始時間點的靜態版本。比如,undo_retention
參數設置成了3600,表示這個保留時間窗口爲1小時(3600s),那麼versions子句所能看到的數據爲:表在一小時前那個時間點的靜態版本+這一小時內的所有的變化流水。
流水記錄中的內容除了包括數據行內容本身以外,還包括這一行上發生的具體操作是什麼(versions_operation輔助隱藏列,D表示delete,I表示insert,U表示update),操作發生的時間是什麼(導致該行發生變化的操作何時開始versions_startscn,該行再次發生變化的時間即該行不再是最新行版本的時間versions_endscn,SCN是數據庫隨時間單調遞增的系統變更號,可以理解爲時間)
SQL> col versions_startscn for 99999999999999
SQL> col versions_endscn for 99999999999999
SQL> col versions_operation for a30
SQL> select versions_operation,versions_startscn,versions_endscn,t.empno
from emp versions between scn minvalue and maxvalue t;
VERSIONS_OPERATION VERSIONS_STARTSCN VERSIONS_ENDSCN EMPNO
-------------------- ----------------- --------------- ------------
D 145852760112 7369
145852760112 7369
7499
7521
7566
7654
7698
7782
7788
7839
7844
7876
7900
7902
7934
15 rows selected.
這樣我們就能清楚地區分出EMPNO=7369
的兩行當中,versions_operation='D'
的那一行是我們剛纔刪除的那一行,而另一行的versions_operation is null
表示該行爲初始版本(UNDO保留最早時間點的數據版本),由於被delete之前,改行沒有其他操作,實際上兩行的數據部分完全一致,恢復時只需要對查詢結果去重即可,或者添加versions_operation = 'D'
條件
現在我們開始恢復:
SQL> insert into emp select * from emp versions between scn minvalue and maxvalue where versions_operation = 'D' and empno = 7369;
1 rows inserted.
commit;
方法二:FLASHBACK
- 原理簡單介紹:
Oracle FLASHBACK TABLE 功能同樣是利用了UNDO“記流水賬”的功能,但FLASHBACK TABLE TO BEFORE DROP不是,前面已經說了,這是使用了“回收站”的原理。回收站純粹是爲了防誤刪而設計的,而UNDO除了誤刪恢復以外,還有其他更重要的意義——UNDO和事務的一致性和隔離性息息相關、事務的回滾(rollback命令或事務的異常終止)和一致性讀需要UNDO。這裏給大家簡單科普一下UNDO的大致作用,若當前會話上的事務對數據進行了修改,在執行提交之前,其他的會話不應該看到當前會話修改後的內容(事務隔離性),但數據又的確是修改了,只是沒有提交,因此需要UNDO來實現其他會話看到當前會話修改之前的數據版本,UNDO具有這種記流水賬的功能,因此具備提供時間窗口內任意一個時間點的數據版本的能力。
- 示例:
查看數據,然後刪除一行,刪除之前查看一下時間,主要是爲了記錄一個誤刪之前的時間點,後續恢復需要用到這個時間。這種方法相對於versions方法的侷限性在於需要回憶起誤刪操作的大致時間範圍。
SQL> conn scott/tiger
Connected.
SQL> set linesize 1000
SQL> set pagesize 1000
SQL> select * from emp;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10
7788 SCOTT ANALYST 7566 19-APR-87 3000 20
7839 KING PRESIDENT 17-NOV-81 5000 10
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
7876 ADAMS CLERK 7788 23-MAY-87 1100 20
7900 JAMES CLERK 7698 03-DEC-81 950 30
7902 FORD ANALYST 7566 03-DEC-81 3000 20
7934 MILLER CLERK 7782 23-JAN-82 1300 10
14 rows selected.
SQL> select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') as cur_date from dual;
CUR_DATE
-------------------
2019-09-19 02:07:15
SQL> delete from emp where empno = 7369;
1 row deleted.
SQL> commit;
SQL> select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') as cur_date from dual;
CUR_DATE
-------------------
2019-09-19 02:08:29
SQL> select * from emp where empno = 7369;
no rows selected
使用as of timestamp
子句查看指定時間點的數據版本
SQL> select * from emp as of timestamp to_timestamp('2019-09-19 02:07:15','yyyy-mm-dd hh24:mi:ss');
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10
7788 SCOTT ANALYST 7566 19-APR-87 3000 20
7839 KING PRESIDENT 17-NOV-81 5000 10
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
7876 ADAMS CLERK 7788 23-MAY-87 1100 20
7900 JAMES CLERK 7698 03-DEC-81 950 30
7902 FORD ANALYST 7566 03-DEC-81 3000 20
7934 MILLER CLERK 7782 23-JAN-82 1300 10
14 rows selected.
SQL> select * from emp as of timestamp to_timestamp('2019-09-19 02:07:15','yyyy-mm-dd hh24:mi:ss') where empno = 7369;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
然後開始恢復,可以直接將as of timestamp
語句(閃回查詢)查到的誤刪行insert回去,也可以用flashback table to timestamp
語句進行閃回操作。
SQL> flashback table emp to timestamp to_timestamp('2019-09-19 02:07:15','yyyy-mm-dd hh24:mi:ss');
Flashback complete.
SQL> select * from emp;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10
7788 SCOTT ANALYST 7566 19-APR-87 3000 20
7839 KING PRESIDENT 17-NOV-81 5000 10
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
7876 ADAMS CLERK 7788 23-MAY-87 1100 20
7900 JAMES CLERK 7698 03-DEC-81 950 30
7902 FORD ANALYST 7566 03-DEC-81 3000 20
7934 MILLER CLERK 7782 23-JAN-82 1300 10
14 rows selected.
兩種方法的區別和優缺點總結:
versions方法受限於undo_retention參數,時間窗口會隨着時間不段向後推進,若undo_retention設置過小,則很快就沒法查到變化記錄,不過可以臨時調大參數來滿足查詢,優點是可以查到所有行的修改操作記錄,缺點是比較複雜,不太直觀。FLASHBACK方法則可以查到UNDO中實際存在的所有數據版本,和undo_rentention參數無關,UNDO保留的機制和回收站類似,取決於UNDO表空間的剩餘大小,還受到相關參數的影響,flashback table方法的優點是便捷、直觀,缺點是需要找到相對準確的時間點。