利用Oracle表扫描机制恢复被Truncate的数据

几天前,用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 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值