【實戰】使用“基表修改法”恢復被“set unused”方式誤刪除的列

secooler發表於2009-10-09
在《【實驗】列刪除的一些細節探索》http://space.itpub.net/519536/viewspace-616123中談到了可以透過手工修改tab$ col$基表的方式對被“set unused”方式誤刪除的列進行恢復。

透過這個文章,我來給大家深入的演示一下這個極端的恢復過程,供參考。

文中為了展示全貌,有一些不可迴避的換行,影響了顯示效果,如果您有深入研究的興趣,可以將這篇文章複製到Ultraedit中並使用“Ctrl+W”調整折行模式進行研究比較。

1.建立實驗用測試表t,並初始化資料
sec@ora10g> create table t (x number, y number);
sec@ora10g> insert into t values (1,1);
sec@ora10g> insert into t values (2,2);
sec@ora10g> insert into t values (3,3);
sec@ora10g> commit;

2.檢視錶結構和測試資料
sec@ora10g> desc t;
 Name       Null?    Type
 ---------- -------- ------------
 X                   NUMBER
 Y                   NUMBER

sec@ora10g> select * from t;

         X          Y
---------- ----------
         1          1
         2          2
         3          3

3.檢視四個檢視中關於t表的定義資訊
1)檢視dba_objects檢視(為清晰的顯示,SQL*Plus下要做精心的格式化)
sys@ora10g> col OWNER for a5
sys@ora10g> col OBJECT_NAME for a11
sys@ora10g> col SUBOBJECT_NAME for a3
sys@ora10g> col OBJECT_TYPE for a11
sys@ora10g> select * from dba_objects where object_name = 'T' and wner = 'SEC';

OWNER OBJECT_NAME SUB  OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE CREATED             LAST_DDL_TIME       TIMESTAMP           STATUS  T G S
----- ----------- --- ---------- -------------- ----------- ------------------- ------------------- ------------------- ------- - - -
SEC   T                    16416          16416 TABLE       2009-10-09 08:18:59 2009-10-09 08:18:59 2009-10-09:08:18:59 VALID   N N N

2)檢視obj$檢視(為清晰的顯示,SQL*Plus下要做精心的格式化)
sys@ora10g> set lines 2000
sys@ora10g> col OBJ# for 99999
sys@ora10g> col DATAOBJ# for 99999
sys@ora10g> col name for a4
sys@ora10g> col SUBNAME for a7
sys@ora10g> col TYPE# for 99
sys@ora10g> col REMOTEOWNER for a11
sys@ora10g> col LINKNAME for a8
sys@ora10g> col FLAGS for 99
sys@ora10g> col OID$ for a4
sys@ora10g> col SPARE1 for 99
sys@ora10g> col SPARE2 for 99
sys@ora10g> col SPARE3 for 999
sys@ora10g> col SPARE4 for a6
sys@ora10g> col SPARE5 for a6
sys@ora10g> col SPARE6 for a6
sys@ora10g> select * from obj$ where name = 'T';

  OBJ# DATAOBJ#     OWNER# NAME  NAMESPACE SUBNAME TYPE# CTIME               MTIME               STIME                   STATUS REMOTEOWNER LINKNAME FLAGS OID$ SPARE1 SPARE2 SPARE3 SPARE4 SPARE5 SPARE6
------ -------- ---------- ---- ---------- ------- ----- ------------------- ------------------- ------------------- ---------- ----------- -------- ----- ---- ------ ------ ------ ------ ------ ------
 16416    16416         40 T             1             2 2009-10-09 08:18:59 2009-10-09 08:18:59 2009-10-09 08:18:59          1                          0       6       1

obj$檢視對應的插入語句如下:
Insert into SYS.OBJ$
   (OBJ#, DATAOBJ#, OWNER#, NAME, NAMESPACE,
    SUBNAME, TYPE#, CTIME, MTIME, STIME,
    STATUS, REMOTEOWNER, LINKNAME, FLAGS, OID$,
    SPARE1, SPARE2, SPARE3, SPARE4, SPARE5,
    SPARE6)
 Values
   (16416, 16416, 40, 'T', 1,
    NULL, 2, TO_DATE('10/09/2009 08:18:59', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('10/09/2009 08:18:59', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('10/09/2009 08:18:59', 'MM/DD/YYYY HH24:MI:SS'),
    1, NULL, NULL, 0, NULL,
    6, 1, NULL, NULL, NULL,
    NULL);


3)檢視tab$檢視(為清晰的顯示,SQL*Plus下要做精心的格式化)
根據上面得到的OBJ#得到tab$檢視中關於表t的資訊
sys@ora10g> col flags clear
sys@ora10g> col flags for 99999999999
sys@ora10g> col SPARE6 clear
sys@ora10g> col AUDIT$ for a38
sys@ora10g> col SPARE1 clear
sys@ora10g> col SPARE1 for 999
sys@ora10g> col PROPERTY clear
sys@ora10g> select * from tab$ where obj# = 16416;

  OBJ# DATAOBJ#        TS#      FILE#     BLOCK#      BOBJ#       TAB#       COLS    CLUCOLS   PCTFREE$   PCTUSED$   INITRANS   MAXTRANS        FLAGS AUDIT$                                 ROWCNT BLKCNT EMPCNT AVGSPC CHNCNT AVGRLN AVGSPC_FLB FLBCNT ANALYZETIME         SAMPLESIZE DEGREE INSTANCES INTCOLS KERNELCOLS   PROPERTY TRIGFLAG SPARE1 SPARE2 SPARE3 SPARE4 SPARE5 SPARE6
------ -------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ------------ -------------------------------------- ------ ------ ------ ------ ------ ------ ---------- ------ ------------------- ---------- ------ --------- ------- ---------- ---------- -------- ------ ------ ------ ------ ------ -------------------
 16416    16416          5          5         11                                2                    10         40          1        255   1073741825 --------------------------------------                                                                                                             2       2           536870912        0    736                             2009-10-09 08:18:59

tab$檢視對應的插入語句如下:
Insert into SYS.TAB$
   (OBJ#, DATAOBJ#, TS#, FILE#, BLOCK#,
    BOBJ#, TAB#, COLS, CLUCOLS, PCTFREE$,
    PCTUSED$, INITRANS, MAXTRANS, FLAGS, AUDIT$,
    ROWCNT, BLKCNT, EMPCNT, AVGSPC, CHNCNT,
    AVGRLN, AVGSPC_FLB, FLBCNT, ANALYZETIME, SAMPLESIZE,
    DEGREE, INSTANCES, INTCOLS, KERNELCOLS, PROPERTY,
    TRIGFLAG, SPARE1, SPARE2, SPARE3, SPARE4,
    SPARE5, SPARE6)
 Values
   (16416, 16416, 5, 5, 11,
    NULL, NULL, 2, NULL, 10,
    40, 1, 255, 1073741825, '--------------------------------------',
    NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, 2, 2, 536870912,
    0, 736, NULL, NULL, NULL,
    NULL, TO_DATE('10/09/2009 08:18:59', 'MM/DD/YYYY HH24:MI:SS'));


4)檢視col$檢視(為清晰的顯示,SQL*Plus下要做精心的格式化)
sys@ora10g> col DEFLENGTH for 9
sys@ora10g> set long 10
sys@ora10g> select * from col$ where obj# = 16416;

  OBJ#       COL#    SEGCOL# SEGCOLLENGTH     OFFSET NAME TYPE#     LENGTH FIXEDSTORAGE PRECISION#      SCALE      NULL$ DEFLENGTH DEFAULT$      INTCOL#   PROPERTY  CHARSETID CHARSETFORM. SPARE1 SPARE2 SPARE3 SPARE4 SPARE5 SPARE6
------ ---------- ---------- ------------ ---------- ---- ----- ---------- ------------ ---------- ---------- ---------- --------- ---------- ---------- ---------- ---------- ----------- ------ ------ ------ ------ ------ -------------------
 16416          1          1           22          0 X        2         22            0                                0                               1          0          0           0      0      0      0
 16416          2          2           22          0 Y        2         22            0                                0                               2          0          0           0      0      0      0

col$檢視對應的插入語句如下:
Insert into SYS.COL$
   (OBJ#, COL#, SEGCOL#, SEGCOLLENGTH, OFFSET,
    NAME, TYPE#, LENGTH, FIXEDSTORAGE, PRECISION#,
    SCALE, NULL$, DEFLENGTH, DEFAULT$, INTCOL#,
    PROPERTY, CHARSETID, CHARSETFORM, SPARE1, SPARE2,
    SPARE3, SPARE4, SPARE5, SPARE6)
 Values
   (16416, 1, 1, 22, 0,
    'X', 2, 22, 0, NULL,
    NULL, 0, NULL, NULL, 1,
    0, 0, 0, 0, 0,
    0, NULL, NULL, NULL);
Insert into SYS.COL$
   (OBJ#, COL#, SEGCOL#, SEGCOLLENGTH, OFFSET,
    NAME, TYPE#, LENGTH, FIXEDSTORAGE, PRECISION#,
    SCALE, NULL$, DEFLENGTH, DEFAULT$, INTCOL#,
    PROPERTY, CHARSETID, CHARSETFORM, SPARE1, SPARE2,
    SPARE3, SPARE4, SPARE5, SPARE6)
 Values
   (16416, 2, 2, 22, 0,
    'Y', 2, 22, 0, NULL,
    NULL, 0, NULL, NULL, 2,
    0, 0, 0, 0, 0,
    0, NULL, NULL, NULL);

4.使用“set unused”語句將x列修改為不可用(背後的故事即將展現在我們的面前)
1)刪除x列
sys@ora10g> alter table sec.t set unused column x cascade constraints;

Table altered.

2)變化一:表結構的變化
前:
sec@ora10g> desc t;
 Name       Null?    Type
 ---------- -------- ------------
 X                   NUMBER
 Y                   NUMBER

後:
sec@ora10g> desc t;
 Name       Null?    Type
 ---------- -------- ------------
 Y                   NUMBER


比較結果:顯然,x列不見了。

2)變化二:從dba_tab_cols檢視中觀察一下修改前與修改後的區別
前:
sys@ora10g> select table_name,column_name,hidden_column from dba_tab_cols where table_name='T';

TABLE_NAME                     COLUMN_NAME                    HID
------------------------------ ------------------------------ ---
T                              X                              NO
T                              Y                              NO

後:
sys@ora10g> select table_name,column_name,hidden_column from dba_tab_cols where table_name='T';

TABLE_NAME                     COLUMN_NAME                    HID
------------------------------ ------------------------------ ---
T                              Y                              NO
T                              SYS_C00001_09100910:19:16$     YES


比較結果:顯然,x列被系統自動標識為隱藏狀態(注意,這裡只是標註為隱藏,並沒有真正的刪除,否則就沒有這個高階的恢復方式啦)。

3)變化三:比較一下dba_objects檢視中的變化
前:
sys@ora10g> select * from dba_objects where object_name = 'T' and wner = 'SEC';

OWNER OBJECT_NAME SUB  OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE CREATED             LAST_DDL_TIME       TIMESTAMP           STATUS  T G S
----- ----------- --- ---------- -------------- ----------- ------------------- ------------------- ------------------- ------- - - -
SEC   T                    16416          16416 TABLE       2009-10-09 08:18:59 2009-10-09 08:18:59 2009-10-09:08:18:59 VALID   N N N
後:
sys@ora10g> select * from dba_objects where object_name = 'T' and wner = 'SEC';

OWNER OBJECT_NAME SUB  OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE CREATED             LAST_DDL_TIME       TIMESTAMP           STATUS  T G S
----- ----------- --- ---------- -------------- ----------- ------------------- ------------------- ------------------- ------- - - -
SEC   T                    16416          16416 TABLE       2009-10-09 08:18:59 2009-10-09 10:19:16 2009-10-09:10:19:16 VALID   N N N


比較結果:較顯然,觀察後,發現只有LAST_DDL_TIME和TIMESTAMP欄位發生了變化,更新了一下最後DDL的時間。

4)變化四:比較一下obj$檢視中的變化
前:
sys@ora10g> select * from obj$ where name = 'T';

  OBJ# DATAOBJ#     OWNER# NAME  NAMESPACE SUBNAME TYPE# CTIME               MTIME               STIME                   STATUS REMOTEOWNER LINKNAME FLAGS OID$ SPARE1 SPARE2 SPARE3 SPARE4 SPARE5 SPARE6
------ -------- ---------- ---- ---------- ------- ----- ------------------- ------------------- ------------------- ---------- ----------- -------- ----- ---- ------ ------ ------ ------ ------ ------
 16416    16416         40 T             1             2 2009-10-09 08:18:59 2009-10-09 08:18:59 2009-10-09 08:18:59          1                          0       6       1
後:
sys@ora10g> select * from obj$ where name = 'T';

  OBJ# DATAOBJ#     OWNER# NAME  NAMESPACE SUBNAME TYPE# CTIME               MTIME               STIME                   STATUS REMOTEOWNER LINKNAME FLAGS OID$ SPARE1 SPARE2 SPARE3 SPARE4 SPARE5 SPARE6
------ -------- ---------- ---- ---------- ------- ----- ------------------- ------------------- ------------------- ---------- ----------- -------- ----- ---- ------ ------ ------ ------ ------ ------
 16416    16416         40 T             1             2 2009-10-09 08:18:59 2009-10-09 10:19:16 2009-10-09 10:19:16          1                          0       6       2


比較結果:MTIME、STIME和SPARE2欄位發生變化,前兩個與結構修改有關,最後一個欄位由原來的“1”修改成了現在的“2”。

5)變化五:比較一下tab$檢視中的變化
前:
sys@ora10g> select * from tab$ where obj# = 16416;

  OBJ# DATAOBJ#        TS#      FILE#     BLOCK#      BOBJ#       TAB#       COLS    CLUCOLS   PCTFREE$   PCTUSED$   INITRANS   MAXTRANS        FLAGS AUDIT$                                 ROWCNT BLKCNT EMPCNT AVGSPC CHNCNT AVGRLN AVGSPC_FLB FLBCNT ANALYZETIME         SAMPLESIZE DEGREE INSTANCES INTCOLS KERNELCOLS   PROPERTY TRIGFLAG SPARE1 SPARE2 SPARE3 SPARE4 SPARE5 SPARE6
------ -------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ------------ -------------------------------------- ------ ------ ------ ------ ------ ------ ---------- ------ ------------------- ---------- ------ --------- ------- ---------- ---------- -------- ------ ------ ------ ------ ------ -------------------
 16416    16416          5          5         11                                2                    10         40          1        255   1073741825 --------------------------------------                                                                                                             2       2           536870912        0    736                             2009-10-09 08:18:59
後:
sys@ora10g> select * from tab$ where obj# = 16416;

  OBJ# DATAOBJ#        TS#      FILE#     BLOCK#      BOBJ#       TAB#       COLS    CLUCOLS   PCTFREE$   PCTUSED$   INITRANS   MAXTRANS        FLAGS AUDIT$                                 ROWCNT BLKCNT EMPCNT AVGSPC CHNCNT AVGRLN AVGSPC_FLB FLBCNT ANALYZETIME         SAMPLESIZE DEGREE INSTANCES INTCOLS KERNELCOLS   PROPERTY TRIGFLAG SPARE1 SPARE2 SPARE3 SPARE4 SPARE5 SPARE6
------ -------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ------------ -------------------------------------- ------ ------ ------ ------ ------ ------ ---------- ------ ------------------- ---------- ------ --------- ------- ---------- ---------- -------- ------ ------ ------ ------ ------ -------------------
 16416    16416          5          5         11                                1                    10         40          1        255   1073741825 --------------------------------------                                                                                                             2       2           537395200        0    736                             2009-10-09 10:19:16

比較結果:COLS、PROPERTY、SPARE6兩個欄位內容發生變化。重點關注COLS和PROPERTY列的變化,COLS列由原來的“2”變為了現在的“1”,因為原來表中包含2列x和y,修改後僅剩1列y;PROPERTY列用原來的“536870912”變為現在的“537395200”。

6)變化六:比較一下col$檢視中的變化
前:
sys@ora10g> col name for a26
sys@ora10g> select * from col$ where obj# = 16416;

  OBJ#       COL#    SEGCOL# SEGCOLLENGTH     OFFSET NAME                       TYPE#     LENGTH FIXEDSTORAGE PRECISION#      SCALE      NULL$ DEFLENGTH DEFAULT$      INTCOL#   PROPERTY  CHARSETID CHARSETFORM. SPARE1 SPARE2 SPARE3 SPARE4 SPARE5 SPARE6
------ ---------- ---------- ------------ ---------- -------------------------- ----- ---------- ------------ ---------- ---------- ---------- --------- ---------- ---------- ---------- ---------- ----------- ------ ------ ------ ------ ------ -------------------
 16416          1          1           22          0 X                              2         22            0                                0                               1          0          0           0      0      0      0
 16416          2          2           22          0 Y                              2         22            0                                0                               2          0          0           0      0      0      0
後:
sys@ora10g> select * from col$ where obj# = 16416;

  OBJ#       COL#    SEGCOL# SEGCOLLENGTH     OFFSET NAME                       TYPE#     LENGTH FIXEDSTORAGE PRECISION#      SCALE      NULL$ DEFLENGTH DEFAULT$      INTCOL#   PROPERTY  CHARSETID CHARSETFORM. SPARE1 SPARE2 SPARE3 SPARE4 SPARE5 SPARE6
------ ---------- ---------- ------------ ---------- -------------------------- ----- ---------- ------------ ---------- ---------- ---------- --------- ---------- ---------- ---------- ---------- ----------- ------ ------ ------ ------ ------ -------------------
 16416          0          1           22          0 SYS_C00001_09100910:19:16$     2         22            0                                0                               1      32800          0           0      0      0      0
 16416          1          2           22          0 Y                              2         22            0                                0                               2          0          0           0      0      0      0

比較結果:COL#、NAME、PROPERTY三列發生明顯變化。此三列均需重點關注。恢復時需要調整。

5.隆重推出使用“基表修改法”對被刪除的欄位進行恢復,請不要眨眼
重點修改的基表只有兩個:tab$和col$

1)恢復前檢視一下表中資料
sec@ora10g> select * from t;

         Y
----------
         1
         2
         3

2)恢復基表tab$的內容
(1)將cols欄位修改為表原有的欄位數,這裡是2行
sys@ora10g> update tab$ set cols=2 where obj#=16416;

1 row updated.

(2)經試驗證明,這一步驟可以不進行操作,修改亦無妨
update tab$ set PROPERTY=536870912 where obj#=16416;

3)恢復基表col$的內容
(1)將COL#欄位更新後保持與INTCOL#欄位內容相同
sys@ora10g> update col$ set COL#=INTCOL# where OBJ#=16416;

2 rows updated.

(2)恢復name欄位為原有面目
sys@ora10g> update col$ set name='X' where obj#=16416 and SEGCOL#=1;

1 row updated.

(3)將PROPERTY欄位統統的設定為“0”
sys@ora10g> update col$ set PROPERTY=0 where obj#=16416;

2 rows updated.

sys@ora10g> commit;

Commit complete.

4)恢復完成了,我們來Check一下恢復的效果,看到下面的內容是不是有一種崩潰的感覺,彷彿自己的一切付出都蕩然無存!不要著急,不要著急,休息一下,經繼續看我分解之。
sys@ora10g> select * from sec.t;

         Y
----------
         1
         2
         3

5)之所以上面沒有看到被恢復的x欄位內容,是因為我們沒有完成一個既簡單又不可或缺的步驟,那就是:重啟資料庫!!
重中之重:重新啟動資料庫。(為了恢復這個“小”錯誤,需要重啟資料庫,代價還是很大的!)
sys@ora10g> startup force;
ORACLE instance started.

Total System Global Area 2147483648 bytes
Fixed Size                  2074152 bytes
Variable Size             486541784 bytes
Database Buffers         1644167168 bytes
Redo Buffers               14700544 bytes
Database mounted.
Database opened.
sys@ora10g> conn sec/sec
Connected.
sec@ora10g>
sec@ora10g> select * from t;

         X          Y
---------- ----------
         1          1
         2          2
         3          3

6)OK,到此,整個“分解麻雀”的過程完成了,目的達到,能收工了麼?如果是在嚴峻的生產環境上遇到的這個問題,也許您還需要馬上使用其他的手段備份一下這個表中的資料,以防不測。

7)還有一種“簡單粗暴的恢復方式”,請參見《【實戰】使用“基表修改法”恢復被“set unused”方式誤刪除的列(續)》http://space.itpub.net/519536/viewspace-616175

6.重要小結
上面介紹的是一種特殊的恢復方法,是在沒有任何備份的情況下使用的極端恢復手段,對細節不瞭解的朋友要小心使用。

這個恢復案例對DBA的教訓或警告是什麼:
教訓1:為什麼會出現上面的情況,也許某些朋友對“set unused”的原理不是很熟悉,誤用了這個命令。因此如果您是一名維護DBA,在運算元據庫時,尤其是在維護生產資料庫時,一定要謹小慎微,要頭腦清醒到知道每一個“Enter”鍵背後的酸甜苦辣,否則您將極有可能因一次誤操作玩火自焚,消失在茫茫的DBA梯隊之中!!

教訓2:“備份無比崇高”,如果您有一個可用的備份,恢復這個錯誤的手段就多,給自己留一個後路,不要對自己過於放縱和隨意!!


教訓3:在做任何操作之前,要做充分的測試,不允許在生產資料庫上使用未經測試過的命令,即使這個命令表面上看上去是那麼的自然,很多DBA也許就是因為這些“自然”現象消失了!!


教訓4:慢慢補充吧,值得思考的內容很多。

請牢記:DBA是一種非常危險的動物!!珍愛DBA生命,遠離資料庫故障:)


-- The End --

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/519536/viewspace-616174/,如需轉載,請註明出處,否則將追究法律責任。

相關文章