Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2886588
  • 博文数量: 599
  • 博客积分: 16398
  • 博客等级: 上将
  • 技术积分: 6875
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-30 12:04
个人简介

WINDOWS下的程序员出身,偶尔也写一些linux平台下小程序, 后转行数据库行业,专注于ORACLE和DB2的运维和优化。 同时也是ios移动开发者。欢迎志同道合的朋友一起研究技术。 数据库技术交流群:58308065,23618606

文章分类

全部博文(599)

文章存档

2014年(12)

2013年(56)

2012年(199)

2011年(105)

2010年(128)

2009年(99)

分类: Oracle

2012-05-14 12:38:13

以前在网上看到过使用BBED来恢复删除的数据的例子,由于当时对数据块的格式不太理解。
一直看不懂别人写的例子,为什么要改数据块的这个字节,而不是改另一个字节,为什么要改为XXX而不是改为YYY等之类的问题。
前段时间研究过一段时间的数据块格式,因此也打算自己试验一下,顺便记录一下过程。
需要说明的是:一条记录如果被删除的话,实际ORACLE在数据块中并没有立马把这条记录清除,而是在行头中将该条记录标记为删除。
在操作之前,我们需要了解一点基础知识。
下面的信息来自Maclean Liu大虾的网站:

Oracle中表的数据块由块头、事务槽、行字典、行数据等多种结构组成。 行数据(rowdata)实际是由许多row piece 行片组成的,每一条row piece的头部都有flag、locks、cols(cc)三个标志位。
其中flag标记了该row piece的类型,该flag位占用一个字节,其不同的bit位代表不同的含义,见下表:
ROW_CLUSTER_KEY = 0x80;              KDRHFK
ROW_CTABLE_NUMBER = 0x40;            KDRHFC
ROW_HEAD_PIECE = 0x20;               KDRHFH
ROW_DELETED_ROW = 0x10;              KDRHFD
ROW_FIRST_PIECE = 0x08;              KDRHFF
ROW_LAST_PIECE = 0x04;               KDRHFL
ROW_FROM_PREVIOUS = 0x02;            KDRHFP
ROW_CONTINUE_NEXT = 0x01;            KDRHFN
一般来说最普通的一条row piece是普通堆表(heap table)的未被删除的且无行迁移/链接的,其flag位应为
普通row的flag一般为
Single Row = ROW_HEAD_PIECE + ROW_FIRST_PIECE + ROW_LAST_PIECE= 0x20 + 0x08 + 0x04= 0x2c
===================================================================================
cluster key的flag一般为
Cluster Key =
ROW_CLUSTER_KEY + ROW_HEAD_PIECE + ROW_FIRST_PIECE + ROW_LAST_PIECE=
KDRHFL, KDRHFF, KDRHFH, KDRHFK =0x80 + 0x2c =  0xac
BBED> x /rn
rowdata[68]                                 @8166
-----------
: 0xac (KDRHFL, KDRHFF, KDRHFH, KDRHFK)
: 0x00
:    1
:    1
:    1

col    0[2] @8185: 10
===================================================================================
Cluster Row =
ROW_CTABLE_NUMBER + ROW_HEAD_PIECE + ROW_FIRST_PIECE + ROW_LAST_PIECE =(KDRHFL, KDRHFF, KDRHFH, KDRHFC) = 0x6c
BBED> x /rncc
rowdata[0]                                  @8098
----------
: 0x6c (KDRHFL, KDRHFF, KDRHFH, KDRHFC)
: 0x00
:   10
col    0[2] @8102: 200
col    1[8] @8105: Jennifer
col    2[6] @8114: Whalen
col    3[7] @8121: JWHALEN
col   4[12] @8129: 515.123.4444
col    5[7] @8142: w....
col    6[7] @8150: AD_ASST
col    7[2] @8158:
                    col    8[0] @8161: *NULL*
col    9[3] @8162: .
 

从上面可以看出:
普通row的flag一般为
Single Row = ROW_HEAD_PIECE + ROW_FIRST_PIECE + ROW_LAST_PIECE= 0x20 + 0x08 + 0x04= 0x2c
如果这行记录被删除了,那么ROW_DELETED_ROW标志位会被置为1,此时
Single Row = ROW_HEAD_PIECE + ROW_DELETED_ROW+ ROW_FIRST_PIECE + ROW_LAST_PIECE= 0x20 + 0x10+ 0x08 + 0x04= 0x3c

如果要把这条记录找回来,只需要把标志位ROW_DELETED_ROW 从1改为0,也就是把0X3C改为0X2C。

我们以SCOTT的DEPT表为例:

SQL> select * from scott.dept;
    DEPTNO DNAME                        LOC
---------- ---------------------------- --------------------------
        10 ACCOUNTING                   NEW YORK
        20 RESEARCH                     DALLAS
        30 SALES                        CHICAGO
        40 OPERATIONS                   BOSTON
SQL> select  dbms_rowid.rowid_relative_fno(rowid) file#,dbms_rowid.rowid_block_number(rowid) block# from scott.dept;
     FILE#     BLOCK#
---------- ----------
         4         16
         4         16
         4         16
         4         16

SQL> select * from v$dbfile;
     FILE# NAME
---------- ------------------------------
         4 /test/orcl/orcl/users01.dbf
         3 /test/orcl/orcl/sysaux01.dbf
         2 /test/orcl/orcl/undotbs01.dbf
         1 /test/orcl/orcl/system01.dbf
         5 /test/orcl/orcl/abcd_0.dbf
         6 /test/orcl/orcl/abcd_1.dbf
         7 /test/orcl/orcl/abcd_2.dbf
7 rows selected.

表的数据在文件4,BLOCK 16中。

[oracle@sxffdb1 ~]$ bbed
Password:
BBED: Release 2.0.0.0.0 - Limited Production on Mon May 14 10:03:02 2012
Copyright (c) 1982, 2007, Oracle.  All rights reserved.
************* !!! For Oracle Internal Use only !!! ***************

BBED> set filename '/test/orcl/orcl/users01.dbf';
        FILENAME        /test/orcl/orcl/users01.dbf
BBED> set block 16
        BLOCK#          16
BBED> map /v
 File: /test/orcl/orcl/users01.dbf (0)
 Block: 16                                    Dba:0x00000000
------------------------------------------------------------
 KTB Data Block (Table/Cluster)
 struct kcbh, 20 bytes                      @0       --Cache Layer
    ub1 type_kcbh                           @0                 -- Block type (see Header Block Types below)
    ub1 frmt_kcbh                           @1                 --Block format 1=Oracle 7, 2=Oracle 8+
    ub1 spare1_kcbh                         @2                 --Not used    
    ub1 spare2_kcbh                         @3                 --Not used  
    ub4 rdba_kcbh                           @4                 --RDBA -Relative Data Block Address
    ub4 bas_kcbh                            @8                 --SCN Base  
    ub2 wrp_kcbh                            @12                --SCN Wrap
    ub1 seq_kcbh                            @14                --Sequence number, incremented for every change made to the block at the same SCN    
    ub1 flg_kcbh                            @15                --Flag:
        0x01 New Block
        0x02 Delayed Logging Change advanced SCN/seq
               0x04 Check value saved - block XOR‘s to zero
        0x08 Temporary block
  
    ub2 chkval_kcbh                         @16               --Optional block checksum (if DB_BLOCK_CHECKSUM=TRUE)    
    ub2 spare3_kcbh                         @18               --Not used
 struct ktbbh, 72 bytes                     @20       --Transaction Layer (Transaction Fixed Header Structure)
    ub1 ktbbhtyp                            @20               --Block type (1=DATA, 2=INDEX)
    union ktbbhsid, 4 bytes                 @24               --DATA OBJECT ID 
    struct ktbbhcsc, 8 bytes                @28               --SCN at last block cleanout   
    b2 ktbbhict                             @36               --Number of ITL slots  
    ub1 ktbbhflg                            @38               --0=on the freelist 
    ub1 ktbbhfsl                            @39               --ITL TX freelist slot     
    ub4 ktbbhfnx                            @40               -- DBA of next block on the freelist
    struct ktbbhitl[2], 48 bytes            @44               --ITL list index
 struct kdbh, 14 bytes                      @100      --Data Header (Data Header Structure )
    ub1 kdbhflag                            @100          --N=pctfree hit(clusters); F=do not put on freelist; K=flushable cluster keys  
    b1 kdbhntab                             @101              --Number of tables (>1 in clusters)
    b2 kdbhnrow                             @102              --Number of rows   
    sb2 kdbhfrre                            @104              --First free row entry index; -1 = you have to add one
    sb2 kdbhfsbo                            @106              --Freespace begin offset  
    sb2 kdbhfseo                            @108              --Freespace end offset 
    b2 kdbhavsp                             @110              --Available space in the block
    b2 kdbhtosp                             @112              --Total available space when all TXs commit
 struct kdbt[1], 4 bytes                    @114    -- Table Directory
    b2 kdbtoffs                             @114    
    b2 kdbtnrow                             @116    
 
 sb2 kdbr[4]                                @118    --Row Directory 
 ub1 freespace[7970]                        @126     -- Free Space
 ub1 rowdata[92]                            @8096    -- Row Data
 ub4 tailchk                                @8188    --Tailchk
 

Header Block Types

ID

Type

01

Undo segment header

02

Undo data block

03

Save undo header

04

Save undo data block

05

Data segment header (temp, index, data and so on)

06

KTB managed data block (with ITL)

07

Temp table data block (no ITL)

08

Sort Key

09

Sort Run

10

Segment free list block

11

Data file header

 

kdbr 是块中的Row Directory结构,里面包含块中行的相关信息。
BBED> p kdbr      --p kdbr打印出row directory的信息。
sb2 kdbr[0]                                 @118      8062
sb2 kdbr[1]                                 @120      8040
sb2 kdbr[2]                                 @122      8020
sb2 kdbr[3]                                 @124      7996

如果需要知道N行开始的位置,可以使用print *kdbr[n-1] 即可。

BBED> p *kdbr[0]
rowdata[66]
-----------
ub1 rowdata[66]                             @8162     0x2c
BBED> p *kdbr[1]
rowdata[44]
-----------
ub1 rowdata[44]                             @8140     0x2c
BBED> p *kdbr[2]
rowdata[24]
-----------
ub1 rowdata[24]                             @8120     0x2c
BBED> p *kdbr[3]
rowdata[0]
----------
ub1 rowdata[0]                              @8096     0x2c
BBED>

上面显示,块中有4条记录,记录1开始于数据块偏移量8162,记录2开始于数据块偏移量8140,记录3开始于数据块偏移量1020,记录4开始于数据块偏移量8096处。
后面的0x2c 就是对应偏移量中的值,也就是行头的FLAG值 (Single Row = ROW_HEAD_PIECE + ROW_FIRST_PIECE + ROW_LAST_PIECE= 0x20 + 0x08 + 0x04= 0x2c)。
如果这些记录被删除了,那么相应的值将会变为0x3c ,如下:
SQL> select * from scott.dept;
    DEPTNO DNAME                        LOC
---------- ---------------------------- --------------------------
        10 ACCOUNTING                   NEW YORK
        20 RESEARCH                     DALLAS
        30 SALES                        CHICAGO
        40 OPERATIONS                   BOSTON
SQL> delete from scott.dept;
4 rows deleted.
SQL> commit;
Commit complete.
SQL> alter system checkpoint;
System altered.
SQL> select * from scott.dept;
no rows selected
SQL>
 
BBED> set filename '/test/orcl/orcl/users01.dbf';
        FILENAME        //test/orcl/orcl/users01.dbf
BBED> set block 16
        BLOCK#          16
BBED> print kdbr
sb2 kdbr[0]                                 @118      7970
sb2 kdbr[1]                                 @120      8040
sb2 kdbr[2]                                 @122      8020
sb2 kdbr[3]                                 @124      7996
BBED> print *kdbr[0]
rowdata[0]
----------
ub1 rowdata[0]                              @8070     0x3c
BBED> print *kdbr[1]
rowdata[70]
-----------
ub1 rowdata[70]                             @8140     0x3c
BBED> print *kdbr[2]
rowdata[50]
-----------
ub1 rowdata[50]                             @8120     0x3c
BBED> print *kdbr[3]
rowdata[26]
-----------
ub1 rowdata[26]                             @8096     0x3c
 
可以看到原来的记录不光偏移量变了,标志位也变为了0x3c,也就是DELETE标志位被设置为了1。
如果我们把标志位0x3c改为0x2c,数据将会重新找回来。
如下:
BBED> set offset 8070
        OFFSET          8070
BBED> dump /v count 16
 File: //test/orcl/orcl/users01.dbf (0)
 Block: 16      Offsets: 8070 to 8085  Dba:0x00000000
-------------------------------------------------------
 3c020302 c10b0a41 43434f55 4e54494e l <......ACCOUNTIN
 <16 bytes per line>
BBED> modify /x 2c     
BBED-00215: editing not allowed in BROWSE mode

BBED> set mode edit
        MODE            Edit
BBED> modify /x 2c
 File: //test/orcl/orcl/users01.dbf (0)
 Block: 16               Offsets: 8070 to 8085           Dba:0x00000000
------------------------------------------------------------------------
 2c020302 c10b0a41 43434f55 4e54494e
 <32 bytes per line>
BBED> set offset 8140
        OFFSET          8140
BBED> dump /v count 16
 File: //test/orcl/orcl/users01.dbf (0)
 Block: 16      Offsets: 8140 to 8155  Dba:0x00000000
-------------------------------------------------------
 3c020302 c1150852 45534541 52434806 l <......RESEARCH.
 <16 bytes per line>
BBED> modify /x 2c
 File: //test/orcl/orcl/users01.dbf (0)
 Block: 16               Offsets: 8140 to 8155           Dba:0x00000000
------------------------------------------------------------------------
 2c020302 c1150852 45534541 52434806
 <32 bytes per line>
BBED> set offset 8120
        OFFSET          8120
BBED> dump /v count 16
 File: //test/orcl/orcl/users01.dbf (0)
 Block: 16      Offsets: 8120 to 8135  Dba:0x00000000
-------------------------------------------------------
 3c020302 c11f0553 414c4553 07434849 l <......SALES.CHI
 <16 bytes per line>
BBED> modify /x 2c
 File: //test/orcl/orcl/users01.dbf (0)
 Block: 16               Offsets: 8120 to 8135           Dba:0x00000000
------------------------------------------------------------------------
 2c020302 c11f0553 414c4553 07434849
 <32 bytes per line>
BBED> set offset 8096 
        OFFSET          8096
BBED> dump /v count 16
 File: //test/orcl/orcl/users01.dbf (0)
 Block: 16      Offsets: 8096 to 8111  Dba:0x00000000
-------------------------------------------------------
 3c020302 c1290a4f 50455241 54494f4e l <....).OPERATION
 <16 bytes per line>
BBED> modify /x 2c
 File: //test/orcl/orcl/users01.dbf (0)
 Block: 16               Offsets: 8096 to 8111           Dba:0x00000000
------------------------------------------------------------------------
 2c020302 c1290a4f 50455241 54494f4e
 <32 bytes per line>
BBED> sum dba 4,16 apply
BBED-00312: no LISTFILE specified

BBED> sum apply
Check value for File 0, Block 16:
current = 0x26ff, required = 0x26ff
BBED> verify
DBVERIFY - Verification starting
FILE = //test/orcl/orcl/users01.dbf
BLOCK = 16
Block Checking: DBA = 16777232, Block Type = KTB-managed data block
data header at 0x110203064
kdbchk: the amount of space used is not equal to block size
        used=118 fsc=84 avsp=7970 dtl=8088
Block 16 failed with check code 6110
DBVERIFY - Verification complete
Total Blocks Examined         : 1
Total Blocks Processed (Data) : 1
Total Blocks Failing   (Data) : 1
Total Blocks Processed (Index): 0
Total Blocks Failing   (Index): 0
Total Blocks Empty            : 0
Total Blocks Marked Corrupt   : 0
Total Blocks Influx           : 0
BBED> print *kdbr[0]
rowdata[0]
----------
ub1 rowdata[0]                              @8070     0x2c
BBED> print *kdbr[1]
rowdata[70]
-----------
ub1 rowdata[70]                             @8140     0x2c
BBED> print *kdbr[2]
rowdata[50]
-----------
ub1 rowdata[50]                             @8120     0x2c
BBED> print *kdbr[3]
rowdata[26]
-----------
ub1 rowdata[26]                             @8096     0x2c
BBED> x /rncc offset 8070
rowdata[0]                                  @8070   
----------
: 0x2c (KDRHFL, KDRHFF, KDRHFH)
: 0x02
:    3
col    0[2] @8073: 10
col   1[10] @8076: ACCOUNTING
col    2[8] @8087: NEW YORK

BBED> x /rncc offset 8140
rowdata[70]                                 @8140   
-----------
: 0x2c (KDRHFL, KDRHFF, KDRHFH)
: 0x02
:    3
col    0[2] @8143: 20
col    1[8] @8146: RESEARCH
col    2[6] @8155: DALLAS

BBED> x /rncc offset 8120
rowdata[50]                                 @8120   
-----------
: 0x2c (KDRHFL, KDRHFF, KDRHFH)
: 0x02
:    3
col    0[2] @8123: 30
col    1[5] @8126: SALES
col    2[7] @8132: CHICAGO

BBED> x /rncc offset 8096
rowdata[26]                                 @8096   
-----------
: 0x2c (KDRHFL, KDRHFF, KDRHFH)
: 0x02
:    3
col    0[2] @8099: 40
col   1[10] @8102: OPERATIONS
col    2[6] @8113: BOSTON

再次回到sqlplus窗口,即可发现数据已经找回来:
SQL>  select * from scott.dept;
no rows selected
SQL> alter system flush buffer_cache;
System altered.
SQL> select * from scott.dept;
    DEPTNO DNAME                        LOC
---------- ---------------------------- --------------------------
        10 ACCOUNTING                   NEW YORK
        20 RESEARCH                     DALLAS
        30 SALES                        CHICAGO
        40 OPERATIONS                   BOSTON
SQL>
 
 

 
阅读(2648) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

天的星星2012-05-14 22:09:50

貌似XX门就是因为数据块恢复找到数据,才流传出来的吧?

7大爷2012-05-14 22:08:17

数据块的操作很考验的,许多高手都比较重视