HelloDBA [English]
搜索Internet 搜索 HelloDBABA
  Oracle技术站。email: fuyuncat@gmail.com  MSN: fuyuncat@hotmail.com   acoug  acoug 

移花接木————利用Oracle表扫描机制恢复被Truncate的数据

[English]

作者: fuyuncat

来源: www.HelloDBA.com

日期: 2012-08-14 01:59:34

分享到  新浪微博 腾讯微博 人人网 i贴吧 开心网 豆瓣 淘宝 推特 Facebook GMail Blogger Orkut Google Bookmarks

几天前,用FySafe帮朋友恢复了一个测试环境(无备份、无归档)中被意外TRUNCATE的一张表的数据。FySafe是一个类似DUL的工具,通过逐个解析数据块内容来恢复数据。朋友问,在这种情况下,还有没有更加迅捷的方法来恢复数据?我说目前还没有。
后来又仔细考虑一下这个问题。觉得从理论上讲应该可以找到另外一个方法来恢复数据。
首先,我们分析一下TRUNCATE的过程。TRUNCATE不会逐个清除用户数据块上的数据,而仅仅重置数据字典和元数据块上的元数据(如存储段头和扩展段图)。也就是说,此时,其基本数据并未被破坏,而是被系统回收、等待被重新分配————因此,要恢复被TRUNCATE的数据,需要及时备份其所在的数据文件。
然后,再分析一下表扫描的过程(我曾经在这篇文章中分析了小表扫描的过程:oracle逻辑IO的秘密: Full Table Scan: Part 1):Oracle会读取段头的元数据,获得高水位线等信息,然后读取高水位线以下被格式化的数据块。因此,理论上讲,如果能够将被重置的元数据和元数据块重新构造出来,就能使数据能被重新读取。然而,要完成这个任务,难度相当大————要找出原有的所有元数据块被保证其每个字节与被TRUANCATE之前完全相同————看起来似乎是一个不可能完成的任务。
不过,我们可以换一角度来找方法————如果我们已经有一套元数据及数据块,然后将被TRUNCATE的用户数据块的内容取代其用户数据块的内容,是否可以“骗”过Oracle,让它读出这些数据呢?
回顾一下表扫描的过程,这个方法应该是可行的。我们只要想办法构造出一个结构相同、且具有完整元数据信息和格式化了的用户数据块的傀儡表对象,然后将被TRUNCATE的用户数据块找出,再将其数据内容部分嫁接到傀儡对象的用户数据块,使Oracle以外这是傀儡对象的数据,就能让Oracle扫描并读出数据内容。其原理用图示描述如下:

XML/HTML代码
  1.                                                 +-------------------------+  
  2.                                                 | Copy Of Dummy Data File |  
  3.                                                 |  (With Formmated Blocks)|  
  4.                                                 +-------------------------+  
  5.                                                             ||  
  6.                                                             \/  
  7.                                                 (Blcok Header, Block Tail)  
  8.                                                             ||  
  9.                                                             \/  
  10. +-------------------+                                +----------------+     Table Scan    +---------------+  
  11. | Source Data File  | => (Data Block Content) =>     |  Dummy Table   |    ============>  | Restore Table |  
  12. |(Without Meta Data)|                                |(With Meta Data)|                   +---------------+  
  13. +-------------------+                                +----------------+  

按照这个原理,我创建了PLSQL包Fy_Recover_Data,并成功恢复了被TRUNCATE的数据:

SQL代码
  1. HELLODBA.COM>insert into demo.truntab select * from demo.t_objects;  
  2.   
  3. 47585 rows created.  
  4.   
  5. HELLODBA.COM>commit;  
  6.   
  7. Commit complete.  
  8.   
  9. HELLODBA.COM>select count(*) from demo.truntab;  
  10.   
  11.   COUNT(*)  
  12. ----------  
  13.      47585  
  14.   
  15. HELLODBA.COM>truncate table demo.truntab;  
  16.   
  17. Table truncated.  
  18.   
  19. HELLODBA.COM>declare  
  20.   2    tgtowner varchar2(30);  
  21.   3    tgttable varchar2(30);  
  22.   4    datapath varchar2(4000);  
  23.   5    datadir varchar2(30);  
  24.   6    rects varchar2(30);  
  25.   7    recfile varchar2(30);  
  26.   8    rstts varchar2(30);  
  27.   9    rstfile varchar2(30);  
  28.  10    blksz number;  
  29.  11    rectab varchar2(30);  
  30.  12    rsttab varchar2(30);  
  31.  13    copyfile varchar2(30);  
  32.  14  begin  
  33.  15    tgtowner := 'DEMO';  
  34.  16    tgttable := 'TRUNTAB';  
  35.  17    datapath := 'D:\oracle\product\10.2.0\oradata\EDGAR\DATAFILE\'; 
  36.  18    datadir := 'FY_DATA_DIR';  
  37.  19    Fy_Recover_data.prepare_files(tgtowner, tgttable, datapath, datadir, rects, recfile, rstts, rstfile, blksz);  
  38.  20    Fy_Recover_data.fill_blocks(tgtowner, tgttable, datadir, rects, recfile, rstts, 8, tgtowner, tgtowner, rectab, rsttab, copyfile);  
  39.  21    Fy_Recover_data.recover_table(tgtowner, tgttable, tgtowner, rectab, tgtowner, rsttab, datadir, datadir, recfile,datadir, copyfile, blksz);  
  40.  22  end;  
  41.  23  /  
  42. Directory Name: FY_DATA_DIR  
  43. Recover Tablespace: FY_REC_DATA; Data File: FY_REC_DATA.DAT  
  44. Restore Tablespace: FY_RST_DATA; Data File: FY_RST_DATA.DAT  
  45. Recover Table: DEMO.TRUNTAB$  
  46. Restore Table: DEMO.TRUNTAB$$  
  47. Data Blocks formatted.  
  48. Copy file of Recover Tablespace: FY_REC_DATA_COPY.DAT  
  49. 373 records recovered  
  50. 328 records recovered  
  51. 334 records recovered  
  52. .. ...  
  53. 285 records recovered  
  54. 275 records recovered  
  55. 235 records recovered  
  56. 47585 records recovered in backup table DEMO.TRUNTAB$$  
  57.   
  58. PL/SQL procedure successfully completed.  
  59.   
  60. HELLODBA.COM>insert into demo.truntab select * from DEMO.TRUNTAB$$;  
  61.   
  62. 47585 rows created.  
  63.   
  64. HELLODBA.COM>commit;  
  65.   
  66. Commit complete.  

数据全部恢复。我们再测试一下压缩表的恢复:

SQL代码
  1. HELLODBA.COM>set serveroutput on format wrapped  
  2. HELLODBA.COM>insert into demo.truntab select * from demo.t_objects;  
  3.   
  4. 47585 rows created.  
  5.   
  6. HELLODBA.COM>commit;  
  7.   
  8. Commit complete.  
  9.   
  10. HELLODBA.COM>alter table demo.truntab move compress;  
  11.   
  12. Table altered.  
  13.   
  14. HELLODBA.COM>select count(*) from demo.truntab;  
  15.   
  16.   COUNT(*)  
  17. ----------  
  18.      95170  
  19.   
  20. HELLODBA.COM>truncate table demo.truntab;  
  21.   
  22. Table truncated.  
  23.   
  24. HELLODBA.COM>declare  
  25.   2    tgtowner varchar2(30);  
  26.   3    tgttable varchar2(30);  
  27.   4    datapath varchar2(4000);  
  28.   5    datadir varchar2(30);  
  29.   6    rects varchar2(30);  
  30.   7    recfile varchar2(30);  
  31.   8    rstts varchar2(30);  
  32.   9    rstfile varchar2(30);  
  33.  10    blksz number;  
  34.  11    rectab varchar2(30);  
  35.  12    rsttab varchar2(30);  
  36.  13    copyfile varchar2(30);  
  37.  14  begin  
  38.  15    tgtowner := 'DEMO';  
  39.  16    tgttable := 'TRUNTAB';  
  40.  17    --datapath := 'D:\oracle\product\10.2.0\oradata\EDGAR\DATAFILE\';  
  41.  18    datadir := 'FY_DATA_DIR'; 
  42.  19    --prepare_files(tgtowner, tgttable, datapath, datadir, rects, recfile, rstts, rstfile, blksz); 
  43.  20    rects := 'FY_REC_DATA'; 
  44.  21    rstts := 'FY_RST_DATA'; 
  45.  22    recfile := 'FY_REC_DATA.DAT';  
  46.  23    Fy_Recover_data.clean_up_ts(rects, rstts);  
  47.  24    --select block_size into blksz from dba_tablespaces ts, dba_tables t where ts.tablespace_name = t.tablespace_name and t.owner = tgtowner and t.table_name = tgttable;  
  48.  25    blksz := 8192;  
  49.  26    Fy_Recover_data.fill_blocks(tgtowner, tgttable, datadir, rects, recfile, rstts, 8, tgtowner, tgtowner, rectab, rsttab, copyfile);  
  50.  27    Fy_Recover_data.recover_table(tgtowner, tgttable, tgtowner, rectab, tgtowner, rsttab, datadir, datadir, recfile,datadir, copyfile, blksz);  
  51.  28  end;  
  52.  29  /  
  53. Recover Table: DEMO.TRUNTAB$  
  54. Restore Table: DEMO.TRUNTAB$$  
  55. Data Blocks formatted.  
  56. Copy file of Recover Tablespace: FY_REC_DATA_COPY.DAT  
  57. 965 records recovered  
  58. 958 records recovered  
  59. 1144 records recovered  
  60. ... ...  
  61. 655 records recovered  
  62. 662 records recovered  
  63. 97 records recovered  
  64. 95170 records recovered in backup table DEMO.TRUNTAB$$  
  65.   
  66. PL/SQL procedure successfully completed.  

同样可以恢复。
利用这个包,应该可以恢复大多数情况下被TRUNCATE掉的数据。并且,还能实现部分FySafe的功能。例如,损毁的数据库的SYSTEM表空间可用的情况下,可以先从SYSTEM表空间恢复数据字典表(对每个数据库版本来说,每个数据字典表的结构和数据对象编号是固定的),然后由数据字典表中信息创建傀儡表,利用表扫描恢复用户数据表。但是,相对FySafe这样比较完善的恢复工具来说,Fy_Recover_Data还是存在一些不足的地方。例如,在没有元数据可用的情况下,FySafe可以猜测出表结构进行数据恢复,而Fy_Recover_Data则无法完成恢复。简单比较如下:

XML/HTML代码
  1.                 Fy_Recover_Data     FySafe 
  2. 单表恢复效率      高                  高  
  3. 多表恢复效率      低                  高  
  4. 压缩表           支持                支持  
  5. 索引组织表       支持                支持  
  6. 分区表           支持                支持  
  7. 行链接/行迁移    不支持               支持  
  8. 标准SQL类型      支持                支持  
  9. BLOB/CLOB       支持Store in Row    支持  
  10. 删除记录恢复     不支持              支持  
  11. 离线恢复        支持                支持  
  12. 无元数据恢复     不支持              支持  
  13. 操作系统平台     全部                全部  
  14. 数据库版本      9i以上              8i以上  
  15. 本地管理表空间   支持                支持  
  16. 数据字典表空间   未测试              支持  
  17. 其他需求        不需要              Java 

最新版的源代码可以在这里下载: Fy_Recover_Data

--- Fuyuncat ---

Top

Copyright ©2005,HelloDBA.Com 保留一切权利

申明
by fuyuncat