暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

DBA实验手册第4讲-运用bbed工具解析位图数据块和truncate内部原理

178
古人学问无遗力,少壮工夫老始成。
纸上得来终觉浅,绝知此事要躬行。
     --(南宋)陆游《冬夜读书示子聿


 前       言 

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 a60
    select 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 9999
    select dbms_metadata.get_ddl('TABLESPACE', 'PRDTBS') from dual;
    CREATE TABLESPACE "prdtbs" DATAFILE
    '/u01/app/oracle/oradata/PROD4/prdtbs.dbf' SIZE 20971520
    LOGGING ONLINE PERMANENT BLOCKSIZE 8192
    EXTENT MANAGEMENT LOCAL AUTOALLOCATE DEFAULT
    NOCOMPRESS SEGMENT SPACE MANAGEMENT AUTO


    20M大小的表空间是由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字段值是相同的,都是94981
        col object_name for a15
        col owner for a10
        select object_id, data_object_id, owner,object_name from dba_objects
        where object_name='TAB11';
        OBJECT_ID DATA_OBJECT_ID OWNER OBJECT_NAME
        ---------- -------------- ---------- ---------------
        94981 94981 user02 TAB11
             
        数据段存放在2号数据文件,段头块编号为130,是由17个区组成,共256个数据块。
        col segment_name for a15
        select SEGMENT_NAME,HEADER_FILE,HEADER_BLOCK,EXTENTS,BLOCKS from dba_segments
        where 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 100
        col segment_name for a15
        select SEGMENT_NAME,extent_id,file_id,block_id,blocks from dba_extents
        where tablespace_name='PRDTBS';
        SEGMENT_NAME EXTENT_ID FILE_ID BLOCK_ID BLOCKS
        --------------- ---------- ---------- ---------- ----------
        TAB11 0 7 128 8
        TAB11 1 7 136 8
        TAB11 2 7 144 8
        TAB11 3 7 152 8
        TAB11 4 7 160 8
        TAB11 5 7 168 8
        TAB11 6 7 176 8
        TAB11 7 7 184 8
        TAB11 8 7 192 8
        TAB11 9 7 200 8
        TAB11 10 7 208 8
        TAB11 11 7 216 8
        TAB11 12 7 224 8
        TAB11 13 7 232 8
        TAB11 14 7 240 8
        TAB11 15 7 248 8
        TAB11            16        7         256    128
        17 rows selected.


        测试表空间对应的TS#编号为14,后面解读数据块的dump记录会使用。
        SQL> select ts#,name from v$tablespace where name='PRDTBS';
        TS# NAME
        ---------- ------------------------------
             14 PRDTBS


        测试表空间对应的数据文件编号为7,后面解读数据块的dump记录会使用。
        set linesize 1000
        col file_name for a60
        select 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=29360258
            BH (0x743f43d8) file#: 7 rdba: 0x01c00082 (7/130)【数据块位置】 class: 4 ba: 0x74308000
            set: 5 pool: 3 bsz: 8192【数据块大小】 bsi: 0 sflg: 0 pwc: 0,0
            dbwrid: 0 obj: 94981 objn: 94981【对象编号】 tsn: [0/14] afn: 7 hint: f
            hash: [0x7cb9def8,0x7cb9def8] lru: [0x773eb640,0x793eba00]
            lru-flags: on_auxiliary_list
            ckptq: [NULL] fileq: [NULL]
            objq: [NULL] objaq: [NULL]
            st: FREE md: NULL fpin: 'ktswh03: ktscts' fscn: 0x0.5001f2 tch: 0 lfb: 33
            flags:
            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: 0x021a2301
            frmt: 0x02 chkval: 0xd95d type: 0x23=PAGETABLE SEGMENT HEADER【数据块类型0x23:段头】
            Hex dump of block: st=0, typ_found=1
            Dump of memory from 0x00007F1446E09000 to 0x00007F1446E0B000
            7F1446E09000 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 HEADER
            select 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_id
            from dual;
            FILE_ID BLOCK_ID
            ---------- ----------
            7 130


            16进制中两个字符表示1bytes(1个offset),将十六进制0x01c00082按两个字符一步步拆解:
            DBA=0x01c00082====> 拆解为:0x 01 c0 00 82
            select number_to_bit(to_number('01','xxxxxxxx')) as bit from dual;
            --> 00000001
            select number_to_bit(to_number('c0','xxxxxxxx')) as bit from dual;
            --> 11000000
            select number_to_bit(to_number('00','xxxxxxxx')) as bit from dual;
            --> 00000000
            select number_to_bit(to_number('82','xxxxxxxx')) as bit from dual;
            --> 10101010
            number_to_bit函数完整创建脚本详见《DBA实验手册第1讲》
            DBA=0x01c00082====> 转换为二进制为:00000001 11000000 00000000 10000010
            DBA(data block address)===file#(10bit)+block#(22bit)==32bit
            file#=00000001 11 -----1号文件
            block#=000000 00000110 10101010 --> 520号块
            select bit_to_number('0000000111') as num from dual;
            --> 7
            select 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: 2716
              Highwater:: 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: 16
              Unlocked
              --------------------------------------------------------
              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: 16
              Level 1 BMB for High HWM block: 0x01c00100
              Level 1 BMB for Low HWM block: 0x01c00100
              --------------------------------------------------------
              Segment Type: 1 nl2: 1 blksz: 8192 fbsz: 0
              L2 Array start offset: 0x00001434
              First Level 3 BMB: 0x00000000
              L2 Hint for inserts: 0x01c00081【二级位图块的DBA地址(7/129)】
              Last Level 1 BMB: 0x01c00101
              Last Level II BMB: 0x01c00081
              Last Level III BMB: 0x00000000
              Map Header:: next 0x00000000 #extents: 17 obj#: 94981 flag: 0x10000000
              【obj#对应数据字典:dba_objects.DATA_OBJECT_ID】
              Extent Map区地图,段包含几个区,区的起始地址以及长度。
              Extent Map
              -----------------------
              0x01c00080 length: 8
              0x01c00088 length: 8
              0x01c00090 length: 8
              0x01c00098 length: 8
              0x01c000a0 length: 8
              0x01c000a8 length: 8
              0x01c000b0 length: 8
              0x01c000b8 length: 8
              0x01c000c0 length: 8
              0x01c000c8 length: 8
              0x01c000d0 length: 8
              0x01c000d8 length: 8
              0x01c000e0 length: 8
              0x01c000e8 length: 8
              0x01c000f0 length: 8
              0x01c000f8 length: 8
              0x01c00100 length: 128


              Auxillary 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: 0x01c00091
              Extent 3 : L1 dba: 0x01c00090 Data dba: 0x01c00098
              Extent 4 : L1 dba: 0x01c000a0 Data dba: 0x01c000a1
              Extent 5 : L1 dba: 0x01c000a0 Data dba: 0x01c000a8
              Extent 6 : L1 dba: 0x01c000b0 Data dba: 0x01c000b1
              Extent 7 : L1 dba: 0x01c000b0 Data dba: 0x01c000b8
              Extent 8 : L1 dba: 0x01c000c0 Data dba: 0x01c000c1
              Extent 9 : L1 dba: 0x01c000c0 Data dba: 0x01c000c8
              Extent 10 : L1 dba: 0x01c000d0 Data dba: 0x01c000d1
              Extent 11 : L1 dba: 0x01c000d0 Data dba: 0x01c000d8
              Extent 12 : L1 dba: 0x01c000e0 Data dba: 0x01c000e1
              Extent 13 : L1 dba: 0x01c000e0 Data dba: 0x01c000e8
              Extent 14 : L1 dba: 0x01c000f0 Data dba: 0x01c000f1
              Extent 15 : L1 dba: 0x01c000f0 Data dba: 0x01c000f8
              Extent 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: 0x74120000
                set: 6 pool: 3 bsz: 8192 bsi: 0 sflg: 0 pwc: 0,0
                dbwrid: 0 obj: 94981 objn: 94981 tsn: [0/14] afn: 7 hint: f
                hash: [0x7c457078,0x7c457078] lru: [0x76be8a80,0x7a3e0740]
                lru-flags: on_auxiliary_list
                ckptq: [NULL] fileq: [NULL]
                objq: [NULL] objaq: [NULL]
                st: FREE md: NULL fpin: 'ktspswh4: ktspfsbmb' fscn: 0x0.5001f2 tch: 0 lfb: 33
                flags:
                Block dump from disk:
                Decrypting encrypted buffer before dump.
                buffer tsn: 14 rdba: 0x01c00081 (7/129)
                scn: 0x0.500216 seq: 0x09 flg: 0x04 tail: 0x02162109
                frmt: 0x02 chkval: 0xf476 type: 0x21=SECOND LEVEL BITMAP BLOCK【数据块类型为0x21,即二级位图块】
                Hex dump of block: st=0, typ_found=1
                Dump of memory from 0x00007F1446E09000 to 0x00007F1446E0B000
                7F1446E09000 0000A221 01C00081 00500216 04090000 [!.........P.....]
                【0000A221中21与type: 0x21 对应】


                Dump of Second Level Bitmap Block
                number: 10 nfree: 2 ffree: 8 pdba: 0x01c00082
                【pdba表示父DBA,也就是L3的地址 number代表L1的总数,nfree代表空闲的L1数量】
                Inc #: 0 Objd: 94981
                opcode:0
                xid:
                L1 Ranges :
                --------------------------------------------------------
                0x01c00080 Free: 1 Inst: 1【第1个L1数据块】
                0x01c00090 Free: 1 Inst: 1
                0x01c000a0 Free: 1 Inst: 1
                0x01c000b0 Free: 1 Inst: 1
                0x01c000c0 Free: 1 Inst: 1
                0x01c000d0 Free: 1 Inst: 1
                0x01c000e0 Free: 1 Inst: 1
                0x01c000f0 Free: 1 Inst: 1
                0x01c00100 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_id
                from 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 128
                  Block dump from cache:
                  Dump of buffer cache at level 4 for pdb=0 tsn=14 rdba=29360256
                  BH (0x743f4298) file#: 7 rdba: 0x01c00080 (7/128) class: 8 ba: 0x74306000
                  set: 5 pool: 3 bsz: 8192 bsi: 0 sflg: 0 pwc: 0,0
                  dbwrid: 0 obj: 94981 objn: 94981 tsn: [0/14] afn: 7 hint: f
                  hash: [0x7cb4b008,0x7cb4b008] lru: [0x73fd64c0,0x71fd6d80]
                  lru-flags: on_auxiliary_list
                  ckptq: [NULL] fileq: [NULL]
                  objq: [NULL] objaq: [NULL]
                  st: FREE md: NULL fpin: 'ktspfwh6: ktspffbmb' fscn: 0x0.5001f2 tch: 0 lfb: 33
                  flags:
                  Block dump from disk:
                  Decrypting encrypted buffer before dump.
                  buffer tsn: 14 rdba: 0x01c00080 (7/128)
                  scn: 0x0.500216 seq: 0x03 flg: 0x04 tail: 0x02162003
                  frmt: 0x02 chkval: 0xa648 type: 0x20=FIRST LEVEL BITMAP BLOCK
                  Hex dump of block: st=0, typ_found=1
                  Dump of memory from 0x00007F1446E09000 to 0x00007F1446E0B000
                  7F1446E09000 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: 3
                  owning instance : 1
                  instance ownership changed at
                  Last successful Search
                  Freeness Status: nf1 0 nf2 0 nf3 0 nf4 0


                  Extent Map Block Offset: 4294967295
                  First free datablock : 16
                  Bitmap block lock opcode 0
                  Locker xid: : 0x0000.000.00000000
                  Dealloc scn: 1594145.0
                  Flag: 0x00000000 (-/-/-/-/-/-)
                  Inc #: 0 Objd: 94981
                  --------------------------------------------------------
                  DBA Ranges :【这个L1管理的数据块的范围】
                  --------------------------------------------------------
                  0x01c00080 Length: 8 Offset: 0
                  0x01c00088 Length: 8 Offset: 8
                  【Metadata 是无数据,这里存放的是L3 L2 L1的块。】
                  0:Metadata 1:Metadata 2:Metadata 3:FULL
                  4:FULL 5:FULL 6:FULL 7:FULL
                  8:FULL 9:FULL 10:FULL 11:FULL
                  12:FULL 13:FULL 14:FULL 15:FULL


                  --First Level Bitmap Block
                  select 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_id
                  from 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 HEADER
                    BBED> d dba 7,130 offset 0 count 8
                    File: 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 8
                    File: u01/app/oracle/oradata/PROD4/prdtbs.dbf (7)
                    Block: 130 Offsets: 5192 to 5199 Dba:0x01c00082
                    ------------------------------------------------------------------------
                    8100c001 00000000


                    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_id
                    from 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 8
                      File: /u01/app/oracle/oradata/PROD4/prdtbs.dbf (7)
                      Block: 129 Offsets: 0 to 7 Dba:0x01c00081
                      ------------------------------------------------------------------------
                      21a20000 8100c001
                      L2指向L1数据块的位置从offset 116开始,从dump出来的地方我们来看,前面是一个地址,后面跟着是01000100,比较规律,大概7组之后就变成00000000(空)了。跟上面一样因为字节序的问题,这里我们需要将8000c001转换成01c00080。


                      BBED> d offset 116 count 100
                      File: /u01/app/oracle/oradata/PROD4/prdtbs.dbf (7)
                      Block: 129 Offsets: 116 to 215 Dba:0x01c00081
                      ------------------------------------------------------------------------
                      8000c001 05000100 9000c001 01000100 a000c001 01000100 b000c001 01000100
                      c000c001 01000100 d000c001 01000100 e000c001 01000100 f000c001 01000100
                      0001c001 05000100 0101c001 05000100 00000000 00000000 00000000 00000000


                       Truncate原理 

                      07


                      1、在执行truncate命令前,我把36个关键的offset详细信息记录到表格,方便理解对应的含义。有需要的朋友,可以从附件下载。

                      从offset 2736开始,记录aux map信息。

                        d v offset 2736
                        BBED> d v offset 2736
                        File: 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 a60
                          select member from v$logfile
                          where 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.trc
                          oradebug 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.trc
                            delete from compression_stat$ where dataobj# = :1 and ts# = :2
                            delete from superobj$ where subobj# = :1
                            delete from tab_stats$ where obj#=:1


                            检索update基表操作
                            $ grep ^update u01/app/oracle/diag/rdbms/prod4/PROD4/trace/PROD4_ora_125632_truncate.trc
                            update 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 null
                            update 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#=:1
                            update 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.trc
                            CDOBJ: new object number:94981
                            CDOBJ: new object number:95017


                            select 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 a15
                            col owner for a10
                            select object_id, data_object_id, owner,object_name from dba_objects
                            where 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 17329
                              17305转换为057301、17329转换为297301


                              L3位图块,offset 272代表Map Header的obj#
                              BBED> d dba 7,130 offset 272 count 32
                              File: /u01/app/oracle/oradata/PROD4/prdtbs.dbf (7)
                              Block: 130 Offsets: 272 to 303 Dba:0x01c00082
                              ------------------------------------------------------------------------
                              29730100 00000010 8000c001 08000000 8800c001 08000000 9000c001 08000000


                              L2位图块,offset 104代表LHWM所在的dba地址
                              BBED> d dba 7,129 offset 104 count 32
                              File: /u01/app/oracle/oradata/PROD4/prdtbs.dbf (7)
                              Block: 129 Offsets: 104 to 135 Dba:0x01c00081
                              ------------------------------------------------------------------------
                              29730100 01000000 00000000 8000c001 01000100 9000c001 01000100 a000c001


                              L1位图块,offset 192代表LHWM所在的dba地址
                              BBED> d dba 7,128 offset 192 count 32
                              File: /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为128
                                truncate后对应的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 Opcode
                                Highwater:: 0x01c00126 ext#: 16 blk#: 38 ext size: 128
                                Highwater:: 0x01c00126 ext#: 16 blk#: 38 ext size: 128
                                Highwater:: 0x01c00083 ext#: 0 blk#: 3 ext size: 8
                                Highwater:: 0x01c00083 ext#: 0 blk#: 3 ext size: 8
                                      
                                select to_char(16,'xxxxx') ext,to_char(38,'xxxxx') blk,to_char(128,'xxxxx') extsize from dual;
                                EXT BLK EXTSIZ
                                ------ ------ ------
                                    10       26      80


                                select 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.trc
                                  ADD: dba:0x1c00100 len:128 at offset:16
                                  ADD: dba:0x1c000f8 len:8 at offset:15
                                  ADD: dba:0x1c000f0 len:8 at offset:14
                                  ADD: dba:0x1c000e8 len:8 at offset:13
                                  ADD: dba:0x1c000e0 len:8 at offset:12
                                  ADD: dba:0x1c000d8 len:8 at offset:11
                                  ADD: dba:0x1c000d0 len:8 at offset:10
                                  ADD: dba:0x1c000c8 len:8 at offset:9
                                  ADD: dba:0x1c000c0 len:8 at offset:8
                                  ADD: dba:0x1c000b8 len:8 at offset:7
                                  ADD: dba:0x1c000b0 len:8 at offset:6
                                  ADD: dba:0x1c000a8 len:8 at offset:5
                                  ADD: dba:0x1c000a0 len:8 at offset:4
                                  ADD: dba:0x1c00098 len:8 at offset:3
                                  ADD: dba:0x1c00090 len:8 at offset:2
                                  ADD: dba:0x1c00088 len:8 at offset:1
                                    
                                  Auxillary Map 地图信息
                                  $ grep -i "ADDAXT" /u01/app/oracle/diag/rdbms/prod4/PROD4/trace/PROD4_ora_125632_truncate.trc
                                  ADDAXT: offset:16 fdba:x01c00100 bdba:0x01c00102
                                  ADDAXT: offset:15 fdba:x01c000f0 bdba:0x01c000f8
                                  ADDAXT: offset:14 fdba:x01c000f0 bdba:0x01c000f1
                                  ADDAXT: offset:13 fdba:x01c000e0 bdba:0x01c000e8
                                  ADDAXT: offset:12 fdba:x01c000e0 bdba:0x01c000e1
                                  ADDAXT: offset:11 fdba:x01c000d0 bdba:0x01c000d8
                                  ADDAXT: offset:10 fdba:x01c000d0 bdba:0x01c000d1
                                  ADDAXT: offset:9 fdba:x01c000c0 bdba:0x01c000c8
                                  ADDAXT: offset:8 fdba:x01c000c0 bdba:0x01c000c1
                                  ADDAXT: offset:7 fdba:x01c000b0 bdba:0x01c000b8
                                  ADDAXT: offset:6 fdba:x01c000b0 bdba:0x01c000b1
                                  ADDAXT: offset:5 fdba:x01c000a0 bdba:0x01c000a8
                                  ADDAXT: offset:4 fdba:x01c000a0 bdba:0x01c000a1
                                  ADDAXT: offset:3 fdba:x01c00090 bdba:0x01c00098
                                  ADDAXT: offset:2 fdba:x01c00090 bdba:0x01c00091
                                  ADDAXT: offset:1 fdba:x01c00080 bdba:0x01c00088

                                  truncate实际上是将ext_map和aux_map的对应的offset信息置为空值。我们可以修改offset位置对应的值,进行段头信息修复。

                                  文章转载自数据库运维之道,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                  评论