Oracle Data Block 详细结构解析
> 基于 Oracle 内部培训教材 DSI 系列(DSI401/DSI402/DSI403)整理,用于代码开发中的 Block 异常恢复与解析。
> 适用版本:Oracle 8i/9i(核心结构在后续版本中基本保持兼容)
一、Oracle Data Block 整体布局
一个 Oracle 数据块由下至上由以下部分组成:
```
┌──────────────────────────────┐
│ Cache Layer (KCBH) │ ← 20 bytes,固定
├──────────────────────────────┤
│ Transaction Layer: Fixed │ ← KTBBH,固定部分
│ (KTBBH) │
├──────────────────────────────┤
│ Transaction Layer: Variable │ ← KTBIT,ITL 条目(可变数量)
│ (ITL slots) │
├──────────────────────────────┤
│ Table Directory │ ← pti\[\],多表簇时使用
├──────────────────────────────┤
│ Row Directory │ ← pri\[\],行偏移数组
├──────────────────────────────┤
│ Free Space │ ← 空闲空间(从中间向两端扩展)
├──────────────────────────────┤
│ Row Data │ ← 行数据(从底部向上增长)
├──────────────────────────────┤
│ Block Footer (Tail) │ ← 4 bytes,一致性校验
└──────────────────────────────┘
```
Oracle 数据块是物理存储的基本单位,通常由多个 OS 块组成,也称为 **page**。标准块大小在数据库创建时通过 `DB_BLOCK_SIZE` 参数设置。 pdf_1
二、Cache Layer(KCBH)--- 缓存层(20 bytes)
Cache Layer 是块头的第一部分,由结构体 `kcbh` 描述,固定 **20 字节**。 pdf_1pdf_2
2.1 C 结构体定义(kcbh.h)
```c
struct kcbh
{
ub1 type_kcbh; /* Block type */
ub1 frmt_kcbh; /* Block format: #define KCBH_FRMT8 2 */
ub1 spare1_kcbh; /* 保留(未使用) */
ub1 spare2_kcbh; /* 保留(未使用) */
krdba rdba_kcbh; /* Relative DBA(相对数据块地址) */
ub4 bas_kcbh; /* SCN Base(SCN 基值) */
ub2 wrp_kcbh; /* SCN Wrap(SCN 包装值) */
ub1 seq_kcbh; /* Sequence number(同一 SCN 下的变更序列号) */
ub1 flg_kcbh; /* Flag 标志位(定义在 kcbh.h) */
ub2 chkval_kcbh; /* Check value(校验值,可选) */
};
```
pdf_2
2.2 字段详细说明
| 偏移 | 大小 | 字段名 | dump 中标识 | 说明 |
|------|------|--------|-------------|------|
| 0x00 | 1 | type_kcbh | `type` | 块类型(见下节块类型枚举) |
| 0x01 | 1 | frmt_kcbh | `frmt` | 块格式版本,Oracle8+ 为 0x02 |
| 0x02 | 1 | spare1_kcbh | --- | 保留 |
| 0x03 | 1 | spare2_kcbh | --- | 保留 |
| 0x04 | 4 | rdba_kcbh | `rdba` | 相对数据块地址(文件号+块号) |
| 0x08 | 4 | bas_kcbh | `scn` 前半部分 | SCN Base(低 32 位) |
| 0x0C | 2 | wrp_kcbh | `scn` 后半部分 | SCN Wrap(高 16 位) |
| 0x0E | 1 | seq_kcbh | `seq` | 序列号,每次变更递增 |
| 0x0F | 1 | flg_kcbh | `flg` | 标志位 |
| 0x10 | 2 | chkval_kcbh | `chkval` | 可选校验和(`DB_BLOCK_CHECKSUM=TRUE` 时有效) |
**总计:20 字节** pdf_1pdf_2
2.3 块类型枚举(type_kcbh)
| 类型值 | 宏定义 | 说明 |
|--------|--------|------|
| 0x01 | --- | 索引块(Index) |
| 0x02 | --- | 表/索引数据块(Table/Index,早期版本) |
| 0x06 | --- | **事务数据块(trans data)**--- 最常用的数据块 |
| 0x10 | --- | 数据段头部 --- UNLIMITED |
| 0x11 | --- | 数据段头部 --- 带 Free List |
| 0x16 | --- | 数据段 Free List 块 |
| 0x1D | --- | KTFB Bitmapped File Space Header |
| 0x20 | --- | FIRST LEVEL BITMAP BLOCK(L1 BMB) |
| 0x21 | --- | SECOND LEVEL BITMAP BLOCK(L2 BMB) |
| 0x23 | --- | PAGETABLE SEGMENT HEADER(ASSM 段头) |
pdf_2\]\[pdf_5\]\[pdf_6
2.4 Block 格式版本(frmt_kcbh)
-
**Oracle8 / 8i / 9i**:值为 `0x02`
-
早期版本(v6/v7):值为其他
2.5 SEQ 序列号特殊值
```c
SEQ -> 0 /* 非日志变更,不推进 seq# */
SEQ -> (UB1MAXVAL-1) /* 最大可能序列号 */
SEQ -> (UB1MAXVAL) /* 表示块已损坏(corrupt),值为 0xFF */
```
pdf_2
2.6 典型 Block Dump 实例
```
Start dump data blocks tsn: 4 file#: 9 minblk 5357 maxblk 5358
buffer tsn: 4 rdba: 0x024014ed (9/5357)
scn: 0x0000.000ec539 seq: 0x02 flg: 0x00 tail: 0xc5390602
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data
```
pdf_1
三、Transaction Layer(事务层)
事务层分为**固定部分(KTBBH)**和**可变部分(KTBIT)**。 pdf_1
3.1 固定部分 KTBBH
包含以下关键信息:
| 字段 | 说明 |
|------|------|
| **Object type** | 对象类型(数据/索引等) |
| **Object id** | 段/对象 ID(`seg/obj`) |
| **Last cleanout SCN (csc)** | 上次块清理的 SCN |
| **ITL count (itc)** | ITL 条目数量 |
| **Flag** | `flg` 标志(如 O=在线,-=正常) |
| **typ** | 类型:1=DATA,2=INDEX |
| **fsl** | Free list 锁 |
| **fnx** | 下一个 DBA(Free List 链) |
| **ver** | 版本号 |
FLM 段示例:
```
Block header dump: 0x024014ed
Object id on Block? Y
seg/obj: 0x2e61 csc: 0x00.ec539 itc: 1 flg: - typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01
```
pdf_1
ASSM 段示例:
```
Block header dump: 0x01c00025
Object id on Block? Y
seg/obj: 0x1544 csc: 0x00.180e8 itc: 3 flg: - typ: 1 - DATA
brn: 0 bdba: 0x1c00021 ver: 0x01
inc: 0 unused: 0
```
pdf_1
3.2 可变部分 --- ITL(Interested Transaction List)
ITL 是事务层的可变部分,每个 ITL 条目对应一个对块中数据有修改兴趣的事务。 pdf_1
**默认数量**:
-
表(TABLE):1 个 ITL
-
索引(INDEX):2 个 ITL
-
可通过 `INITRANS` 参数调整上限
**ITL 条目结构**:
```
Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0005.000.00000805 uba: 0x00c02619.0304.01 ---- 1 fsc 0x0000.00000000
```
| ITL 字段 | 大小 | 说明 |
|----------|------|------|
| **XID** | 8 bytes | 事务 ID,由 (usn, slot, seq) 三部分组成 |
| **UBA** | 8 bytes | 撤销块地址(Undo Block Address),指向该事务的 undo 记录 |
| **Flag** | 1 byte | 事务标志位(见下方 Flag 定义) |
| **Lck** | 1 byte | 行锁计数(该事务在此块中锁定的行数) |
| **Scn/Fsc** | 8 bytes | 提交 SCN 或快速提交的剩余空间字节数 |
**ITL Flag 标志位含义**:
| 标志 | 值 | 含义 |
|------|-----|------|
| `C` | --- | Committed(已提交) |
| `U` | --- | Upper bound(上界提交,需要确认) |
| `-` | --- | 普通活动事务 |
| `----` | --- | 全部为 `-` 表示活动事务,无特殊标志 |
示例:
```
Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0xffff.000.00000000 uba: 0x00000000.0000.00 C--- 0 scn 0x0000.000180e8
0x02 xid: 0x0004.014.00000037 uba: 0x008009a6.0015.08 --U- 1 fsc 0x0075.0003ce71
```
-
`C---`:已提交事务,Lck=0
-
`--U-`:上界事务(slot 可能已被重用),Lck=1 pdf_1pdf_5
3.3 KTBBH + KTBIT 在 $type_size 中的定义
```
|COMPONENT|TYPE |DESCRIPTION |TYPE_SIZE|
|---------|-------|------------------|---------|
|KCB |KCBH |BLOCK COMMON | 20 |
|KTB |KTBIT |TRANSACTION VARIABLE| (可变) |
|KTB |KTBBH |TRANSACTION FIXED | (固定) |
```
pdf_1
四、Data Layer(数据层)--- KDBH(14 bytes)
数据层由数据头结构 **KDBH** 和行数据组成。KDBH 固定 **14 字节**。 pdf_1
4.1 KDBH 字段
```
data_block_dump
===============
tsiz: 0x780
hsiz: 0x42
pbl: 0x024d019c
bdba: 0x01c00025
flag=-------------
ntab=1
nrow=24
frre=-1
fsbo=0x42
fseo=0x10d
avsp=0xcb
tosp=0xcb
0xe:pti0 nrow=24 offs=0
0x12:pri0 offs=0x742
0x14:pri1 offs=0x6fe
...
```
| 字段 | 大小 | 说明 |
|------|------|------|
| **tsiz** | 2 | Total data area size(总数据区大小) |
| **hsiz** | 2 | Data header size(数据头大小) |
| **pbl** | 4 | Pointer to buffer holding the block(缓冲区指针) |
| **bdba** | 4 | Block relative data block address(RDBA) |
| **flag** | 1 | 标志位:`N`=pctfree 命中(clusters);`F`=不放 free list;`K`=可刷新 cluster key |
| **ntab** | 1 | 表数量(>1 表示簇表) |
| **nrow** | 2 | 行数量 |
| **frre** | 2 | First free row index entry(-1=需要新增) |
| **fsbo** | 2 | Free space begin offset(空闲空间起始偏移) |
| **fseo** | 2 | Free space end offset(空闲空间结束偏移) |
| **avsp** | 2 | Available space in the block(当前可用空间) |
| **tosp** | 2 | Total available space when all TXs commit(全部提交后可用空间) |
**KDBH 总大小:14 字节** pdf_1pdf_2
4.2 Table Directory(表目录)--- pti\[\]
```
0xe:pti0 nrow=24 offs=0
```
-
`pti0`:第一个表(或唯一表)的目录条目
-
`nrow`:该表的行数
-
`offs`:行的起始偏移位置
对于非簇表,只有一个表目录条目。簇表可以有多个。 pdf_1
4.3 Row Directory(行目录)--- pri\[\]
```
0x12:pri0 offs=0x742
0x14:pri1 offs=0x6fe
0x16:pri2 offs=0x49e
0x18:pri3 offs=0x494
```
-
每个条目 **2 字节**,存储行数据的偏移量
-
通过 ROWID 中的 slot 号可以直接索引到对应行偏移 pdf_1
五、Row Format(行格式)
5.1 行数据整体结构
行数据以行片段(row piece)形式存储,基本结构如下:
```
┌──────────────────────────────────────────────┐
│ Row Flag (1 byte) --- 行标志位 │
├──────────────────────────────────────────────┤
│ Lock Byte (1 byte) --- 锁字节(ITL#) │
├──────────────────────────────────────────────┤
│ # of Columns (1 byte) --- 该片段中的列数 │
├──────────────────────────────────────────────┤
│ Cluster Key Idx (1 byte) --- 簇键索引(可选) │
├──────────────────────────────────────────────┤
│ Column Length (1 or 3 bytes) --- 列长度 │
├──────────────────────────────────────────────┤
│ Column Data --- 列数据 │
├──────────────────────────────────────────────┤
│ ...(更多列,重复 长度+数据 结构) │
└──────────────────────────────────────────────┘
```
pdf_1
5.2 Row Flag(行标志位)
| 位 | 名称 | 含义 |
|-----|------|------|
| 0x01 | --- | 行片段是最后一个片段(last piece) |
| 0x02 | --- | 行片段是第一个片段(first piece) |
| 0x04 | --- | 行片段是中间的片段(middle piece) |
| 0x08 | --- | 行片段被迁移(migrated) |
| 0x10 | **DELETED** | 行已被删除 |
| 0x20 | --- | 行片段包含分支信息(branch) |
在 dump 中,行标志表现为如下格式:
```
fb: -----------PN lb: 0x1 cc: 1
```
其中 `fb` 字段的每个字符代表一个标志位。 pdf_1
5.3 行删除时的 Stub Space
删除行时,仅保留前 2 字节作为 stub:
-
**第 1 字节**:Flag 字段,设置 DELETED 位(0x10)
-
**第 2 字节**:Lock 字段,保留行锁(ITL#)
这样做可以释放行的其余空间,但保留锁信息。 pdf_5pdf_6
5.4 列数据编码示例
```
col 0; len 2; (2): c1 05
col 1; len 10; (10): 00 00 32 2c 00 41 0e 89 00 01
```
-
`col N`:第 N 列(从 0 开始)
-
`len M`:数据长度 M 字节
-
`(M)`:括号内为十六进制原始数据
5.5 特殊值编码
| 值 | 含义 |
|-----|------|
| `0xFE` + 2 bytes length | 长度字段,表示列的实际长度 |
| `0xFF` | NULL 值 |
pdf_1
六、Block Footer / Tail(块尾,4 bytes)
块尾(Block Tail/Footer)是数据块的最后 **4 个字节**,用于一致性校验,检测块是否断裂(fractured)。 pdf_1pdf_2
6.1 Tail 结构
```
Tail = 低 2 字节的 SCN Base + Block Type + SCN Seq
```
**4 字节布局**:
| 偏移 | 大小 | 内容 | 说明 |
|------|------|------|------|
| 0 | 2 | SCN Base 低 2 字节 | SCN 基值的低 16 位 |
| 2 | 1 | Block Type | 块类型(与 type_kcbh 一致) |
| 3 | 1 | SCN Seq | 序列号(与 seq_kcbh 一致) |
6.2 校验原理
Cache Layer 中的 `bas_kcbh`(低 2 字节)、`type_kcbh`、`seq_kcbh` 与 Tail 中的对应值必须一致。如果块头与尾部不匹配,则说明块可能已损坏或断裂。 pdf_1pdf_2
6.3 典型实例
```
scn: 0x0000.000ec539 seq: 0x02 flg: 0x00 tail: 0xc5390602
```
解析 tail `0xc5390602`:
-
`c539` = SCN Base 低 2 字节(0x000ec539 的低 16 位 = 0xc539)
-
`06` = Block type(trans data)
-
`02` = SCN Seq
6.4 损坏块检测
```
Corrupt block relative dba: 0x01c0003a file=7. blocknum=58.
Fractured block found during buffer read
Data in bad block - type:6. format:2. rdba:0x01c0003a
last change scn:0x0000.0000e9c5 seq:0xa0 flg:0x00
consistancy value in tail 0x44c506a0
check value in block header:0x0, check value not calculated
```
pdf_8
七、块类型分类(Block Classes)
Oracle 中有十类块,由不同的 PCM 锁保护: pdf_4
| Class | 描述 |
|-------|------|
| 1 | DATA(数据块) |
| 2 | SORT(排序块,不受 PCM 锁保护,实例私有) |
| 3~10 | 其他类型(索引、段头、撤销段等) |
八、块异常恢复与校验
8.1 块完整性检查要点
在代码开发中,解析和恢复 Oracle Block 时,应依次检查以下字段:
检查 1:Tail 一致性
```
条件:tail 的低 2 字节 == SCN Base 低 2 字节
tail 的第 3 字节 == type_kcbh
tail 的第 4 字节 == seq_kcbh
```
不匹配 → 块可能断裂(fractured)。
检查 2:SEQ 损坏标记
```
条件:seq_kcbh == 0xFF
```
→ 块被标记为损坏(corrupt)。 pdf_2
检查 3:Chkval 校验
```
条件:DB_BLOCK_CHECKSUM=TRUE 时,chkval 必须等于重新计算的块校验和
```
不匹配 → 块内容可能已损坏。 pdf_2
检查 4:块格式版本
```
条件:frmt_kcbh == 2(Oracle8+)
```
不匹配 → 可能是旧版本或无效块。
检查 5:Block Type 合法性
```
条件:type_kcbh 必须是已知的合法块类型
```
检查 6:RDBA 一致性
```
条件:bdba(数据层中的 RDBA)== rdba_kcbh(缓存层中的 RDBA)
```
8.2 块损坏类型
| 类型 | 说明 |
|------|------|
| **介质损坏** | 物理块损坏,无法读取 |
| **逻辑损坏** | 块可读但内容不一致 |
| **断裂块** | Tail 校验失败,块未完整写入 |
| **热块** | 多次变更导致 SEQ 溢出 |
8.3 块介质恢复(Block Media Recovery --- BMR)
Oracle9i 引入的块级介质恢复,通过 RMAN 执行:
```
RMAN> BLOCKRECOVER DATAFILE 5 BLOCK 1713;
```
特点:
-
块是最小的介质恢复单元
-
恢复时数据文件保持在线
-
仅支持完整恢复(complete recovery)
-
RMAN 从备份中恢复指定块,然后应用 redo 推进到当前 SCN pdf_2pdf_3
8.4 诊断工具
| 工具 | 用途 |
|------|------|
| **DBVERIFY** | 检查数据文件块的物理完整性 |
| **ANALYZE TABLE** | 验证表/索引的逻辑一致性 |
| **DBMS_REPAIR** | 标记和修复损坏块 |
| **BBED** | 底层块编辑工具(Oracle 内部) |
| **ORAPATCH** | 修复特定损坏问题 |
pdf_2
九、Block 异常恢复场景代码解析指南
9.1 解析 Block 的基本流程
```
-
定位块起始位置(文件偏移 = 文件头 + block_size × block_number)
-
读取前 20 字节 → 解析 KCBH(Cache Layer)
-
验证 Tail(块尾 4 字节)
-
解析 KTBBH(事务层固定部分)
-
读取 ITL count(itc)→ 解析每个 ITL 条目
-
解析 KDBH(数据层头,14 字节)
-
解析 Table Directory → Row Directory
-
按行偏移读取行数据,解析行格式
```
9.2 块恢复的关键判断逻辑
```c
// 伪代码 --- Block 完整性检查
int check_block_integrity(unsigned char *block) {
kcbh = parse_kcbh(block);
tail = parse_tail(block + block_size - 4);
// 1. SEQ 损坏标记
if (kcbh.seq == 0xFF) return BLOCK_CORRUPT_MARKED;
// 2. Tail 校验
if (tail.scn_low != (kcbh.scn_base & 0xFFFF)) return BLOCK_FRACTURED;
if (tail.type != kcbh.type) return BLOCK_FRACTURED;
if (tail.seq != kcbh.seq) return BLOCK_FRACTURED;
// 3. Checksum 校验(如果启用)
if (kcbh.chkval != 0) {
calc = compute_checksum(block, block_size);
if (calc != kcbh.chkval) return BLOCK_CHECKSUM_ERROR;
}
// 4. RDBA 一致性
kdbh = parse_kdbh(block + KCBH_SIZE + KTBBH_SIZE + itl_size);
if (kdbh.bdba != kcbh.rdba) return BLOCK_RDBA_MISMATCH;
return BLOCK_OK;
}
```
9.3 ITL 事务状态解析逻辑
```c
// 伪代码 --- ITL 事务状态判断
enum tx_state {
TX_ACTIVE, // 活动事务
TX_COMMITTED, // 明确提交
TX_UPPER_BOUND, // 上界提交(需要进一步确认)
};
tx_state parse_itl_state(itl_entry *itl) {
if (itl->flag & FLAG_COMMITTED) return TX_COMMITTED;
if (itl->flag & FLAG_UPPER_BOUND) return TX_UPPER_BOUND;
return TX_ACTIVE;
}
```
9.4 行数据解析逻辑
```c
// 伪代码 --- 解析行片段
typedef struct {
uint8_t flag; // 行标志
uint8_t lock; // 锁字节(ITL 索引)
uint8_t cc; // 列数
uint8_t cki; // 簇键索引(可选)
// 列数据...
} row_piece;
row_piece parse_row(unsigned char *data, int offset) {
row_piece row;
row.flag = dataoffset;
row.lock = dataoffset + 1;
row.cc = dataoffset + 2;
// 簇键索引仅在簇表中存在
// 后续为列长度 + 列数据的重复序列
return row;
}
```
十、关键要点总结
| 组件 | 大小 | C 结构 | 作用 |
|------|------|--------|------|
| **KCBH(Cache Layer)** | 20 字节 | `kcbh` | 块类型、格式、SCN、地址、校验 |
| **KTBBH(Tx Fixed)** | 可变 | --- | 对象 ID、清理 SCN、ITL 计数 |
| **KTBIT(ITL 条目)** | 每个约 24 字节 | --- | 事务 ID、Undo 地址、事务状态 |
| **KDBH(Data Layer)** | 14 字节 | --- | 行数、空闲空间偏移、可用空间 |
| **Table Directory** | 每表 4 字节 | --- | 表索引,指向行目录 |
| **Row Directory** | 每行 2 字节 | --- | 行偏移数组 |
| **Block Footer/Tail** | 4 字节 | --- | SCN 低 2 字节 + 类型 + 序列号 |
| **Row Header** | 3+ 字节 | --- | 标志、锁、列数 |
参考文档
-
DSI401: Dumps, Crashes, and Corruptions --- Block dump 分析、损坏检测、BMR pdf_2
-
DSI402e: Data Types and Block Structure --- 块结构、行格式、事务层、ITL pdf_1
-
DSI402: Space and Transaction Management --- 段结构、ASSM/FLM、事务管理 pdf_5pdf_6
-
DSI403e: Recovery Architecture Components --- 恢复架构、BMR pdf_3
-
DSI408: Real Application Clusters Internals --- 块分类与 PCM 锁 pdf_4