一万字,浅谈Oracle数据库truncate原理,无备份,如何恢复!

来源:这里教程网 时间:2026-03-03 20:55:32 作者:

一:truncate table原理说明
二:truncate table恢复思路
三:Oracle ASSM管理,L3、L2、L1块介绍
四:truncate table恢复测试
五:truncate table恢复的其他方法

一:truncate table原理说明 参考文章:《Oracle TRUNCATE TABLE原理解析》 链接:

https://www.modb.pro/db/156582

Oracle数据库,truncate table时只产生非常少的redo和undo,就实现了清空表数据并降低表HWM的功能。可以通过10046以及redo dump去分析truncate的整个操作过程,详细过程可以参考上面的链接,本文直接引用上面文章的结论: truncate table操作时,实际上自动完成以下操作: 对基表的修改主要是:

(1)修改obj$,tab$的dataobj#;
(2)修改seg$的对应信息如(extents,blocks,hwmincr等等);
(3)删除tab_stats$对应对象的统计信息;

对于L3位图块(segment header):

(1)修改块的dataobj#;
(2)修改LHWM和HHWM;
(3)修改extent map、aux map以及extents个数;

对于L2位图块:

(1)删除L1 ranges;
(2)修改L2块的dataobj#;

对于第一个L1位图块:

(1)修改第一个L1块的dataobj#;
(2)set hwm为ext#为0的第3+1个块(即段头块+1);

对于HWM block所属的L1位图块:

(1)clear HWM flag;

可以看到,Oracle truncate table的实质是在不修改数据块的情况下,通过修改segment header的data_object_id,hwm,extent map,aux map等信息来实现清空表的目的,其中还涉及数据字典基表以及L1、L2位图块的修改,truncate操作存储数据的数据块没有产生任何redo和undo,但是segment header,位图块,数据字典基表还是会产生redo和undo。 二:truncate table恢复思路 通过上面的truncate table原理可以知道,实际上并没有删除数据块里的数据,如果truncate table后,对应数据块没有数据写入(可以将对应的表空间改成只读),理论上可以找回数据,具体步骤入职: 对基表的修改,改回原值:

(1)修改obj$,tab$的dataobj#;
(2)修改seg$的对应信息如(extents,blocks,hwmincr ...);

对于L3位图块(segment header),改回原值:

(1)修改块的dataobj#;
(2)修改LHWM和HHWM;
(3)修改extent map、aux map以及extents个数;

对于L2位图块,改回原值:

(1)修改L2块的dataobj#;

对于第一个L1位图块,改回原值:

(1)修改第一个L1块的dataobj#;
(2)set hwm为ext#为0的第3+1个块(即段头块+1);

而改回原值的信息,可以通过dump对应数据块、redo log file(或archive log file)获得。 三:Oracle ASSM管理,L3、L2、L1块介绍 什么是L1,L2,L3位图块?什么又是hwm,extent map,aux map ...? 参考书籍:《Oracle内核技术解密》-吕海波 参考文章:《Oracle官方文档》 链接:

https://docs.oracle.com/cd/E11882_01/server.112/e40540/logical.htm#CNCPT301

Oracle数据库为数据库中的所有数据分配逻辑空间。数据库空间分配的逻辑单元是data blocks, extents, segments和tablespace。在物理层面,数据存储在磁盘上的数据文件中。数据文件中的数据存储在操作系统块中。 先看看表空间管理的两种方式: Logical Space Management Oracle数据库使用Logical Space Management来跟踪和分配表空间中的extents。当数据库对象需要一个extents时,数据库必须有一种查找和分配它的方法。同样,当对象不再需要一个extents时,数据库也必须有一个使空闲extents可用的方法。 Oracle创建表空间时,可以选择两种类型,默认是Locally managed tablespaces: (1)Locally managed tablespaces (default) 数据库使用表空间本身中的位图来管理数据块。因此,本地管理的表空间有一部分表空间专门用于位图。 在表空间中,数据库可以通过自动段空间管理(ASSM)或手动段空间管理来管理段(MSSM)。 (2)Dictionary-managed tablespaces 数据库使用数据字典来管理extents。 Dictionary-Managed Tablespaces 字典管理的表空间使用数据字典来管理其范围。每当分配或释放数据块以供重用时,Oracle数据库都会更新数据字典中的表。例如,当一个表需要一个区段时,数据库会查询数据字典表,并搜索空闲区段。如果数据库找到空间,那么它会修改一个数据字典表,并将一行插入到另一个表中。通过这种方式,数据库通过修改和移动数据来管理空间。 数据库在后台执行以获取数据库对象空间的SQL是递归SQL。频繁使用递归SQL会对性能产生负面影响,因为数据字典的更新必须序列化。本地管理的表空间是默认的,可以避免这种性能问题。 Locally Managed Tablespaces 本地化管理的表空间 本地管理的表空间在数据文件头中维护一个位图,以跟踪数据文件体中的可用和已用空间。每个比特对应一组块。当分配或释放空间时,Oracle数据库会更改位图值以反映块的新状态。 下图是位图管理存储的概念表示。标头中的1表示已用空间,而0表示可用空间。 段空间的管理: 段空间管理是从包含段的表空间继承的属性。在本地管理的表空间中,数据库可以自动或手动管理段。例如,表空间用户中的段可以自动管理(ASSM),而表空间工具中的段则可以手动管理(MSSM)。 1.自动分段空间管理(ASSM) Automatic Segment Space Management ASSM方法使用位图来管理空间。位图具有以下优势: (1)简化管理: ASSM避免了手动确定许多存储参数的正确设置的需要。只有一个关键的SQL参数控制空间分配:PCTFREE。此参数指定块中为将来更新保留的空间百分比。 (2)提高并发性: 多个事务可以搜索单独的空闲数据块列表,从而减少争用和等待。对于许多标准工作负载,使用ASSM的应用程序性能优于使用MSSM的经过良好调优的应用程序的性能。 2.手动分段空间管理 Manual Segment Space Management 传统的MSSM方法使用一个称为空闲列表的链表来管理段中的空闲空间。对于具有可用空间的数据库对象,可用列表跟踪高水位线(HWM)下的块,HWM是已使用和尚未使用的段空间之间的分界线。当使用块时,数据库会根据需要将块放在空闲列表上或从空闲列表中删除块。 除了PCTFREE,MSSM还要求您使用SQL参数(如PCTUSED、FREELIST和FREELIST GROUP)控制空间分配。PCTUSED设置当前使用的块中必须存在的可用空间百分比,以便数据库将其放入可用列表。例如,如果在CREATE TABLE语句中将PCTUSED设置为40,则在块空间使用率低于40%之前,无法将行插入到段中的块中。 作为示例,假设您在表中插入一行。数据库检查表中第一个可用块的空闲列表。如果该行无法放入块中,并且块中已使用的空间大于或等于PCTUSED,则数据库会从列表中删除该块并搜索另一个块。如果从块中删除行,则数据库会检查块中已使用的空间现在是否小于PCTUSED。如果是这样,则数据库将块放置在空闲列表的开头。 一个对象可能有多个自由列表。这样,对表执行DML的多个会话可以使用不同的列表,从而减少争用。每个数据库会话在其会话期间只使用一个空闲列表。 位图块的作用: 记录表空间中区的分配情况。位图块中每一个二进制位对应一个区是否被分配给某个表、索引等对象。 如果第一个二进制位为0说明表空间中第一个区未分配,如果为1说明已分配;第二个二进制位对应第二个区,以此类推。 位图块又分为两部分,其中第一个位图块又被当作位图段头(Bitmapped File Space Header),1号块是文件头,2号块是位图头,在Oracle 10g中,3到8号块是位图数据块,共6个位图块,如果块大小是8KB,一个是48KB字节,每个字节8个二进制位,一共393216个二进制位。 每个二进制对应一个区,一共393216个区,在Oracle 11g更多。 Oracle ASSM管理,L3、L2、L1块介绍: ASSM的整体结构是3层位图块+数据库,即4层树状结构。 第一层位图块称为L3块,一个L3块中可以存放多个L2块地址,一个L2块可以存放多个L1块,一个L1块中可以存放多个数据块地址。 Oracle如何使用4层树状结构(3层位图块+数据块)来确定向哪个块中插入数据?

1.查找数据字典(dba_segments对应的基表),确定段头位置。
2.在段头中找到第一个L2块位置信息;
3.在L2块中根据执行插入操作进程的PID号,做HASH运算,得到一个随机数N,在L2中,找到第N个L1块的位置信息;
4.到第3步中确定的L1块中,再根据执行插入操作进程PID号,做HASH运算,得到一个随机数M,在L1中找到第M号的数据库;
5.向第M号数据块中插入。

四:truncate table恢复测试 参考文章:

《bbed修复truncate表(未被覆盖)》
https://blog.csdn.net/u014596132/article/details/135858545
《bbed-truncate表恢复》
https://blog.csdn.net/renyanjie123/article/details/122929522
《Oracle TRUNCATE TABLE原理解析》
https://www.modb.pro/db/156582
《揭秘Oracle数据库truncate原理》
https://blog.csdn.net/weixin_30907935/article/details/96765216

注意:仅供参考,请勿用于生产环境! 数据库版本:Oracle 11.2.0.4.0创建测试数据

[oracle@cjc-db-01 ~]$ sqlplus cjc/******
create table t1123(id int,name varchar(10),adate varchar(20));
insert into t1123 values(1,'chen','20241123');
insert into t1123 values(2,'ju','20241124');
insert into t1123 values(3,'chao','20241125');
insert into t1123 values(4,'yyy','20241126');
insert into t1123 values(5,'zzz','20241127');
commit;

查看段头块 ````

SQL> select HEADER_FILE,HEADER_BLOCK from dba_segments where owner='CJC' and segment_name='T1123';
HEADER_FILE HEADER_BLOCK
----------- ------------
   5      282

```` 查看 extent_id ````

SQL> select extent_id,file_id,block_id,blocks from dba_extents where segment_name='T1123' and owner='CJC';
 EXTENT_ID    FILE_ID    BLOCK_ID     BLOCKS
---------- ---------- ---------- ----------
  0     5      280   8

```` 查看数据所在数据块 ````

SQL> select dbms_rowid.rowid_relative_fno(rowid) file#,dbms_rowid.rowid_block_number(rowid) block#,id,name,adate from cjc.t1123;
     FILE#     BLOCK#       ID NAME     ADATE
---------- ---------- ---------- ---------- --------------------
  5   287        1 chen     20241123
  5   287        2 ju     20241124
  5   287        3 chao     20241125
  5   287        4 yyy     20241126
  5   287        5 zzz     20241127

```` 通过bbed dump功能,分别查看当前CJC.T1123表对应L3、L2、L1块信息: 已知 offset 信息如下: ````

offset 36:  total extents
offset 40:  total blocks
offset 48:  HWM所在的ext#
offset 52:  HWM所在的ext#的第几个block(从0开始)
offset 56:  HWM所在的ext#的extblocks
offset 60:  HWM所在的dba地址
offset 76:  HWM下有多少个block
offset 92:  LHWM所在的ext#
offset 96:  LHWM所在的ext#的第几个block(从0开始)
offset 100: LHWM所在的ext#的exttsize
offset 104: LHWM所在的dba地址
offset 2736:aux map信息,ext#为0的L1 dba
offset 2740:aux map信息,ext#为0的data dba
offset 2744:aux map信息,ext#为1的L1 dba
offset 2748:aux map信息,ext#为1的data dba
offset 5192:Second Level Bitmap block DBAs

```` ````

[oracle@cjc-db-01 trace]$ bbed
BBED: Release 2.0.0.0.0 - Limited Production on Sat Nov 23 15:38:52 2024
Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.
************* !!! For Oracle Internal Use only !!! ***************
BBED> set file 5 block 282
    FILE#              5
    BLOCK#          282
````
段头块的第一个offset是23
````
BBED> dump /v offset 0 count 20
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 282     Offsets:    0 to   19  Dba:0x0140011a
-------------------------------------------------------
 23a20000 1a014001 42390f00 00000304 l #.....@.B9......
 e6fe0000                            l ....
 <16 bytes per line>
````
段头块指向的L2位图块在offset 5192的位置
````
BBED> dump /v offset 5192 count 100
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 282     Offsets: 5192 to 5291  Dba:0x0140011a
-------------------------------------------------------
 19014001 00000000 00000000 00000000 l ..@.............
 00000000 00000000 00000000 00000000 l ................
 00000000 00000000 00000000 00000000 l ................
 00000000 00000000 00000000 00000000 l ................
 00000000 00000000 00000000 00000000 l ................
 00000000 00000000 00000000 00000000 l ................
 00000000                            l ....
 <16 bytes per line>
````
可以看到L2位图块是19014001,反序后是01400119,这里只有1个L2块,转换为对应的文件号和块号。
L2块:fileno=5,blockno=281。
````
select 
dbms_utility.data_block_address_file(to_number('01400119','xxxxxxxx')) as fileno,
dbms_utility.data_block_address_block(to_number('01400119','xxxxxxxx')) as blockno
from dual;
    FILENO    BLOCKNO
---------- ----------
  5   281
````
L2的第一个offset是21
````
BBED> set file 5 block 281
    FILE#              5
    BLOCK#          281
BBED> dump /v offset 0 count 20
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 281     Offsets:    0 to   19  Dba:0x01400119
-------------------------------------------------------
 21a20000 19014001 3c390f00 00000204 l !.....@.<9......
 12d20000                            l ....
 <16 bytes per line>
````
L2指向L1数据块的位置从offset 116开始
````
BBED> dump /v offset 116 count 100
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 281     Offsets:  116 to  215  Dba:0x01400119
-------------------------------------------------------
 18014001 05000100 00000000 00000000 l ..@.............
 00000000 00000000 00000000 00000000 l ................
 00000000 00000000 00000000 00000000 l ................
 00000000 00000000 00000000 00000000 l ................
 00000000 00000000 00000000 00000000 l ................
 00000000 00000000 00000000 00000000 l ................
 00000000                            l ....
 <16 bytes per line>
````
其中:18014001 反序 01400118,查询对应的数据块号;
L1块:fileno=5,blockno=280。
````
select 
dbms_utility.data_block_address_file(to_number('01400118','xxxxxxxx')) as fileno,
dbms_utility.data_block_address_block(to_number('01400118','xxxxxxxx')) as blockno 
from dual;
    FILENO    BLOCKNO
---------- ----------
  5   280
````
查看L1位图块指向的数据块:
````
BBED> dump /v offset 0 count 10
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 280     Offsets:    0 to    9  Dba:0x01400118
-------------------------------------------------------
 20a20000 18014001 4239              l  .....@.B9
 <16 bytes per line>
````
L1的第一个offset是20,L1指向数据块的位置从offset 204开始
````
BBED> dump /v offset 204 count 100
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 280     Offsets:  204 to  303  Dba:0x01400118
-------------------------------------------------------
 18014001 08000000 00000000 00000000 l ..@.............
 00000000 00000000 00000000 00000000 l ................
 00000000 00000000 00000000 00000000 l ................
 00000000 00000000 00000000 00000000 l ................
 00000000 00000000 00000000 00000000 l ................
 00000000 00000000 00000000 00000000 l ................
 00000000                            l ....
 <16 bytes per line>
````

18014001 表示 file 5 block 280,08000000 表示 这个块后面的连续7个块,也就是数据块号是287。 总结: L3块(段头块):282 L2块:281 L1块:280 数据块:287 可以 dump L3,L2,L1块,有更多的信息: L3块(段头块):282 ````

alter system dump datafile 5 block 282;
select value from v$diag_info where name ='Default Trace File';
VALUE
--------------------------------------------------------------------------------
/oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_13608.trc
SQL> ho cp /oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_13608.trc /home/oracle/trace
````
L2块:281
````
alter system dump datafile 5 block 281;
---/oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_15178.trc
````
在trace里可以看到 Dump of Second Level Bitmap Block 信息。
L1块:280
````
alter system dump datafile 5 block 280;
---/oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_15268.trc
````
在trace里可以看到 Dump of First Level Bitmap Block 信息。
数据块:287
````
alter system dump datafile 5 block 287;
---/oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_15289.trc
ho cp /oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_15178.trc /home/oracle/trace
ho cp /oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_15268.trc /home/oracle/trace
ho cp /oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_15289.trc /home/oracle/trace
````
继续参看 HWM
````
BBED> dump /v offset 48 count 16
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 282     Offsets:   48 to   63  Dba:0x0140011a
-------------------------------------------------------
 00000000 08000000 08000000 20014001 l ............ .@.
 <16 bytes per line>
````
查看 LHWM
````
BBED> dump /v offset 92 count 16
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 282     Offsets:   92 to  107  Dba:0x0140011a
-------------------------------------------------------
 00000000 08000000 08000000 20014001 l ............ .@.
 <16 bytes per line>
````
HWM 和 LHWM 都是 20014001,反序后为 01400120
````
select 
dbms_utility.data_block_address_file(to_number('01400120','xxxxxxxx')) as fileno,
dbms_utility.data_block_address_block(to_number('01400120','xxxxxxxx')) as blockno 
from dual;
    FILENO    BLOCKNO
---------- ----------
  5   288
````
也可以在dump trace里查看:
````
[oracle@cjc-db-01 trace]$ cat cjc_ora_13608.trc|grep -i HWM
  Level 1 BMB for High HWM block: 0x01400118
  Level 1 BMB for Low HWM block: 0x01400118
[oracle@cjc-db-01 trace]$ cat cjc_ora_13608.trc|grep -i HighWater
      Highwater::  0x01400120  ext#: 0      blk#: 8      ext size: 8     
  Low HighWater Mark : 
      Highwater::  0x01400120  ext#: 0      blk#: 8      ext size: 8

模拟误删除truncate

SQL> select * from cjc.t1123;
    ID NAME       ADATE
---------- ---------- --------------------
  1 chen       20241123
  2 ju       20241124
  3 chao       20241125
  4 yyy       20241126
  5 zzz       20241127
SQL> truncate table cjc.t1123;
Table truncated.
SQL> select * from cjc.t1123;
no rows selected

 问题分析: 先dump 文件头 和 current redo log file。

1.dump file header
````
alter system dump datafile 5 block 282;
select value from v$diag_info where name ='Default Trace File';
VALUE
--------------------------------------------------------------------------------
/oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_23315.trc
SQL> ho cp /oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_23315.trc /home/oracle/trace
````
2.dump current redo log file
````
set line 100
col member for a50
select a.group#,a.status,b.member from v$log a,v$logfile b where a.group#=b.group#;
    GROUP# STATUS     MEMBER
---------- ---------------- --------------------------------------------------
  3 INACTIVE     /oracle/app/oracle/oradata/cjc/redo03.log
  2 INACTIVE     /oracle/app/oracle/oradata/cjc/redo02.log
  1 CURRENT     /oracle/app/oracle/oradata/cjc/redo01.log
alter system dump logfile '/oracle/app/oracle/oradata/cjc/redo01.log';
select value from v$diag_info where name ='Default Trace File';
VALUE
--------------------------------------------------------------------------------
/oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_23563.trc
SQL> ho cp /oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_23563.trc /home/oracle/trace
````
cjc.t1123表被truncate后,DATA_OBJECT_ID由87395变成了87397
````
[oracle@cjc-db-01 trace]$ cat /home/oracle/trace/cjc_ora_23563.trc|grep -i "CDOBJ"
   CDOBJ: new object number:87395
   CDOBJ: new object number:87397
SQL> select object_id, data_object_id from dba_objects where owner='CJC' and object_name='T1123';
 OBJECT_ID DATA_OBJECT_ID
---------- --------------
     87395     87397
SQL> select object_name from dba_objects where object_id=87397;
no rows selected

数据恢复: 一:修改数据字典表

SQL> select obj#,dataobj# from sys.obj$ where obj#=87395;
      OBJ#   DATAOBJ#
---------- ----------
     87395    87397
SQL> select obj#,dataobj# from sys.tab$ where obj#=87395;
      OBJ#   DATAOBJ#
---------- ----------
     87395    87397
SQL> update sys.obj$ set dataobj#=87395 where obj#=87395;
1 row updated.
SQL> update sys.tab$ set dataobj#=87395 where obj#=87395;
1 row updated.
SQL> commit;
Commit complete.
SQL> select * from cjc.t1123;
no rows selected

二、修改段头,L1、L2块头的dataobj# 通过redo log file dump信息查看:

L2 block:
````
[oracle@cjc-db-01 trace]$ cat /home/oracle/trace/cjc_ora_23315.trc |grep "Last Level II BMB"
  Last Level II BMB:  0x01400119
````
L1 block:
````
[oracle@cjc-db-01 trace]$ cat /home/oracle/trace/cjc_ora_23315.trc |grep "Last Level 1 BMB"
  Last Level 1 BMB:  0x01400118
````
L2块 --offset 104
````
select 
dbms_utility.data_block_address_file(to_number('01400119','xxxxxxxx')) as fileno,
dbms_utility.data_block_address_block(to_number('01400119','xxxxxxxx')) as blockno 
from dual;
    FILENO    BLOCKNO
---------- ----------
  5   281
````
L1块 --offset 192
````
select 
dbms_utility.data_block_address_file(to_number('01400118','xxxxxxxx')) as fileno,
dbms_utility.data_block_address_block(to_number('01400118','xxxxxxxx')) as blockno 
from dual;
    FILENO    BLOCKNO
---------- ----------
  5   280
````
段头块:
````
SQL> select to_char('87395','xxxxxxxx') old_dataobj,to_char('87397','xxxxxxxx') new_dataobj from dual;
OLD_DATAO NEW_DATAO
--------- ---------
    15563     15565
[oracle@cjc-db-01 trace]$ cat /home/oracle/trace/cjc_ora_23315.trc |grep 15565
7FFFF4E4DB10 00015565 10000000 01400118 00000008  [eU........@.....]
[oracle@cjc-db-01 trace]$ cat /home/oracle/trace/cjc_ora_23315.trc |grep 15563
[oracle@cjc-db-01 trace]$
````
修改段头块L3:
file 5 block 282 --offset 272
````
BBED> set file 5 block 282
    FILE#              5
    BLOCK#          282
BBED> dump /v offset 272 count 16
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 282     Offsets:  272 to  287  Dba:0x0140011a
-------------------------------------------------------
 65550100 00000010 18014001 08000000 l eU........@.....
````
需要将 00015565 改成 00015563
其中:
00015565 反序为 65550100
00015563 反序为 63550100
也就是将65改成63。
````
BBED> modify /x 63 offset 272
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 282              Offsets:  272 to  287           Dba:0x0140011a
------------------------------------------------------------------------
 63550100 00000010 18014001 08000000 
````
继续修改L2:
````
BBED> set file 5 block 281
    FILE#              5
    BLOCK#          281
BBED> dump /v offset 104 count 16
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 281     Offsets:  104 to  119  Dba:0x01400119
-------------------------------------------------------
 65550100 01000000 00000000 18014001 l eU............@.
BBED> modify /x 63 offset 104
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 281              Offsets:  104 to  119           Dba:0x01400119
------------------------------------------------------------------------
 63550100 01000000 00000000 18014001 
````
继续修改L1:
````
BBED> set file 5 block 280
    FILE#              5
    BLOCK#          280
BBED> dump /v offset 192 count 16
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 280     Offsets:  192 to  207  Dba:0x01400118
-------------------------------------------------------
 65550100 ed570f00 00000000 18014001 l eU...W........@.
 <16 bytes per line>
BBED> modify /x 63 offset 192
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 280              Offsets:  192 to  207           Dba:0x01400118
------------------------------------------------------------------------
 63550100 ed570f00 00000000 18014001 
````
提交
````
BBED> sum apply
Check value for File 5, Block 280:
current = 0x9640, required = 0x9640
````
查看数据,还需要继续修改HWM
````
SQL> alter system flush buffer_cache;
System altered.
SQL> select * from cjc.t1123;
select * from cjc.t1123
*
ERROR at line 1:
ORA-08103: object no longer exists

```` 三:修改高水位线 ````

[oracle@cjc-db-01 ~]$ cat /home/oracle/trace/cjc_ora_23563.trc|grep -i "Highwater"
      Highwater::  0x01400120  ext#: 0      blk#: 8      ext size: 8     
      Highwater::  0x01400120  ext#: 0      blk#: 8      ext size: 8     
      Highwater::  0x0140011b  ext#: 0      blk#: 3      ext size: 8     
      Highwater::  0x0140011b  ext#: 0      blk#: 3      ext size: 8     
Opcode: 32      Highwater::  0x0140011b  ext#: 0      blk#: 3      ext size: 8  
````
高水位信息从
````
Highwater::  0x01400120  ext#: 0      blk#: 8      ext size: 8 
````
变成
````
Highwater::  0x0140011b  ext#: 0      blk#: 3      ext size: 8
````
其中,需要将
````
blk#:3 改成 blk#:8
0x0140011b 改成 0x01400120
````
其中 0x01400120 反序为 20014001
需要改回原值:
````
select to_char(8,'xxxxx') ext from dual;
EXT
------
     8
````
开始修改:
````
BBED> set file 5 block 282
    FILE#              5
    BLOCK#          282
BBED> dump /v offset 52 count 16
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 282     Offsets:   52 to   67  Dba:0x0140011a
-------------------------------------------------------
 03000000 08000000 1b014001 00000000 l ..........@.....
BBED> m /x 08 offset 52
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 282              Offsets:   52 to   67           Dba:0x0140011a
------------------------------------------------------------------------
 08000000 08000000 1b014001 00000000 
BBED> sum apply
Check value for File 5, Block 282:
current = 0xfeed, required = 0xfeed
BBED> verify
BBED> dump /v offset 60 count 16
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 282     Offsets:   60 to   75  Dba:0x0140011a
-------------------------------------------------------
 1b014001 00000000 00000000 00000000 l ..@.............
 <16 bytes per line>
BBED> m /x 20014001 offset 60
 File: /oracle/app/oracle/oradata/cjc/cjc01.dbf (5)
 Block: 282              Offsets:   60 to   75           Dba:0x0140011a
------------------------------------------------------------------------
 20014001 00000000 00000000 00000000 
 <32 bytes per line>
BBED> sum apply;
Check value for File 5, Block 282:
current = 0xfed6, required = 0xfed6
````
同理,修改 LHWM
````
BBED> m /x 08 offset 96
BBED> m /x 20014001 offset 104
BBED> sum apply;
BBED> verify
DBVERIFY - Verification starting
FILE = /oracle/app/oracle/oradata/cjc/cjc01.dbf
BLOCK = 282
DBVERIFY - Verification complete
Total Blocks Examined         : 1
Total Blocks Processed (Data) : 0
Total Blocks Failing   (Data) : 0
Total Blocks Processed (Index): 0
Total Blocks Failing   (Index): 0
Total Blocks Empty            : 0
Total Blocks Marked Corrupt   : 0
Total Blocks Influx           : 0
Message 531 not found;  product=RDBMS; facility=BBED
````

数据恢复成功: ````

SQL> alter system flush buffer_cache;
System altered.
SQL> alter system flush shared_pool;
System altered.
SQL>  select * from cjc.t1123;
    ID NAME       ADATE
---------- ---------- --------------------
  1 chen       20241123
  2 ju       20241124
  3 chao       20241125
  4 yyy       20241126
  5 zzz       20241127
````
立即将数据复制到另一张表里
````
SQL> create table cjc.t1123_A as select * from cjc.t1123;
Table created.
````
因为此时原表不支持 insert
````
SQL> insert into cjc.t1123 values(6,'xxx','20241128');
insert into cjc.t1123 values(6,'xxx','20241128')
                *
ERROR at line 1:
ORA-01578: ORACLE data block corrupted (file # 5, block # 281)
ORA-01110: data file 5: '/oracle/app/oracle/oradata/cjc/cjc01.dbf'
````
告警日志如下:
````
Sat Nov 23 20:28:38 2024
ALTER SYSTEM: Flushing buffer cache
Sat Nov 23 20:31:01 2024
Hex dump of (file 5, block 281) in trace file /oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_23563.trc
Corrupt block relative dba: 0x01400119 (file 5, block 281)
Bad check value found during buffer read
Data in bad block:
 type: 33 format: 2 rdba: 0x01400119
 last change scn: 0x0000.000f57ef seq: 0x2 flg: 0x04
 spare1: 0x0 spare2: 0x0 spare3: 0x0
 consistency value in tail: 0x57ef2102
 check value in block header: 0xd0ce
 computed block checksum: 0x6
Reading datafile '/oracle/app/oracle/oradata/cjc/cjc01.dbf' for corruption at rdba: 0x01400119 (file 5, block 281)
Reread (file 5, block 281) found same corrupt data (no logical check)
Sat Nov 23 20:31:01 2024
Corrupt Block Found
         TSN = 6, TSNAME = CJC
         RFN = 5, BLK = 281, RDBA = 20971801
         OBJN = 87395, OBJD = 87395, OBJECT = , SUBOBJECT = 
         SEGMENT OWNER = , SEGMENT TYPE = 
Errors in file /oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_23563.trc  (incident=175410):
ORA-01578: ORACLE data block corrupted (file # 5, block # 281)
ORA-01110: data file 5: '/oracle/app/oracle/oradata/cjc/cjc01.dbf'
Incident details in: /oracle/app/oracle/diag/rdbms/cjc/cjc/incident/incdir_175410/cjc_ora_23563_i175410.trc
Sat Nov 23 20:31:03 2024
Dumping diagnostic data in directory=[cdmp_20241123203103], requested by (instance=1, osid=23563), summary=[incident=175410].
Sat Nov 23 20:31:03 2024
Sweep [inc][175410]: completed
Sweep [inc2][175410]: completed
````
删除原表
````
SQL> drop table cjc.t1123 purge;
````
重命名新表
````
SQL> conn cjc/******
SQL> rename t1123_A to t1123;
SQL> insert into cjc.t1123 values(6,'xxx','20241128');
SQL> commit;
SQL> select * from cjc.t1123;
    ID NAME       ADATE
---------- ---------- --------------------
  1 chen       20241123
  2 ju       20241124
  3 chao       20241125
  4 yyy       20241126
  5 zzz       20241127
  6 xxx       20241128
6 rows selected.
````

如果上述操作结束后,仍然查不到数据,还需要继续修改段头的extent map、Auxillary Map、extent个数等。 五:truncate table恢复的其他方法 ## 1.OUD恢复 详细内容参考我的另一篇文字: 《震惊,一单几十个W的Oracle非常规恢复,原来这么简单?》

https://mp.weixin.qq.com/s?__biz=MzI5OTY2NzQ5MA==&mid=2247489614&idx=1&sn=ddf27726c53854d7e4bbc32d2489a29b&chksm=ec924103dbe5c815817ccb71315d4d15e70b34e99e359cbc137de705fe52a93e15ce724edd72&token=451153029&lang=zh_CN#rd
Oracle Delete表恢复(ODU)
Oracle Truncate表恢复(ODU)
Oracle Drop表(purge)恢复(ODU)

2.FY_Recover_Data.pck 参考文章:《Oracle使用fy_recover_data恢复truncate删除的数据》 链接:

https://www.cnblogs.com/lijiaman/p/12747658.html

测试过程如下:

SQL> conn cjc/******
SQL> select id,name,to_char(time,'YYYY-MM-DD HH24:MI:SS') time from cjc.t1121;
ID NAME       TIME
---------- ---------- -------------------
1 xxx        2024-11-21 12:32:58
2 cjc        2024-11-21 12:33:39
3 chen       2024-11-21 12:33:44
4 yyy        2024-11-21 12:33:49
5 zzz        2024-11-21 12:33:55
````
删除数据
````
SQL> truncate table cjc.t1121;
Table truncated.
````
安装工具
````
SQL> conn / as sysdba
SQL> @/home/oracle/tmp/20241121/FY_Recover_Data.pck
Enter value for files:
old 30:   --   1. Temp Restore and Recover tablespace & files                     ---
new 30:   --   1. Temp Restore and Recover tablespace                      ---
Package created.
Package body created.
````
执行恢复
````
SQL> set time on
12:44:16 SQL> set serveroutput on
12:44:20 SQL> exec fy_recover_data.recover_truncated_table('CJC','T1121');
12:44:26: New Directory Name: FY_DATA_DIR
12:44:26: Recover Tablespace: FY_REC_DATA; Data File: FY_REC_DATA.DAT
12:44:26: Restore Tablespace: FY_RST_DATA; Data File: FY_RST_DATA.DAT
12:44:26: Recover Table: CJC.T1121$
12:44:27: Restore Table: CJC.T1121$$
12:44:32: Copy file of Recover Tablespace: FY_REC_DATA_COPY.DAT
12:44:32: begin to recover table CJC.T1121
12:44:32: New Directory Name: TMP_HF_DIR
12:44:32: Recovering data in datafile /db/oradata/cjc/cjc01.dbf
12:44:32: Use existing Directory Name: TMP_HF_DIR
12:44:33: 5 truncated data blocks found.
12:44:33: 5 records recovered in backup table CJC.T1121$$
12:44:33: Total: 5 truncated data blocks found.
12:44:33: Total: 5 records recovered in backup table CJC.T1121$$
12:44:33: Recovery completed.
12:44:33: Data has been recovered to CJC.T1121$$
PL/SQL procedure successfully completed.
````
查看数据,已经自动将数据恢复到 CJC.T1121$$ 表
````
12:46:58 SQL> select id,name,to_char(time,'YYYY-MM-DD HH24:MI:SS') time from cjc.t1121;
no rows selected
12:46:59 SQL> select id,name,to_char(time,'YYYY-MM-DD HH24:MI:SS') time from cjc.t1121$$;
ID NAME       TIME
---------- ---------- -------------------
1 xxx        2024-11-21 12:32:58
2 cjc        2024-11-21 12:33:39
3 chen       2024-11-21 12:33:44
4 yyy        2024-11-21 12:33:49
5 zzz        2024-11-21 12:33:55
````
插入回原表:
````
insert into cjc.t1121 select * from cjc.t1121$$;
commit;
select id,name,to_char(time,'YYYY-MM-DD HH24:MI:SS') time from cjc.t1121;
ID NAME       TIME
---------- ---------- -------------------
1 xxx        2024-11-21 12:32:58
2 cjc        2024-11-21 12:33:39
3 chen       2024-11-21 12:33:44
4 yyy        2024-11-21 12:33:49
5 zzz        2024-11-21 12:33:55
````

3.脚本恢复 参考文章:《Oracle TRUNCATE TABLE恢复-脚本》 链接:

https://www.modb.pro/db/156580

六:总结 可以看到,无备份情况下恢复truncate误删除的表基本都需要一个前提条件:数据块没有新数据写入!而在实际场景中很难实现,除非发现误删除后在第一时间把对应表空间或数据文件设置为只读,这意味着可能影响更多的业务,所以类似的危险操作需要谨慎执行,最后,一定要有备份! ###chenjuchao 20241124### 欢迎关注我的公众号《 IT小Chen

相关推荐