内容介绍
BBED 是Block Browser/Editor的缩写,是Oracle的一个内部工具,不对外发布文档及支持。正因为如此,所以其功能强大,可以直接修改block上的内容,因此很显然可以通过BBED来修改表上的数据,直接绕过通过SQL语句对表的操作。因此当数据库出现极端的启动故障时,可以考虑此工具作为最后的抢救手段。
概念普及
Oracle8i 的BBED在windows 平台下的$ORACLE_HOME/bin下可以找到(从9i开始没有了),而 在linux上面有,需要编译。
在9i/10g中连接生成bbed:
$ cd $ORACLE_HOME/rdbms/lib
$ make-f ins_rdbms.mk $ORACLE_HOME/rdbms/lib/bbed
BBED 是Oracle 内部使用的命令,所以Oracle 不提供技术支持。 为了安全,BBED设置了口令保护,默认密码为blockedit。 还需要注意的是bbed直接修改文件有很大的风险,使用需要慎重。最好先做好备份,以防出现错误无法回退。
字节顺序
字节顺序是指占内存多于一个字节类型的数据在内存中的存放顺序,通常有小端(little-endian)、大端(big-endian)两种字节顺序。小端字节序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处;大端字节序是高字节数据存放在低地址处,低字节数据存放在高地址处。考虑 将 0x1234abcd (0xcd 是低位,0x12是高位,这是人类的书写顺序), 写入到以 0x0000 开始的内存地址中, 那么它在内存中的存储可能有如下两种情况:
注意 , 我们的书写字数据表示法是从高字节位 ---> 低字节位(从左到右)
如,十进制数 “一百二十”我们手工书写出来则是 120 ,第一个数字 1 就是高字节位。
内存地址 生长方向为 : 从左到右 由低到高 ( 这是不变的 )
|
0x0001 |
0x0002 |
0x0003 |
0x0004 | |
|
little-endian |
0xcd |
0xab |
0x34 |
0x12 |
|
big-endian |
0x12 |
0x34 |
0xab |
0xcd |
Oracle 内可以通过一张视图查询出各主机OS平台的大/小字节序,如下: SQL>select * from v$transportable_platform PLATFORM_ID PLATFORM_NAME ENDIAN_FORMAT ----------- --------------------------------------------- -------------- 1 Solaris[tm] OE (32-bit) Big 2 Solaris[tm] OE (64-bit) Big 7 Microsoft Windows IA (32-bit) Little 10 Linux IA (32-bit) Little 6 AIX-Based Systems (64-bit) Big 3 HP-UX (64-bit) Big 5 HP Tru64 UNIX Little 4 HP-UX IA (64-bit) Big 11 Linux IA (64-bit) Little 15 HP Open VMS Little 8 Microsoft Windows IA (64-bit) Little 9 IBM zSeries Based Linux Big 13 Linux x86 64-bit Little 16 Apple Mac OS Big 12 Microsoft Windows x86 64-bit Little 17 Solaris Operating System (x86) Little 18 IBM Power Based Linux Big 20 Solaris Operating System (x86-64) Little 19 HP IA Open VMS Little
可以看出来X86平台cpu都是little-endian,而小型机上使用的精简指令集架构cpu则多采用big-endian。了解这个对于适用bbed很重要,因为bbed里面就是用16进制显示的,修改也要采用16进制数。在不同平台下这个修改的16进制数据就需要根据数据库所在平台是little-endian或者big-endian来修改。
常用命令
下面是几个常用的:
set 设定当前的环境
show 查看当前的环境参数,跟 sqlplus 的同名命令类似。
dump 列出指定 block 的内容
find 在指定的 block 中查找指定的字符串,结果是显示出字符串,及其偏移量 --offset ,偏移量就是在 block 中的字节数
modify 修改指定 block 的指定偏移量的值,可以在线修改。
copy 把一个 block 的内容 copy 到另一个 block 中
verify 检查当前环境是否有坏块
sum 计算 block 的 checksum , modify 之后 block 就被标识为坏块, current checksum 与 reqired checksum 不一致, sum 命令可以计算出新的 checksum 并应用到当前块。
undo 回滚当前的修改操作,如果手误做错了, undo 一下就 ok 了,回到原来的状态。
revert 回滚所有之前的修改操作,意思就是 undo all
BBED> help all 可以查看更详细的帮助信息
案例讲解
某客户数据库因为机房突然掉电导致数据库宕机。而更倒霉的是rman备份文件也被损坏,只能恢复出数据文件,而归档日志则无法恢复。备份集恢复出来的数据因不一致导致数据库无法正常open。
查询各数据文件scn结果如下
SQL> select file#,checkpoint_change#,resetlogs_change#,fuzzy,status from v$datafile_header order by 2;
10 26271884 994063 NO ONLINE
16 26271884 994063 YES ONLINE
7 26271884 994063 NO ONLINE
13 26271884 994063 NO ONLINE
5 26271884 994063 NO ONLINE
17 26271884 994063 YES ONLINE
8 26271895 994063 NO ONLINE
6 26271895 994063 NO ONLINE
18 26271895 994063 YES ONLINE
2 26271895 994063 NO ONLINE
14 26271895 994063 YES ONLINE
11 26271895 994063 NO ONLINE
4 26271915 994063 YES ONLINE
1 26271915 994063 NO ONLINE
15 26271915 994063 YES ONLINE
12 26271915 994063 NO ONLINE
3 26271915 994063 NO ONLINE
9 26271915 994063 NO ONLINE
SQL> select max(checkpoint_change#) from v$datafile_header;
26271915 最大的scn
SQL> select min(checkpoint_change#) from v$datafile_header;
26271884 最小的scn
查询所有小于最大的scn的数据文件信息
SQL> select file#||' '||name||' '||bytes from v$datafile_header where checkpoint_change#<26271915;
2 E:\APP\ADMINISTRATOR\ORADATA\ORCL\SYSAUX01.DBF 880803840
5 E:\APP\ADMINISTRATOR\ORADATA\DATE\USERS05.DBF 32212254720
6 E:\APP\ADMINISTRATOR\ORADATA\DATE\USERS06.DBF 32212254720
7 E:\APP\ADMINISTRATOR\ORADATA\DATE\USERS07.DBF 32212254720
8 E:\APP\ADMINISTRATOR\ORADATA\DATE\USERS08.DBF 32212254720
10 E:\APP\ADMINISTRATOR\ORADATA\DATE\USERS10.DBF 32212254720
11 E:\APP\ADMINISTRATOR\ORADATA\DATE\USERS11.DBF 32212254720
13 E:\APP\ADMINISTRATOR\ORADATA\DATE\USERS13.DBF 32212254720
14 E:\APP\ADMINISTRATOR\ORADATA\DATE\USERS14.DBF 32212254720
16 E:\APP\ADMINISTRATOR\ORADATA\DATE\USERS16.DBF 32212254720
17 E:\APP\ADMINISTRATOR\ORADATA\DATE\USERS17.DBF 15644753920
18 E:\APP\ADMINISTRATOR\ORADATA\DATE\USERS18.DBF 15571353600
这12个数据文件就是我们要通过bbed修改的文件,首先在操作系统级别把这几个文件全部备份一遍。
因为1号文件的scn值就是最大scn值,故可以直接以该文件作为参考修改另外的12个文件
SQL> select file#||' '||name||' '||bytes from v$datafile_header where file#=1;
1 E:\APP\ADMINISTRATOR\ORADATA\ORCL\SYSTEM01.DBF 933232640
1. 创建文件列表参数文件,将以上 sql 查询结果保存到一个文本文档中如 e:\filelist.txt
2. 创建参数文件,该参数文件指定了 bbed 的基本环境 如 e:\bbedpar.txt
blocksize=8192
listfile=e:\filelist.txt
mode=edit
( 3 )使用参数文件连接 bbed
[oracle@dg1 ~]$ bbed parfile= e:\bbedpar.tx
Password: blockedit
BBED: Release 2.0.0.0.0 - Limited Production on Tue Jun 11 15:21:27 2015
Copyright (c) 1982, 2011, Oracle and/or its affiliates. All rights reserved.
************* !!! For Oracle Internal Use only !!! ***************
BBED> set dba 1 ,1 设定位置为1号文件的第一个数据块,数据文件头就在这里
BBED> p kcvfh
BBED-00400: invalid blocktype (00)
正常情况下第一个数据块就是文件头,但是这里报错应该是跟我使用的是在oracle 10g版本下编译出来的bbed有关,用11g版本编译出来的bbed则不会有这个问题。通过这里发现用10g版本的bbed修改11g数据文件,则数据文件头是2号块。
BBED> set dba 1,2
BBED> p kcvfh
struct kcvfh, 340 bytes @0
struct kcvfhbfh, 20 bytes @0
ub1 type_kcbh @0 0x0b
ub1 frmt_kcbh @1 0xa2
ub1 spare1_kcbh @2 0x00
ub1 spare2_kcbh @3 0x00
ub4 rdba_kcbh @4 0x00800001
ub4 bas_kcbh @8 0x00000000
ub2 wrp_kcbh @12 0x0000
ub1 seq_kcbh @14 0x01
ub1 flg_kcbh @15 0x04 (KCBHFCKV)
ub2 chkval_kcbh @16 0x74bd
ub2 spare3_kcbh @18 0x0000
struct kcvfhhdr, 76 bytes @20
显示1号文件2号数据块offset为484开始的内容,v$data_file_header的checkpoint_change# 列值就取自这里
BBED> dump offset 484 dba 1,2
File: E:\APP\ADMINISTRATOR\ORADATA\ORCL\SYSTEM01.DBF (1)
Block: 2 Offsets: 484 to 995 Dba:0x00400002
------------------------------------------------------------------------
abe09001 00000000 55de9134 01000000 11090000 551b0000 1000f54d 02000000
……
对比显示2号文件2号数据块offset为484开始的内容
BBED> dump offset 484 dba 2,2
File: E:\APP\ADMINISTRATOR\ORADATA\ORCL\SYSAUX01.DBF (2)
Block: 2 Offsets: 484 to 995 Dba:0x00800002
------------------------------------------------------------------------
97e09001 00000000 51de9134 01000000 11090000 c01a0000 1000db21 02000000
……
而根据v$datafile_header.checkpoint_change# 转换应该是
由于X86_64架构下的windows属于 Little endian , 故通过bbed看到的16进制值是反向的,把上面查询到的v$datafile_header.checkpoint_change#转换成16进制值则可以发现正好根dump出来的内容一致。
SQL> select to_char(26271895,'xxxxxxxx') from dual;
TO_CHAR(26271895,'XXXXXXXX')
----------------------------
190e097 其在内存里面根据 Little endian 存储为 97e09001
SQL> select to_char(26271915,'xxxxxxxx') from dual;
TO_CHAR(26271915,'XXXXXXXX')
----------------------------
190e0ab 其在内存里面根据 Little endian 存储为 abe09001
将2号文件的checkpoint_change# 修改成同一号文件一致
BBED> modify /x abe09001 offset 484
File: E:\APP\ADMINISTRATOR\ORADATA\ORCL\SYSAUX01.DBF (2)
Block: 2 Offsets: 484 to 995 Dba:0x00800002
------------------------------------------------------------------------
abe09001 00000000 51de9134 01000000 11090000 c01a0000 1000db21 02000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0c000c00 0c000100 00000000 00000000 00000000 02008000 d07e4101 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
<32 bytes per line>
BBED> verify
DBVERIFY - Verification starting
FILE = E:\APP\ADMINISTRATOR\ORADATA\ORCL\SYSAUX01.DBF
BLOCK = 1
Block 1 is corrupt
***
Corrupt block relative dba: 0x00800001 (file 0, block 1)
Bad header found during verification
Data in bad block -
type: 11 format: 162 rdba: 0x00800001
last change scn: 0x0000.00000000 seq: 0x1 flg: 0x04
consistency value in tail: 0x00000b01
check value in block header: 0x74bd, computed block checksum: 0x3c
spare1: 0x0, spare2: 0x0, spare3: 0x0
***
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 : 1 这里就是刚刚修改过的块,其自动被标记为损坏
Total Blocks Influx : 0
BBED> sum apply 计算修改后的数据块的checksum值,然后写入数据块的offset
Check value for File 2, Block 2:
current = 0x7481, required = 0x7481
再次查询,可以看到2号文件的checkpoint_change# 已经更正过来
SQL> select file#,checkpoint_change#,resetlogs_change#,fuzzy from v$datafile_header where file# in(1,2);
FILE# CHECKPOINT_CHANGE# RESETLOGS_CHANGE# FUZ
---------- ------------------ ----------------- ---
1 26271915 994063 NO
2 26271915 994063 NO
按照此步骤修改余下的11 个文件,过程略,修改完成后
SQL> alter database open resetlogs;
alter database open resetlogs
*
第 1 行出现错误:
ORA-01194: 文件 4 需要更多的恢复来保持一致性
ORA-01110: 数据文件 4: 'E:\APP\ADMINISTRATOR\ORADATA\ORCL\USERS01.DBF'
此时检查发现数据文件checkpoint_change# 已经一致,但是个别文件fuzzy 为yes ,这个说明数据文件里面的某些块的scn 比数据文件头的checkpoint_change# 还大,也需要通过日志恢复,而目前环境也不可能实现,继续bbed 修改。
修改fuzzy ,该标记为在数据文件头的offset=138 处,0 表示no ,也就是修改成0 即可。
BBED> d offset 138 dba 1,2
File: E:\APP\ADMINISTRATOR\ORADATA\ORCL\SYSTEM01.DBF (1)
Block: 2 Offsets: 138 to 649 Dba:0x00400002
------------------------------------------------------------------------
0020ca0a 00005e1b 9434c90a 00000000 00000000 00000000 00000000 00000000
BBED> d offset 138 dba 4,2
File: E:\APP\ADMINISTRATOR\ORADATA\ORCL\USERS01.DBF (4)
Block: 2 Offsets: 138 to 649 Dba:0x01000002
------------------------------------------------------------------------
4000c70a 00005e1b 9434c60a 00000000 00000000 00000000 00000000 00000000
BBED> set dba 4,2
BBED> m /x 00 offset 138
BBED> sum apply
重复此步骤直到所有文件修改完毕。最后 alter database open resetlogs 成功打开数据库。打开后发现一张核心表最近一天的导出报错,该表基本上每时每刻都在插入数据,虽然bbed 修改后强制打开了数据库,但是数据的一致性实际上不可避免的要被破坏,但是至少恢复了上一次备份到断电前一天的数据。
技术结论
Bbed 作为oracle的内部工具,确实具备很多强大的功能,但是也并非无所不能,极端情况下可以利用它来修改数据文件继而打开数据库,单是内部的数据一致性是不能得到保障的,因此一定要提前备份数据。
