前 言
01
前面,我已经分享过三篇关于bbed工具使用的文章。今天我想跟大家分享下如何运用bbed工具解析truncate内部原理。当Oracle数据库执行truncate语句后,表中数据实际上并没有被删除,而是修改表的段头信息和回收高水位线。因此,在数据块没有被覆盖之前,通过修改段头、基表和高水位线,就可以找回数据了。如果想掌握bbed工具恢复truncate表的技能,我们必须先弄明白数据块存储结构以及truncate的内部原理。本篇重点介绍数据块存储结构和truncate原理,下一篇我将具体演示如何恢复truncate表数据。
往期文章推荐
02
1、DBA实验手册第1讲 运用bbed工具和SQL语句学习bootstrap$表存储信息
2、DBA实验手册第2讲 运用bbed工具恢复bootstrap$表数据
3、DBA实验手册第3讲 运用bbed工具恢复delete误删除的数据

点击关注公众号,文末有惊喜~

实验环境准备
03
数据库版本:Oracle 12.1.0.2.0 单机版
BBED版本:BBED 2.0.0.0.0
操作系统:Red Hat Enterprise Linux Server 6.5
1、创建测试表空间和测试用户
查看数据文件目录col file_name for a60select file_id,file_name from dba_data_files order by file_id;创建测试表空间create tablespace prdtbs datafile '/u01/app/oracle/oradata/PROD4/prdtbs.dbf' size 20m;查看表空间对应DDL语句,表空间对应的extent和segment都是自动管理模式。set long 9999select dbms_metadata.get_ddl('TABLESPACE', 'PRDTBS') from dual;CREATE TABLESPACE "prdtbs" DATAFILE'/u01/app/oracle/oradata/PROD4/prdtbs.dbf' SIZE 20971520LOGGING ONLINE PERMANENT BLOCKSIZE 8192EXTENT MANAGEMENT LOCAL AUTOALLOCATE DEFAULTNOCOMPRESS SEGMENT SPACE MANAGEMENT AUTO20M大小的表空间是由2560个8k数据块组成SQL> select 20971520/8192 blocks from dual;BLOCKS----------2560
2、创建测试表与测试数据
创建测试用户并授权create user user02 identified by oracle default tablespace prdtbs quota unlimited on prdtbs;grant dba to user02;创建测试表以及10000条的测试数据(为了方便记住)create table user02.TAB11 tablespace prdtbs as select * from dba_objects where rownum<=10000;select count(*) from user02.TAB11;
三级位图块简介
04
对于Truncate单表来说,其实就相当于摧毁我们的一个段,我们数据库中的物理结构是由段、区、块三个构成的。首先我们最需要了解的就是一张“图谱”?图谱是什么?就是一个段的组成结构,而最能表现段的构成的就是一个三级位图块的结构图。三级位图块的结构可以用下图表示(摘自网络):

我们可以看到一个段最先是由段头块构成的,段头其实就是第1个L3块,只有当这些无法记录下的时候,才会产生第2个L3级块,不过这种情况非常少见(参考资料有介绍如何生成L3级块),段头块指向了若干的L2位图块,每个L2位图块又指向了若干L1位图块,L1位图块则指向了真正的数据块。
L1、L2、L3块的作用是为了方便查找数据块。L3中有指向L2的指针,L2有指向L1的指针,L1中有多个数据块的指针和状态。
1、三级位图块(段头)记录二级位图块的地址,每个L3中有多个L2的地址。
2、二级位图块记录了一级位图块的地址,每个L2中有多个L1的地址。
3、一级位图用于管理具体数据块的使用,每个L1中有多个数据块地址。
位图块存储结构
05
1、查看表相关的基本信息,包括对象ID、段头块编号、区的数量与大小、表空间及数据文件编号。
表的OBJ_ID和DATA_OBJCT_ID字段值是相同的,都是94981col object_name for a15col owner for a10select object_id, data_object_id, owner,object_name from dba_objectswhere object_name='TAB11';OBJECT_ID DATA_OBJECT_ID OWNER OBJECT_NAME---------- -------------- ---------- ---------------94981 94981 user02 TAB11数据段存放在2号数据文件,段头块编号为130,是由17个区组成,共256个数据块。col segment_name for a15select SEGMENT_NAME,HEADER_FILE,HEADER_BLOCK,EXTENTS,BLOCKS from dba_segmentswhere tablespace_name='PRDTBS';SEGMENT_NAME HEADER_FILE HEADER_BLOCK EXTENTS BLOCKS--------------- ----------- ------------ ---------- ----------TAB11 2 130 17 256这17个区共由256个数据块组成(8*16+128=256),第1个区(编号为0)实际记录段头信息和位图块信息。set pagesize 100col segment_name for a15select SEGMENT_NAME,extent_id,file_id,block_id,blocks from dba_extentswhere tablespace_name='PRDTBS';SEGMENT_NAME EXTENT_ID FILE_ID BLOCK_ID BLOCKS--------------- ---------- ---------- ---------- ----------TAB11 0 7 128 8TAB11 1 7 136 8TAB11 2 7 144 8TAB11 3 7 152 8TAB11 4 7 160 8TAB11 5 7 168 8TAB11 6 7 176 8TAB11 7 7 184 8TAB11 8 7 192 8TAB11 9 7 200 8TAB11 10 7 208 8TAB11 11 7 216 8TAB11 12 7 224 8TAB11 13 7 232 8TAB11 14 7 240 8TAB11 15 7 248 8TAB11 16 7 256 12817 rows selected.测试表空间对应的TS#编号为14,后面解读数据块的dump记录会使用。SQL> select ts#,name from v$tablespace where name='PRDTBS';TS# NAME---------- ------------------------------14 PRDTBS测试表空间对应的数据文件编号为7,后面解读数据块的dump记录会使用。set linesize 1000col file_name for a60select file_id,file_name from dba_data_files where tablespace_name='PRDTBS';FILE_ID FILE_NAME---------- ------------------------------------------------------------7 /u01/app/oracle/oradata/PROD4/prdtbs.dbf
2、从第1点我们可以知道,段头块的编号为130(数据文件为7),我们通过dump命令将数据段头信息转储到trace文件。dump命令也会把缓存和磁盘里的信息都转储出来。
--清空缓存数据,减少trace文件大小alter system flush SHARED_POOL;alter system flush BUFFER_CACHE;--修改会话级参数,解密数据块内容alter session set "_sga_clear_dump"=true;--查看会话对应trace文件路径alter session set tracefile_identifier='dumpl30block';select value from v$diag_info where name ='Default Trace File';/u01/app/oracle/diag/rdbms/prod4/PROD4/trace/PROD4_ora_18427_dumpl30block.trc--转储数据块alter system dump datafile 7 block 130;
3、段头块(第一个L3块)内容解读
段由哪些区间构成?这个信息我们需要从段头块中获取出来。当你创建一个段后,即使你没有往里面插入任何数据,系统也是会预先分配一些区给你的。所以段头块是那个块,我们可以通过dba_segments查询出来。
Start dump data blocks tsn: 14【表空间编号】 file#:7【数据文件号】minblk 130 maxblk 130【数据块号】Block dump from cache:【从缓存dump数据块信息】Dump of buffer cache at level 4 for pdb=0 tsn=14 rdba=29360258BH (0x743f43d8) file#: 7 rdba: 0x01c00082 (7/130)【数据块位置】 class: 4 ba: 0x74308000set: 5 pool: 3 bsz: 8192【数据块大小】 bsi: 0 sflg: 0 pwc: 0,0dbwrid: 0 obj: 94981 objn: 94981【对象编号】 tsn: [0/14] afn: 7 hint: fhash: [0x7cb9def8,0x7cb9def8] lru: [0x773eb640,0x793eba00]lru-flags: on_auxiliary_listckptq: [NULL] fileq: [NULL]objq: [NULL] objaq: [NULL]st: FREE md: NULL fpin: 'ktswh03: ktscts' fscn: 0x0.5001f2 tch: 0 lfb: 33flags:Block dump from disk:【从磁盘dump数据块信息】Decrypting encrypted buffer before dump.buffer tsn: 14 rdba: 0x01c00082 (7/130)scn: 0x0.50021a seq: 0x01 flg: 0x04 tail: 0x021a2301frmt: 0x02 chkval: 0xd95d type: 0x23=PAGETABLE SEGMENT HEADER【数据块类型0x23:段头】Hex dump of block: st=0, typ_found=1Dump of memory from 0x00007F1446E09000 to 0x00007F1446E0B0007F1446E09000 0000A223 01C00082 0050021A 04010000 [#.........P.....]【0000A223中的23与type: 0x23 对应】0x01c00082表示数据块的存储位置,转换为10进制就是7号数据文件和第130个数据块,可以通过以下两种方法加深理解。BH (0x743f43d8) file#: 7 rdba: 0x01c00082 (7/130)【数据块位置】 class: 4 ba: 0x74308000--PAGETABLE SEGMENT HEADERselect dbms_utility.data_block_address_file(to_number('01c00082','xxxxxxxx')) as file_id,dbms_utility.data_block_address_block(to_number('01c00082','xxxxxxxx')) as block_idfrom dual;FILE_ID BLOCK_ID---------- ----------7 13016进制中两个字符表示1bytes(1个offset),将十六进制0x01c00082按两个字符一步步拆解:DBA=0x01c00082====> 拆解为:0x 01 c0 00 82select number_to_bit(to_number('01','xxxxxxxx')) as bit from dual;--> 00000001select number_to_bit(to_number('c0','xxxxxxxx')) as bit from dual;--> 11000000select number_to_bit(to_number('00','xxxxxxxx')) as bit from dual;--> 00000000select number_to_bit(to_number('82','xxxxxxxx')) as bit from dual;--> 10101010number_to_bit函数完整创建脚本详见《DBA实验手册第1讲》DBA=0x01c00082====> 转换为二进制为:00000001 11000000 00000000 10000010DBA(data block address)===file#(10bit)+block#(22bit)==32bitfile#=00000001 11 -----1号文件block#=000000 00000110 10101010 --> 520号块select bit_to_number('0000000111') as num from dual;--> 7select bit_to_number('0000000000000010000010') as num from dual;--> 130
Extent Control Header 区控制头信息,主要存储高水位、区数量以及二级位置块信息。
Extent Control Header-----------------------------------------------------------------Extent Header:: spare1: 0 spare2: 0 #extents: 17【区数量】 #blocks: 256【块数量明细查看Extent Map】last map 0x00000000 #maps: 0 offset: 2716Highwater:: 0x01c00126 ext#: 16 blk#: 38 ext size: 128【Highwater高水位线的地址DBA--0x01c00126,对应offset为60,是第二个区的第一个块】#blocks in seg. hdr's freelists: 0#blocks below: 166【高水位线下包含166个数据块】mapblk 0x00000000 offset: 16Unlocked--------------------------------------------------------Low HighWater Mark :Highwater:: 0x01c00126 ext#: 16 blk#: 38 ext size: 128【ext#代表区数量、blk#代表块数量】#blocks in seg. hdr's freelists: 0#blocks below: 166【高水位线下包含166个数据块】mapblk 0x00000000 offset: 16Level 1 BMB for High HWM block: 0x01c00100Level 1 BMB for Low HWM block: 0x01c00100--------------------------------------------------------Segment Type: 1 nl2: 1 blksz: 8192 fbsz: 0L2 Array start offset: 0x00001434First Level 3 BMB: 0x00000000L2 Hint for inserts: 0x01c00081【二级位图块的DBA地址(7/129)】Last Level 1 BMB: 0x01c00101Last Level II BMB: 0x01c00081Last Level III BMB: 0x00000000Map Header:: next 0x00000000 #extents: 17 obj#: 94981 flag: 0x10000000【obj#对应数据字典:dba_objects.DATA_OBJECT_ID】Extent Map区地图,段包含几个区,区的起始地址以及长度。Extent Map-----------------------0x01c00080 length: 80x01c00088 length: 80x01c00090 length: 80x01c00098 length: 80x01c000a0 length: 80x01c000a8 length: 80x01c000b0 length: 80x01c000b8 length: 80x01c000c0 length: 80x01c000c8 length: 80x01c000d0 length: 80x01c000d8 length: 80x01c000e0 length: 80x01c000e8 length: 80x01c000f0 length: 80x01c000f8 length: 80x01c00100 length: 128Auxillary Map 辅助区地图,辅助区地址可看出哪几个区内的块共用同一个L1。Auxillary Map--------------------------------------------------------Extent 0 : L1 dba: 0x01c00080 Data dba: 0x01c00083【第一个可用的块的地址】Extent 1 : L1 dba: 0x01c00080 Data dba: 0x01c00088【可以看到这个L1和上一个L1相同,一个L1管理了两个区。】Extent 2 : L1 dba: 0x01c00090 Data dba: 0x01c00091Extent 3 : L1 dba: 0x01c00090 Data dba: 0x01c00098Extent 4 : L1 dba: 0x01c000a0 Data dba: 0x01c000a1Extent 5 : L1 dba: 0x01c000a0 Data dba: 0x01c000a8Extent 6 : L1 dba: 0x01c000b0 Data dba: 0x01c000b1Extent 7 : L1 dba: 0x01c000b0 Data dba: 0x01c000b8Extent 8 : L1 dba: 0x01c000c0 Data dba: 0x01c000c1Extent 9 : L1 dba: 0x01c000c0 Data dba: 0x01c000c8Extent 10 : L1 dba: 0x01c000d0 Data dba: 0x01c000d1Extent 11 : L1 dba: 0x01c000d0 Data dba: 0x01c000d8Extent 12 : L1 dba: 0x01c000e0 Data dba: 0x01c000e1Extent 13 : L1 dba: 0x01c000e0 Data dba: 0x01c000e8Extent 14 : L1 dba: 0x01c000f0 Data dba: 0x01c000f1Extent 15 : L1 dba: 0x01c000f0 Data dba: 0x01c000f8Extent 16 : L1 dba: 0x01c00100 Data dba: 0x01c00102--------------------------------------------------------【二级位图块的DBA,这里只有一个L2,如果有多个L2,都会在这里显示出来的。】Second Level Bitmap block DBAs--------------------------------------------------------DBA 1: 0x01c00081
4、根据上述L3 数据块信息,可以知道L2 数据块的dba值。接下来,我们继续解读L2位图块来寻找相应的L1位图块。
--修改会话级参数,解密数据块内容alter session set "_sga_clear_dump"=true;--查看会话对应trace文件路径alter session set tracefile_identifier='dumpl2block';select value from v$diag_info where name ='Default Trace File';/u01/app/oracle/diag/rdbms/prod4/PROD4/trace/PROD4_ora_18427_dumpl2block.trc--转储数据块alter system dump datafile 7 block 129;L2位图块相比L3位图块信息量较少,主要存储L1的地址信息。BH (0x743e12d8) file#: 7 rdba: 0x01c00081 (7/129) class: 9 ba: 0x74120000set: 6 pool: 3 bsz: 8192 bsi: 0 sflg: 0 pwc: 0,0dbwrid: 0 obj: 94981 objn: 94981 tsn: [0/14] afn: 7 hint: fhash: [0x7c457078,0x7c457078] lru: [0x76be8a80,0x7a3e0740]lru-flags: on_auxiliary_listckptq: [NULL] fileq: [NULL]objq: [NULL] objaq: [NULL]st: FREE md: NULL fpin: 'ktspswh4: ktspfsbmb' fscn: 0x0.5001f2 tch: 0 lfb: 33flags:Block dump from disk:Decrypting encrypted buffer before dump.buffer tsn: 14 rdba: 0x01c00081 (7/129)scn: 0x0.500216 seq: 0x09 flg: 0x04 tail: 0x02162109frmt: 0x02 chkval: 0xf476 type: 0x21=SECOND LEVEL BITMAP BLOCK【数据块类型为0x21,即二级位图块】Hex dump of block: st=0, typ_found=1Dump of memory from 0x00007F1446E09000 to 0x00007F1446E0B0007F1446E09000 0000A221 01C00081 00500216 04090000 [!.........P.....]【0000A221中21与type: 0x21 对应】Dump of Second Level Bitmap Blocknumber: 10 nfree: 2 ffree: 8 pdba: 0x01c00082【pdba表示父DBA,也就是L3的地址 number代表L1的总数,nfree代表空闲的L1数量】Inc #: 0 Objd: 94981opcode:0xid:L1 Ranges :--------------------------------------------------------0x01c00080 Free: 1 Inst: 1【第1个L1数据块】0x01c00090 Free: 1 Inst: 10x01c000a0 Free: 1 Inst: 10x01c000b0 Free: 1 Inst: 10x01c000c0 Free: 1 Inst: 10x01c000d0 Free: 1 Inst: 10x01c000e0 Free: 1 Inst: 10x01c000f0 Free: 1 Inst: 10x01c00100 Free: 5 Inst: 1 【Free: 5标记L1中可用空间状态】0x01c00101 Free: 5 Inst: 1将数据块地址进行转换:rdba: 0x01c00081==》 (7/129)select dbms_utility.data_block_address_file(to_number('01c00081','xxxxxxxx')) as file_id,dbms_utility.data_block_address_block(to_number('01c00081','xxxxxxxx')) as block_idfrom dual;FILE_ID BLOCK_ID---------- ----------7 129
5、根据L2位图块中的 L1 Ranges,可以找到第一个L1块的DBA,然后进一步找出L1位图块指向了哪些数据块。
--修改会话级参数,解密数据块内容alter session set "_sga_clear_dump"=true;--查看会话对应trace文件路径alter session set tracefile_identifier='dumpl1block';select value from v$diag_info where name ='Default Trace File';/u01/app/oracle/diag/rdbms/prod4/PROD4/trace/PROD4_ora_18427_dumpl1block.trc--转储数据块alter system dump datafile 7 block 128;L1位图块相比L3位图块信息量较少,主要存储L1的地址信息。Start dump data blocks tsn: 14 file#:7 minblk 128 maxblk 128Block dump from cache:Dump of buffer cache at level 4 for pdb=0 tsn=14 rdba=29360256BH (0x743f4298) file#: 7 rdba: 0x01c00080 (7/128) class: 8 ba: 0x74306000set: 5 pool: 3 bsz: 8192 bsi: 0 sflg: 0 pwc: 0,0dbwrid: 0 obj: 94981 objn: 94981 tsn: [0/14] afn: 7 hint: fhash: [0x7cb4b008,0x7cb4b008] lru: [0x73fd64c0,0x71fd6d80]lru-flags: on_auxiliary_listckptq: [NULL] fileq: [NULL]objq: [NULL] objaq: [NULL]st: FREE md: NULL fpin: 'ktspfwh6: ktspffbmb' fscn: 0x0.5001f2 tch: 0 lfb: 33flags:Block dump from disk:Decrypting encrypted buffer before dump.buffer tsn: 14 rdba: 0x01c00080 (7/128)scn: 0x0.500216 seq: 0x03 flg: 0x04 tail: 0x02162003frmt: 0x02 chkval: 0xa648 type: 0x20=FIRST LEVEL BITMAP BLOCKHex dump of block: st=0, typ_found=1Dump of memory from 0x00007F1446E09000 to 0x00007F1446E0B0007F1446E09000 0000A220 01C00080 00500216 04030000 [ .........P.....]【0000A220的20,表示:type: 0x20=FIRST LEVEL BITMAP】Dump of First Level Bitmap Block--------------------------------nbits : 4 nranges: 2 parent dba: 0x01c00081 poffset: 0【parent dba代表上一级DBA】unformatted: 0 total: 16 first useful block: 3owning instance : 1instance ownership changed atLast successful SearchFreeness Status: nf1 0 nf2 0 nf3 0 nf4 0Extent Map Block Offset: 4294967295First free datablock : 16Bitmap block lock opcode 0Locker xid: : 0x0000.000.00000000Dealloc scn: 1594145.0Flag: 0x00000000 (-/-/-/-/-/-)Inc #: 0 Objd: 94981--------------------------------------------------------DBA Ranges :【这个L1管理的数据块的范围】--------------------------------------------------------0x01c00080 Length: 8 Offset: 00x01c00088 Length: 8 Offset: 8【Metadata 是无数据,这里存放的是L3 L2 L1的块。】0:Metadata 1:Metadata 2:Metadata 3:FULL4:FULL 5:FULL 6:FULL 7:FULL8:FULL 9:FULL 10:FULL 11:FULL12:FULL 13:FULL 14:FULL 15:FULL--First Level Bitmap Blockselect dbms_utility.data_block_address_file(to_number('01c00080','xxxxxxxx')) as file_id,dbms_utility.data_block_address_block(to_number('01c00080','xxxxxxxx')) as block_idfrom dual;FILE_ID BLOCK_ID---------- ----------7 128
bbed查看存储结构
06
1、下面我将使用bbed工具进一步挖掘数据块的存储信息。由于Linux操作系统下,Oracle采用的是Little Endian操作系统字节序,因此8100c001值需要转换为01c00081,通过SQL转换后,对应的信息为(7/129)即7号数据文件第129个数据块。
段头块的第一个offset对应的值是23,表示数据块类型:PAGETABLE SEGMENT HEADERBBED> d dba 7,130 offset 0 count 8File: u01/app/oracle/oradata/PROD4/prdtbs.dbf (7)Block: 130 Offsets: 0 to 7 Dba:0x01c00082------------------------------------------------------------------------23a20000 8200c001段头块指向的L2位图块在offset 5192的位置,对应的值为8100c001。BBED> d dba 7,130 offset 5192 count 8File: u01/app/oracle/oradata/PROD4/prdtbs.dbf (7)Block: 130 Offsets: 5192 to 5199 Dba:0x01c00082------------------------------------------------------------------------8100c001 00000000select dbms_utility.data_block_address_file(to_number('01c00081','xxxxxxxx')) as file_id,dbms_utility.data_block_address_block(to_number('01c00081','xxxxxxxx')) as block_idfrom dual;FILE_ID BLOCK_ID---------- ----------7 129
2、查看dba 7,129数据块的存储信息,可以看到L2的第一个offset对应的值为21,表示数据块类型SECOND LEVEL BITMAP BLOCK。
BBED> d dba 7,129 offset 0 count 8File: /u01/app/oracle/oradata/PROD4/prdtbs.dbf (7)Block: 129 Offsets: 0 to 7 Dba:0x01c00081------------------------------------------------------------------------21a20000 8100c001L2指向L1数据块的位置从offset 116开始,从dump出来的地方我们来看,前面是一个地址,后面跟着是01000100,比较规律,大概7组之后就变成00000000(空)了。跟上面一样因为字节序的问题,这里我们需要将8000c001转换成01c00080。BBED> d offset 116 count 100File: /u01/app/oracle/oradata/PROD4/prdtbs.dbf (7)Block: 129 Offsets: 116 to 215 Dba:0x01c00081------------------------------------------------------------------------8000c001 05000100 9000c001 01000100 a000c001 01000100 b000c001 01000100c000c001 01000100 d000c001 01000100 e000c001 01000100 f000c001 010001000001c001 05000100 0101c001 05000100 00000000 00000000 00000000 00000000
Truncate原理
07
1、在执行truncate命令前,我把36个关键的offset详细信息记录到表格,方便理解对应的含义。有需要的朋友,可以从附件下载。

从offset 2736开始,记录aux map信息。
d v offset 2736BBED> d v offset 2736File: u01/app/oracle/oradata/PROD4/prdtbs.dbf (7)Block: 130 Offsets: 2736 to 3035 Dba:0x01c00082-------------------------------------------------------8000c001 8300c001 8000c001 8800c001 l ........9000c001 9100c001 9000c001 9800c001 l ........a000c001 a100c001 a000c001 a800c001 l .¡. .¨.b000c001 b100c001 b000c001 b800c001 l °.±.°.¸.c000c001 c100c001 c000c001 c800c001 l Ʈd000c001 d100c001 d000c001 d800c001 l ήϮή֮e000c001 e100c001 e000c001 e800c001 l ...毀.f000c001 f100c001 f000c001 f800c001 l 0001c001 0201c001 00000000 00000000 l ............00000000 00000000 00000000 00000000 l ................
2、通过10046事件了解truncate的原理。
查看当前redo日志文件名col MEMBER for a60select member from v$logfilewhere group#=(select group# from v$log where status='CURRENT');查看当前数据库时间select to_char(sysdate,'yyyymmdd hh24:mi:ss') current_ts from dual;20240621 13:32:52启用10046事件跟踪oradebug setmypid;alter session set tracefile_identifier='truncate';oradebug tracefile_name/u01/app/oracle/diag/rdbms/prod4/PROD4/trace/PROD4_ora_19940_truncate.trcoradebug event 10046 trace name context forever,level 12;truncate table user02.TAB11;oradebug event 10046 trace name context off;dump redo log 日志文件alter system dump logfile '/u01/app/oracle/oradata/PROD4/redo01.log';
3、从跟踪日志可以看到tab$和obj$基表里的dataobj#(data_object_id)值已经被修改掉。
检索delete基表操作$ grep ^delete /u01/app/oracle/diag/rdbms/prod4/PROD4/trace/PROD4_ora_125632_truncate.trcdelete from compression_stat$ where dataobj# = :1 and ts# = :2delete from superobj$ where subobj# = :1delete from tab_stats$ where obj#=:1检索update基表操作$ grep ^update u01/app/oracle/diag/rdbms/prod4/PROD4/trace/PROD4_ora_125632_truncate.trcupdate obj$ set obj#=:4, type#=:5,ctime=:6,mtime=:7,stime=:8,status=:9,dataobj#=:10,flags=:11,oid$=:12,spare1=:13,spare2=:14,spare3=:15,signature=:16,spare7=:17,spare8=:18,spare9=:19, dflcollid=decode(:20,0,null,:20),creappid=:21,creverid=:22, modappid=:23,modverid=:24,crepatchid=:25,modpatchid=:26 where owner#=:1 and name=:2 and namespace=:3 and remoteowner is null and linkname is null and subname is nullupdate tab$ set ts#=:2,file#=:3,block#=:4,bobj#=decode(:5,0,null,:5),tab#=decode(:6,0,null,:6),intcols=:7,kernelcols=:8,clucols=decode(:9,0,null,:9),audit$=:10,flags=:11,pctfree$=:12,pctused$=:13,initrans=:14,maxtrans=:15,rowcnt=:16,blkcnt=:17,empcnt=:18,avgspc=:19,chncnt=:20,avgrln=:21,analyzetime=:22,samplesize=:23,cols=:24,property=:25,degree=decode(:26,1,null,:26),instances=decode(:27,1,null,:27),dataobj#=:28,avgspc_flb=:29,flbcnt=:30,trigflag=:31,spare1=:32,spare2=decode(:33,0,null,:33),spare4=:34,spare6=:35,acdrflags=decode(:36,0,null,:36),acdrtsobj#=decode(:37,0,null,:37),acdrdefaulttime=:38,acdrrowtsintcol#=:39 where obj#=:1update seg$ set type#=:4,blocks=:5,extents=:6,minexts=:7,maxexts=:8,extsize=:9,extpct=:10,user#=:11,iniexts=:12,lists=decode(:13, 65535, NULL, :13),groups=decode(:14, 65535, NULL, :14), cachehint=:15, hwmincr=:16, spare1=DECODE(:17,0,NULL,:17),scanhint=:18, bitmapranges=:19 where ts#=:1 and file#=:2 and block#=:3查看基表信息$ grep -i "CDOBJ" /u01/app/oracle/diag/rdbms/prod4/PROD4/trace/PROD4_ora_125632_truncate.trcCDOBJ: new object number:94981CDOBJ: new object number:95017select dataobj#,obj# from tab$ where obj#=94981;select HWMINCR from seg$ where ts#= 14 AND FILE#= 7;select dataobj#,obj# from obj$ where obj#=94981;col object_name for a15col owner for a10select object_id, data_object_id, owner,object_name from dba_objectswhere object_name='TAB11';OBJECT_ID DATA_OBJECT_ID OWNER OBJECT_NAME---------- -------------- ---------- ---------------94981 95017 user02 TAB11
4、从上述结果,可以看到Truncate表之后,data_object_id从94981变成了95017。我们分别看一下段头块、L2位图块、L1位图块对应的dba地址是否有变化。通过对各个块的dump,发现段头和L2位图块的data_obj_id已经发生了改变,从05730100变成了29730100,而只有第一个L1发生了变化,数据块则没有发生改变。
SQL> select to_char('94981','xxxxxxxx') old_dataobj,to_char('95017','xxxxxxxx') new_dataobj from dual;OLD_DATAO NEW_DATAO--------- ---------17305 1732917305转换为057301、17329转换为297301L3位图块,offset 272代表Map Header的obj#BBED> d dba 7,130 offset 272 count 32File: /u01/app/oracle/oradata/PROD4/prdtbs.dbf (7)Block: 130 Offsets: 272 to 303 Dba:0x01c00082------------------------------------------------------------------------29730100 00000010 8000c001 08000000 8800c001 08000000 9000c001 08000000L2位图块,offset 104代表LHWM所在的dba地址BBED> d dba 7,129 offset 104 count 32File: /u01/app/oracle/oradata/PROD4/prdtbs.dbf (7)Block: 129 Offsets: 104 to 135 Dba:0x01c00081------------------------------------------------------------------------29730100 01000000 00000000 8000c001 01000100 9000c001 01000100 a000c001L1位图块,offset 192代表LHWM所在的dba地址BBED> d dba 7,128 offset 192 count 32File: /u01/app/oracle/oradata/PROD4/prdtbs.dbf (7)Block: 128 Offsets: 192 to 223 Dba:0x01c00080------------------------------------------------------------------------29730100 21531800 00000000 8000c001 08000000 00000000 8800c001 08000000

5、段头高水位信息恢复
truncate前对应的ext#为16,blk#为38,ext size为128truncate后对应的ext#为0,blk#为3,ext size为8$ grep -i "Highwater" /u01/app/oracle/diag/rdbms/prod4/PROD4/trace/PROD4_ora_125632_truncate.trc|grep -v SETHWM|grep -v OpcodeHighwater:: 0x01c00126 ext#: 16 blk#: 38 ext size: 128Highwater:: 0x01c00126 ext#: 16 blk#: 38 ext size: 128Highwater:: 0x01c00083 ext#: 0 blk#: 3 ext size: 8Highwater:: 0x01c00083 ext#: 0 blk#: 3 ext size: 8select to_char(16,'xxxxx') ext,to_char(38,'xxxxx') blk,to_char(128,'xxxxx') extsize from dual;EXT BLK EXTSIZ------ ------ ------10 26 80select to_char(0,'xxxxx') ext,to_char(3,'xxxxx') blk,to_char(8,'xxxxx') extsize from dual;EXT BLK EXTSIZ------ ------ ------0 3 8
truncate命令实际修改以下6个offset位置的数据。

6、如果重新dump段头块信息,可以看到段头部的ext和aux map的信息被清空了。但是这些信息会记录在redo log日志文件,通过dump redo log日志,然后解析出相应的信息。
extent map 地图信息grep -i "ADD: dba" /u01/app/oracle/diag/rdbms/prod4/PROD4/trace/PROD4_ora_125632_truncate.trcADD: dba:0x1c00100 len:128 at offset:16ADD: dba:0x1c000f8 len:8 at offset:15ADD: dba:0x1c000f0 len:8 at offset:14ADD: dba:0x1c000e8 len:8 at offset:13ADD: dba:0x1c000e0 len:8 at offset:12ADD: dba:0x1c000d8 len:8 at offset:11ADD: dba:0x1c000d0 len:8 at offset:10ADD: dba:0x1c000c8 len:8 at offset:9ADD: dba:0x1c000c0 len:8 at offset:8ADD: dba:0x1c000b8 len:8 at offset:7ADD: dba:0x1c000b0 len:8 at offset:6ADD: dba:0x1c000a8 len:8 at offset:5ADD: dba:0x1c000a0 len:8 at offset:4ADD: dba:0x1c00098 len:8 at offset:3ADD: dba:0x1c00090 len:8 at offset:2ADD: dba:0x1c00088 len:8 at offset:1Auxillary Map 地图信息$ grep -i "ADDAXT" /u01/app/oracle/diag/rdbms/prod4/PROD4/trace/PROD4_ora_125632_truncate.trcADDAXT: offset:16 fdba:x01c00100 bdba:0x01c00102ADDAXT: offset:15 fdba:x01c000f0 bdba:0x01c000f8ADDAXT: offset:14 fdba:x01c000f0 bdba:0x01c000f1ADDAXT: offset:13 fdba:x01c000e0 bdba:0x01c000e8ADDAXT: offset:12 fdba:x01c000e0 bdba:0x01c000e1ADDAXT: offset:11 fdba:x01c000d0 bdba:0x01c000d8ADDAXT: offset:10 fdba:x01c000d0 bdba:0x01c000d1ADDAXT: offset:9 fdba:x01c000c0 bdba:0x01c000c8ADDAXT: offset:8 fdba:x01c000c0 bdba:0x01c000c1ADDAXT: offset:7 fdba:x01c000b0 bdba:0x01c000b8ADDAXT: offset:6 fdba:x01c000b0 bdba:0x01c000b1ADDAXT: offset:5 fdba:x01c000a0 bdba:0x01c000a8ADDAXT: offset:4 fdba:x01c000a0 bdba:0x01c000a1ADDAXT: offset:3 fdba:x01c00090 bdba:0x01c00098ADDAXT: offset:2 fdba:x01c00090 bdba:0x01c00091ADDAXT: offset:1 fdba:x01c00080 bdba:0x01c00088
truncate实际上是将ext_map和aux_map的对应的offset信息置为空值。我们可以修改offset位置对应的值,进行段头信息修复。





