DBA实验手册第3讲 运用bbed工具恢复delete误删除的数据
--(南宋)陆游《冬夜读书示子聿》
前 言
01
当面试官问你,Oracle数据库的表被执行了delete操作并已提交,如何找回数据呢?常规的修复方法包括闪回表、impdp单表导入、rman单表恢复,除了这些方法之外,还可以考虑使用BBED工具找回数据。今天我想跟大家先分享下如何运用bbed工具恢复delete误删除的数据。想深入学习bbed工具的朋友,一定要动手多做实验!
当Oracle数据库执行delete语句后,表中数据实际上并没有被删除,而是将行标记为已删除,表示数据块可以重用。因此,在数据块没有被覆盖之前,将数据块上的标志位的已删除标记去掉,就可以找回数据了。
往期文章推荐
02
1、DBA实验手册第1讲 运用bbed工具和SQL语句学习bootstrap$表存储信息
2、DBA实验手册第2讲 运用bbed工具恢复bootstrap$表数据
欢迎关注公众号!
数据块简介
03
在Oracle数据库中,块是存储数据的最小单位,一个块通常大小为8KB。块是Oracle进行I/O操作的基本单位,它用于存储表数据、索引、回滚段数据、临时数据等。数据库中的所有数据都存储在块中,包括表的行数据、索引键值和数据字典信息等。每个数据块的块头结构都是固定的。所有的数据块都包含相同的头结构----kcbh。行状态存储在每行的行标头中,该行标头占用每行的前几个字节。行标头由行标记、锁字节(ITL条目)和列计数组成。第一个Row标志是一个单字节,它保存一个显示行状态的位掩码。位掩码解码如下:

因此行标头(数据块dump文件中的“--H-FL--”对应上面表格的8列信息)=Head of row picec + First data picec + Last data picec = 32+8+4 = 44 转换为16进制 0x2c。当数据被删除后,行标头+(Deleted 16) =32+16+8+4=60 转换为16进制 0x3c。
SQL> select to_char(44,'xxxxxxxx') as hex1,to_char(60,'xxxxxxxx') as hex2 from dual;HEX1 HEX2--------- --------- 2c 3c
因此,当数据未被覆盖时,修改行标头0x3c为0x2c即可恢复。
上面的内容你看明白了没有,如果没理解,可以再认真读一遍,然后再阅读下面的内容。
实验环境准备
04
数据库版本:Oracle 12.1.0.2.0 单机版
BBED版本:BBED 2.0.0.0.0
操作系统:Red Hat Enterprise Linux Server 6.5
若想获取实验环境相关的软件包、文档资料以及批量恢复脚本,可以查看文章的附件部分。
1、创建测试表空间和测试用户
SQL> select file_name from dba_data_FILES;SQL> create tablespace mytbs datafile '/u01/app/oracle/oradata/PROD4/mytbs.dbf' size 50m;SQL> create user myuser identified by oracle default tablespace mytbs;SQL> grant resource,connect, select any dictionary to myuser;SQL> alter user myuser quota unlimited on mytbs;
2、创建测试表与数据,这边只模拟2行记录,这样的话后面dump出来的日志会简短些。
SQL> create table mytable (id number,name varchar2(10));SQL> insert into mytable values(100,'dba1');SQL> insert into mytable values(200,'dba2');
3、刷新缓存,让脏数据落盘
SQL> alter system flush buffer_cache;System altered.
第1次dump数据块
05
1、查看表数据分布情况
1 数据文件号(dba_objects)SQL> select object_id from dba_objects where object_name = 'MYTABLE' and owner = 'MYUSER'; OBJECT_ID---------- 939962、数据分布在2号文件的133数据块SQL> select distinct dbms_rowid.rowid_relative_fno(rowid) rfno, dbms_rowid.rowid_block_number(rowid) blocknum from myuser.mytable order by 2; RFNO BLOCKNUM---------- ---------- 2 1333、表存储在2号文件的第1个区,从128号数据块开始,占用8个块,共65536字节(8k*8个)SQL> select EXTENT_ID,FILE_ID,BLOCK_ID,BYTES,BLOCKS from dba_extents where segment_name='MYTABLE'; EXTENT_ID FILE_ID BLOCK_ID BYTES BLOCKS---------- ---------- ---------- ---------- ---------- 0 2 128 65536 84、数据段基本信息SQL> col segment_name for a15SQL> col tablespace_name for a15SQL> select segment_name,tablespace_name,header_file,header_block from dba_segments where segment_name='MYTABLE';SEGMENT_NAME TABLESPACE_NAME HEADER_FILE HEADER_BLOCK--------------- --------------- ----------- ------------MYTABLE MYTBS 2 130
2、数据块转储
转储MYTABLE表的133数据块,查看删除之前的内容。
1、转储命令SQL> alter system dump datafile 2 block 133;System altered.SQL> select value from v$diag_info where name ='Default Trace File';VALUE--------------------------------------------------------------------------------/u01/app/oracle/diag/rdbms/prod4/PROD4/trace/PROD4_ora_85254.trc2、133数据块转储内容没有执行alter system flush buffer_cache,即脏数据未落盘Start dump data blocks tsn: 9 file#:2 minblk 133 maxblk 133Block dump from cache:【dump出缓存cache的内容】Dump of buffer cache at level 4 for pdb=0 tsn=9 rdba=8388741BH (0x77ff6bd8) file#: 2 rdba: 0x00800085 (2/133) class: 1 ba: 0x77f48000 set: 5 pool: 3 bsz: 8192 bsi: 0 sflg: 0 pwc: 0,0 dbwrid: 0 obj: 93996 objn: 93996 tsn: [0/9] afn: 2 hint: f hash: [0x7cbe8e28,0x7cbe8e28] lru: [0x77ff6e00,0x77ff6b80] obj-flags: object_ckpt_list ckptq: [0x77ff6a98,0x77ff6d18] fileq: [0x77ff6aa8,0x77ff6d28] objq: [0x77ff6e28,0x77ff6ba8] objaq: [0x77ff6e38,0x77ff6bb8] st: XCURRENT md: NULL fpin: 'ktspbwh2: ktspfmdb' fscn: 0x0.3d4778 tch: 4 flags: buffer_dirty LRBA: [0x2.1f1b.0] LSCN: [0x0.3d4778] HSCN: [0x0.3d477c] HSUB: [1]Block dump from disk:【dump出磁盘的内容】Encrypted block content will not be dumped. Dumping header only.buffer tsn: 9 rdba: 0x00000085 (0/133)scn: 0x0.0 seq: 0x01 flg: 0x05 tail: 0x00000001frmt: 0x02 chkval: 0xa785 type: 0x00=unknownHex dump of initial block header 1 = INITIALDump of memory from 0x00007FA34753E200 to 0x00007FA34753E2147FA34753E200 0000A200 00000085 00000000 05010000 [................]7FA34753E210 0000A785 [....] End dump data blocks tsn: 9 file#: 2 minblk 133 maxblk 1333、133数据块转储内容执行alter system flush buffer_cache,即脏数据已落盘Start dump data blocks tsn: 9 file#:2 minblk 133 maxblk 133Block dump from cache:【dump出缓存cache的内容】Dump of buffer cache at level 4 for pdb=0 tsn=9 rdba=8388741BH (0x77ff6bd8) file#: 2 rdba: 0x00800085 (2/133) class: 1 ba: 0x77f48000 set: 5 pool: 3 bsz: 8192 bsi: 0 sflg: 0 pwc: 0,0 dbwrid: 0 obj: 93996 objn: 93996 tsn: [0/9] afn: 2 hint: f hash: [0x7cbe8e28,0x7cbe8e28] lru: [0x753dcc80,0x743da840] lru-flags: on_auxiliary_list ckptq: [NULL] fileq: [NULL] objq: [NULL] objaq: [NULL] st: FREE md: NULL fpin: 'ktspbwh2: ktspfmdb' fscn: 0x0.3d4778 tch: 0 lfb: 33 flags:Block dump from disk:【dump出磁盘的内容】buffer tsn: 9 rdba: 0x00800085 (2/133)scn: 0x0.3d477c seq: 0x01 flg: 0x06 tail: 0x477c0601frmt: 0x02 chkval: 0xbd31 type: 0x06=trans dataHex dump of block: st=0, typ_found=1Dump of memory from 0x00007FD4F7ECA200 to 0x00007FD4F7ECC2007FD4F7ECA200 0000A206 00800085 003D477C 06010000 [........|G=.....]7FD4F7ECA210 0000BD31 00000001 00016F2C 003D4778 [1.......,o..xG=.]7FD4F7ECA220 00000000 00320002 00800080 00030007 [......2.........]7FD4F7ECA230 000006C4 0100092A 00040123 00002002 [....*...#.... ..]7FD4F7ECA240 003D477C 00000000 00000000 00000000 [|G=.............]7FD4F7ECA250 00000000 00000000 00000000 00000000 [................]7FD4F7ECA260 00000000 00020100 0016FFFF 1F6C1F82 [..............l.]7FD4F7ECA270 00001F6C 1F8D0002 00001F82 00000000 [l...............]7FD4F7ECA280 00000000 00000000 00000000 00000000 [................] Repeat 501 times7FD4F7ECC1E0 00000000 012C0000 03C20202 61626404 [......,......dba]7FD4F7ECC1F0 02012C32 0402C202 31616264 477C0601 [2,......dba1..|G]Block header dump: 0x00800085 Object id on Block? Y seg/obj: 0x16f2c csc: 0x00.3d4778 itc: 2 flg: E typ: 1 - DATA brn: 0 bdba: 0x800080 ver: 0x01 opc: 0 inc: 0 exflg: 0 Itl Xid Uba Flag Lck Scn/Fsc0x01 0x0007.003.000006c4 0x0100092a.0123.04 --U- 2 fsc 0x0000.003d477c0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000bdba: 0x00800085data_block_dump,data header at 0x7fd4f7eca264===============tsiz: 0x1f98hsiz: 0x16pbl: 0x7fd4f7eca264 76543210flag=--------ntab=1nrow=2 【表示有2行数据】frre=-1fsbo=0x16fseo=0x1f82avsp=0x1f6ctosp=0x1f6c0xe:pti[0] nrow=2 offs=00x12:pri[0] offs=0x1f8d0x14:pri[1] offs=0x1f82block_row_dump: 【每行记录信息】tab 0, row 0, @0x1f8d 【第1行记录】tl: 11 fb: --H-FL-- lb: 0x1 cc: 2col 0: [ 2] c2 02col 1: [ 4] 64 62 61 31tab 0, row 1, @0x1f82 【第2行记录】tl: 11 fb: --H-FL-- lb: 0x1 cc: 2col 0: [ 2] c2 03col 1: [ 4] 64 62 61 32end_of_block_dumpEnd dump data blocks tsn: 9 file#: 2 minblk 133 maxblk 133
3、通过bbed查看数据文件结构体信息
BBED> set dba 2,133 【定位到2,133数据块】 DBA 0x00800085 (8388741 2,133)BBED> pkcbh.type_kcbh--------------ub1 type_kcbh @0 0x06BBED> map File: /u01/app/oracle/oradata/PROD4/mytbs.dbf (2) Block: 133 Dba:0x00800085------------------------------------------------------------ KTB Data Block (Table/Cluster) struct kcbh, 20 bytes @0 【块头】 struct ktbbh, 72 bytes @20 【事务层头结构】 struct kdbh, 14 bytes @100 【事务型数据(data类型)的数据层头结构】 struct kdbt[1], 4 bytes @114 【数据层表索引】 sb2 kdbr[2] @118 【数据层行索引】 ub1 freespace[8044] @122 【空闲空间】 ub1 rowdata[22] @8166 【数据存放内容】 ub4 tailchk @8188 【尾块】 BBED> p kdbr 【行索引,行索引也是一个数组,定义了该块中包含的所有行数据的位置。行索引的插槽由2个字节组成】sb2 kdbr[0] @118 8077sb2 kdbr[1] @120 8066BBED> p *kdbr[0] 【查看行索引】rowdata[11]-----------ub1 rowdata[11] @8177 0x2c 【0x2c表示标志位是--H-FL--(32+8+4 = 44转换成16进制为2c)】BBED> x rnc *kdbr[0]rowdata[11] @8177 -----------flag@8177: 0x2c (KDRHFL, KDRHFF, KDRHFH)lock@8178: 0x01cols@8179: 2col 0[2] @8180: 100 【第1条记录 第1列数据】col 1[4] @8183: dba1 【第1条记录 第2列数据】BBED> p *kdbr[1]rowdata[0]----------ub1 rowdata[0] @8166 0x2c 【0x2c表示标志位是--H-FL--(32+8+4 = 44转换成16进制为2c)】BBED> x rnc *kdbr[1]rowdata[0] @8166 ----------flag@8166: 0x2c (KDRHFL, KDRHFF, KDRHFH)lock@8167: 0x01cols@8168: 2col 0[2] @8169: 200 【第2条记录 第1列数据】col 1[4] @8172: dba2 【第2条记录 第2列数据】
模拟数据丢失
06
删除测试表记录,模拟delete误删除操作
SQL> delete from myuser.mytable;2 rows deleted.SQL> commit;Commit complete.SQL> alter system flush buffer_cache;System altered.
第2次dump数据块
07
1、再次转储数据块,查看数据删除后的信息
1、转储数据块操作SQL> alter system dump datafile 2 block 133;System altered.SQL> select value from v$diag_info where name ='Default Trace File';VALUE--------------------------------------------------------------------------------/u01/app/oracle/diag/rdbms/prod4/PROD4/trace/PROD4_ora_120104.trc
2、具体的trace日志内容
*** 2024-06-06 23:40:52.588Start dump data blocks tsn: 9 file#:2 minblk 133 maxblk 133Block dump from cache:Dump of buffer cache at level 4 for pdb=0 tsn=9 rdba=8388741BH (0x7a7f6f98) file#: 2 rdba: 0x00800085 (2/133) class: 1 ba: 0x7a74e000 set: 5 pool: 3 bsz: 8192 bsi: 0 sflg: 0 pwc: 0,0 dbwrid: 0 obj: 93996 objn: 93996 tsn: [0/9] afn: 2 hint: f hash: [0x7cbe8e28,0x7cbe8e28] lru: [0x74be1b40,0x783d6600] lru-flags: moved_to_tail on_auxiliary_list ckptq: [NULL] fileq: [NULL] objq: [NULL] objaq: [NULL] st: FREE md: NULL fpin: 'kdswh01: kdstgr' fscn: 0x0.0 tch: 0 lfb: 33 flags:Block dump from disk:buffer tsn: 9 rdba: 0x00800085 (2/133)scn: 0x0.3dc57b seq: 0x01 flg: 0x06 tail: 0xc57b0601frmt: 0x02 chkval: 0x706c type: 0x06=trans dataHex dump of block: st=0, typ_found=1Dump of memory from 0x00007F717BCC2200 to 0x00007F717BCC42007F717BCC2200 0000A206 00800085 003DC57B 06010000 [........{.=.....]7F717BCC2210 0000706C 00000001 00016F2C 003DC57A [lp......,o..z.=.]7F717BCC2220 00000000 00320002 00800080 00030007 [......2.........]7F717BCC2230 000006C4 0100092A 00040123 00008000 [....*...#.......]7F717BCC2240 003D477C 001A0009 0000081B 010011E8 [|G=.............]7F717BCC2250 002C013A 00122002 003DC57B 00000000 [:.,.. ..{.=.....]7F717BCC2260 00000000 00020100 0016FFFF 1F6C1F82 [..............l.]7F717BCC2270 00001F82 1F8D0002 00001F82 00000000 [................]7F717BCC2280 00000000 00000000 00000000 00000000 [................] Repeat 501 times7F717BCC41E0 00000000 023C0000 03C20202 61626404 [...... set offset 8177 【从上述日志文件可知,第1行的偏移位置为8177】 OFFSET 8177BBED> d v File: u01/app/oracle/oradata/PROD4/mytbs.dbf (2) Block: 133 Offsets: 8177 to 8191 Dba:0x00800085------------------------------------------------------- 3c020202 c2020464 62613101 067bc5 l m x 2c offset 8177 File: u01/app/oracle/oradata/PROD4/mytbs.dbf (2) Block: 133 Offsets: 8177 to 8191 Dba:0x00800085------------------------------------------------------------------------ 2c020202 c2020464 62613101 067bc5 BBED> set offset 8166 【从上述日志文件可知,第2行的偏移位置为8166】 OFFSET 8166BBED> d /v File: /u01/app/oracle/oradata/PROD4/mytbs.dbf (2) Block: 133 Offsets: 8166 to 8191 Dba:0x00800085------------------------------------------------------- 3c020202 c2030464 6261322c 020202c2 l m /x 2c offset 8166 File: /u01/app/oracle/oradata/PROD4/mytbs.dbf (2) Block: 133 Offsets: 8166 to 8191 Dba:0x00800085------------------------------------------------------------------------ 2c020202 c2030464 6261322c 020202c2 02046462 61310106 7bc5 BBED> sum apply;Check value for File 2, Block 133:current = 0x607c, required = 0x607c
2、验证数据已恢复出来
SQL> alter system flush buffer_cache;System altered.SQL> select * from myuser.mytable; ID NAME---------- ---------- 100 dba1 200 dba2
实验总结
10
当Oracle数据库执行delete语句后,表中数据实际上并没有被删除,而是将行标记为已删除,表示数据块可以重用。因此,在数据块没有被覆盖之前,将数据块上的标志位的已删除标记去掉,就可以找回数据了。通过对比数据块删除数据前、后两次转储信息,可以知道块中行存在数据时标志位是fb: --H-FL--,行数据被删除后标志位变成fb: --HDFL--,换行成16进制,标志位改成0x2c表示存在数据。通过上述实验,我们在数据未被覆盖时,修改行标头0x3c为0x2c,然后把数据恢复出来。bbed恢复过程总结如下: