一: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.youkuaiyun.com/u014596132/article/details/135858545
《bbed-truncate表恢复》
https://blog.youkuaiyun.com/renyanjie123/article/details/122929522
《Oracle TRUNCATE TABLE原理解析》
https://www.modb.pro/db/156582
《揭秘Oracle数据库truncate原理》
https://blog.youkuaiyun.com/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》