[20250505]drop table的恢复.txt

来源:这里教程网 时间:2026-03-03 21:52:33 作者:

[20250505]drop table的恢复.txt --//尝试drop table的恢复,前提没有备份的情况不得已为之的方法,前面测试提到drop table时清除段头里面的Extent Map以及 --//Auxillary Map的信息。这样通过建立新表的方式也只能像truncate table的方式通过rowid扫描数据块来恢复,做一个完整的测试说 --//明问题: 1.环境: SCOTT@book01p> @ ver2 ============================== PORT_STRING                   : x86_64/Linux 2.4.xx VERSION                       : 21.0.0.0.0 BANNER                        : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production BANNER_FULL                   : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production Version 21.3.0.0.0 BANNER_LEGACY                 : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production CON_ID                        : 0 PL/SQL procedure successfully completed. 2.测试环境建立: SCOTT@book01p> create table t1 as select * from all_objects; Table created. SCOTT@book01p> create table t1_bak as select * from all_objects; Table created. --//t1_bak注意目的为了检验drop table的情况。 SCOTT@book01p> @ o2 t1 SCOTT@book01p> @ pr ============================== O_OWNER                       : SCOTT O_OBJECT_NAME                 : T1 O_OBJECT_TYPE                 : TABLE SEG_PART_NAME                 : O_STATUS                      : VALID OID                           : 125494 D_OID                         : 125494 CREATED                       : 2025-05-05 09:05:53 LAST_DDL_TIME                 : 2025-05-05 09:05:53 PL/SQL procedure successfully completed. SCOTT@book01p> @ seg2 t1 SCOTT@book01p> @ pr ============================== SEG_MB                        : 12 SEG_OWNER                     : SCOTT SEG_SEGMENT_NAME              : T1 SEG_PARTITION_NAME            : SEG_SEGMENT_TYPE              : TABLE SEG_TABLESPACE_NAME           : USERS BLOCKS                        : 1536 HDRFIL                        : 12 HDRBLK                        : 170 PL/SQL procedure successfully completed. 3.恢复测试: SCOTT@book01p> drop table t1 purge ; Table dropped. SCOTT@book01p> create table emp_bak as select * from emp ; Table created. SCOTT@book01p> @ seg2 emp_bak SCOTT@book01p> @ pr ============================== SEG_MB                        : 0 SEG_OWNER                     : SCOTT SEG_SEGMENT_NAME              : EMP_BAK SEG_PARTITION_NAME            : SEG_SEGMENT_TYPE              : TABLE SEG_TABLESPACE_NAME           : USERS BLOCKS                        : 8 HDRFIL                        : 12 HDRBLK                        : 170 PL/SQL procedure successfully completed. --//原来的段头覆盖另外有1个数据块也被破坏。 4.确定drop table的段头。 SYS@book01p> @ versions obj$ OBJ#,DATAOBJ#,name s '' '' versions_operation='D' VERSIONS_STARTTIME   VERSIONS_ENDTIME     VERSIONS_STARTSCN VERSIONS_ENDSCN VERSIONS_XID     V       OBJ#   DATAOBJ# NAME -------------------- -------------------- ----------------- --------------- ---------------- - ---------- ---------- ------ 2025-05-05 09:01:32.                               36801365                 07001F00621E0000 D     125490     125492 BAK_T 2025-05-05 09:04:47.                               36803040                 01000A00341E0000 D     125491     125491 T_BAK 2025-05-05 09:09:44.                               36803972                 08000100C01E0000 D     125494     125494 T1 --//前面2个我测试前删除的表,最后1个OBJ#=125494就是需要恢复的表,实际上你可以通过logminer确定drop table原来的数据段号。 SYS@book01p> @ versions obj$ OBJ#,DATAOBJ#,name s '' '' OBJ#=125494 VERSIONS_STARTTIME   VERSIONS_ENDTIME     VERSIONS_STARTSCN VERSIONS_ENDSCN VERSIONS_XID     V       OBJ#   DATAOBJ# NAME -------------------- -------------------- ----------------- --------------- ---------------- - ---------- ---------- ------ 2025-05-05 09:05:53. 2025-05-05 09:09:44.          36803312        36803972 0A000400701E0000 I     125494     125494 T1 2025-05-05 09:09:44.                               36803972                 08000100C01E0000 D     125494     125494 T1 SYS@book01p> @ as_of tab$ OBJ#,DATAOBJ#,ts#,file#,block# s 36803312 obj#=125494 ROWID                    OBJ#   DATAOBJ#        TS#      FILE#     BLOCK# ------------------ ---------- ---------- ---------- ---------- ---------- AAAAACAABAAAH0yAAA     125494     125494          5         12        170 SYS@book01p> @as_of seg$ * s 36803312  HWMINCR=125494 SYS@book01p> @ pr ============================== ROWID                         : AAAAAIAABAAAIygAAZ FILE#                         : 12 BLOCK#                        : 170 TYPE#                         : 5 TS#                           : 5 BLOCKS                        : 1536 EXTENTS                       : 27 INIEXTS                       : 8 MINEXTS                       : 1 MAXEXTS                       : 2147483645 EXTSIZE                       : 128 EXTPCT                        : 0 USER#                         : 109 LISTS                         : 0 GROUPS                        : 0 BITMAPRANGES                  : 2147483645 CACHEHINT                     : 0 SCANHINT                      : 0 HWMINCR                       : 125494 SPARE1                        : 4325633 SPARE2                        : PL/SQL procedure successfully completed. --//获取drop 前的信息。 --//建立与原来数据结构一样的表,修改段号,在扫描数据块应该可以恢复相关信息。 SCOTT@book01p> create table t1_drop SEGMENT CREATION IMMEDIATE as select * from all_objects where 0=1; Table created. --//建立表空间TSP_AUDIT略。 --//ALTER USER SCOTT QUOTA UNLIMITED ON TSP_AUDIT; SCOTT@book01p> create table bak_t1 tablespace TSP_AUDIT as select * from scott.t1_drop where 0=1; Table created. --//建立新表在原来表空间,只要没有记录插入,不会覆盖(错误!!看后面的测试),另外建立的表要加入SEGMENT CREATION IMMEDIATE,不然 --//oracle认为是空表,无法通过rowid方式读取。扫描获得信息插入的表bak_t1放在另外的表空间,避免覆盖原来的数据文件信息。 SCOTT@book01p> @ o2 t1_drop SCOTT@book01p>  @pr ============================== O_OWNER                       : SCOTT O_OBJECT_NAME                 : T1_DROP O_OBJECT_TYPE                 : TABLE SEG_PART_NAME                 : O_STATUS                      : VALID OID                           : 125500 D_OID                         : 125500 CREATED                       : 2025-05-05 09:19:58 LAST_DDL_TIME                 : 2025-05-05 09:19:58 PL/SQL procedure successfully completed. --//修改t1_drop的数据段号等于原来t1表的数据段号。 SYS@book01p> update (select OBJ#,   DATAOBJ#  ,   OWNER#, NAME from obj$ where obj#=125500) set DATAOBJ#=125494; 1 row updated. SYS@book01p> commit ; Commit complete. SYS@book01p> alter system flush shared_pool; System altered. SYS@book01p> alter system flush shared_pool; System altered. SCOTT@book01p> select OWNER,OBJECT_NAME from t1_drop where rowid = dbms_rowid.rowid_create(1,125494,12,175,0); OWNER                          OBJECT_NAME ------------------------------ ------------------------------ SYS                            I_JIJOIN$ SCOTT@book01p> select OWNER,OBJECT_NAME from t1_drop where rowid = dbms_rowid.rowid_create(1,125494,12,176,0); select OWNER,OBJECT_NAME from t1_drop where rowid = dbms_rowid.rowid_create(1,125494,12,176,0) * ERROR at line 1: ORA-01410: invalid ROWID --//嗯,1个OK,另外1个报错。 SCOTT@book01p> @ seg2 t1_drop SCOTT@book01p>  @pr ============================== SEG_MB                        : 0 SEG_OWNER                     : SCOTT SEG_SEGMENT_NAME              : T1_DROP SEG_PARTITION_NAME            : SEG_SEGMENT_TYPE              : TABLE SEG_TABLESPACE_NAME           : USERS BLOCKS                        : 8 HDRFIL                        : 12 HDRBLK                        : 178 PL/SQL procedure successfully completed. SCOTT@book01p> select * from dba_extents where segment_name='T1_DROP'; OWNER SEGMENT_NAME PARTITION_NAME SEGMENT_TYPE       TABLESPACE_NAME  EXTENT_ID    FILE_ID   BLOCK_ID      BYTES     BLOCKS RELATIVE_FNO ----- ------------ -------------- ------------------ --------------- ---------- ---------- ---------- ---------- ---------- ------------ SCOTT T1_DROP                     TABLE              USERS                    0         12        176      65536          8           12 --//噢,我忽略问题,就是新建立的表要分配新段,建立新的段头,等于覆盖其中3个数据块。先不管这些,继续恢复. SYS@book01p> set timing on SYS@book01p> @ txt/truncT.txt SCOTT T1_DROP SCOTT BAK_T1 PL/SQL procedure successfully completed. Elapsed: 00:01:03.00 SYS@book01p> set timing off SCOTT@book01p> select count(*) from bak_t1;   COUNT(*) ----------      69389 COTT@book01p> select count(*) from t1_bak;   COUNT(*) ----------      69880 SCOTT@book01p> select avg(A) from (select count(*) a ,DBMS_ROWID.ROWID_BLOCK_NUMBER (rowid) from t1_bak group by DBMS_ROWID.ROWID_BLOCK_NUMBER (rowid));     AVG(A) ----------  47.412483      --//69880-69389=491,丢失的了491行。平均每个数据块有47条记录(最小33,最大63),491/47=10.44,大约10-11个数据块没有恢复。 --//建立的表emp_bak占了64K,前面段头3块不算,占用1个数据块,4块含有原来的数据。 --//而建立的新表T1_DROP虽然没有数据,破坏了3个原来的数据块,。剩下5块含有原来的数据。 --//按照以上推断,原始脚本少扫描这16块,9个数据块,其中4个数据块已经破坏。3个其他数据块没有数据。 --//因为里面的扫描范围是: (SELECT relative_fno, block_id, blocks FROM dba_extents WHERE owner = v_owner AND segment_name = v_table AND extent_id = 0           UNION ALL           SELECT relative_fno, block_id, blocks FROM dba_free_space WHERE tablespace_name = v_tablespace           UNION ALL           SELECT relative_fno, block_id, blocks             FROM (SELECT relative_fno                         ,block_id                         ,blocks                         ,ROW_NUMBER () OVER (PARTITION BY owner, segment_name, partition_name ORDER BY extent_id DESC)                             rn                     FROM dba_extents                    WHERE tablespace_name = v_tablespace AND extent_id > 0)            WHERE rn = 1) --//最后1个条件extent_id > 0的情况少考虑drop table建立新表的情况。 SCOTT@book01p> select * from bak_t1 minus select * from t1_bak ; no rows selected --//说明恢复的数据问题。 --//还原: SYS@book01p> update (select OBJ#,   DATAOBJ#  ,   OWNER#, NAME from obj$ where obj#=125500) set DATAOBJ#=125500; 1 row updated. SYS@book01p> commit ; Commit complete. --//如何解决在建表覆盖的问题.可以在原表空间增加一个数据文件: ALTER TABLESPACE USERS   ADD DATAFILE '/u01/oradata/BOOK/book01p/users02.dbf'   SIZE 100M   AUTOEXTEND ON   NEXT 4M   MAXSIZE UNLIMITED; create table t1_drop as select * from all_objects where 0=1; alter  table t1_drop allocate extent ( datafile '/u01/oradata/BOOK/book01p/users02.dbf' size 64K); --//后面的测试发现这种方法行不通。 5.补充测试看看: SCOTT@book01p> drop table t1_drop purge ; Table dropped. ALTER TABLESPACE USERS   ADD DATAFILE '/u01/oradata/BOOK/book01p/users02.dbf'   SIZE 100M   AUTOEXTEND ON   NEXT 4M   MAXSIZE UNLIMITED; SCOTT@book01p> create table t1_drop as select * from all_objects where 0=1; Table created. SCOTT@book01p> alter table t1_drop allocate extent ( datafile '/u01/oradata/BOOK/book01p/users02.dbf' size 8K); Table altered. SCOTT@book01p> @ o2 t1_drop SCOTT@book01p> @ pr ============================== O_OWNER                       : SCOTT O_OBJECT_NAME                 : T1_DROP O_OBJECT_TYPE                 : TABLE SEG_PART_NAME                 : O_STATUS                      : VALID OID                           : 125503 D_OID                         : 125503 CREATED                       : 2025-05-05 09:57:24 LAST_DDL_TIME                 : 2025-05-05 09:57:39 PL/SQL procedure successfully completed. COTT@book01p> select * from dba_extents where segment_name='T1_DROP'; OWNER SEGMENT_NAME PARTITION_NAME SEGMENT_TYPE TABLESPACE_NAME  EXTENT_ID    FILE_ID   BLOCK_ID      BYTES     BLOCKS RELATIVE_FNO ----- ------------ -------------- ------------ --------------- ---------- ---------- ---------- ---------- ---------- ------------ SCOTT T1_DROP                     TABLE        USERS                    0         12        176      65536          8           12 SCOTT T1_DROP                     TABLE        USERS                    1         14        128      65536          8           14 --//不行。如何解决这个问题,另外写一篇blog说明。

相关推荐