Oracle Data Block 详细结构解析

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 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 的基本流程

```

  1. 定位块起始位置(文件偏移 = 文件头 + block_size × block_number)

  2. 读取前 20 字节 → 解析 KCBH(Cache Layer)

  3. 验证 Tail(块尾 4 字节)

  4. 解析 KTBBH(事务层固定部分)

  5. 读取 ITL count(itc)→ 解析每个 ITL 条目

  6. 解析 KDBH(数据层头,14 字节)

  7. 解析 Table Directory → Row Directory

  8. 按行偏移读取行数据,解析行格式

```

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

相关推荐
Irene19911 小时前
当前用户查OWNER报错总结:在Oracle中,查询OWNER(或用户名/SCHEMA)报错,本质是权限不足,但错误的表现形式会根据具体场景而不同
oracle
Code额3 小时前
Python asyncio 异步编程全套学习文档(零基础完整版)
python·学习·oracle·async·异步·asyncio
秋田君5 小时前
【无标题】
数据库·oracle
JacksonMx6 小时前
企业级幂等方案
数据库·spring boot·spring·oracle
Irene19916 小时前
生产级探查脚本:数据探查 SQL 示例(无需DBA权限、跨Oracle版本(11g/19c)的表结构探查脚本方案)
oracle
—Miss. Z—16 小时前
备份与恢复
数据库·oracle
这个DBA有点耶20 小时前
Oracle 迁移金仓兼容性评估指南:5 大维度深度拆解
数据库·oracle·架构
Suhan421 天前
SQLAlchemy的两种查询query()查询、stmt=select()查询
数据库·python·sql·oracle
—Miss. Z—1 天前
第11章 故障管理
网络·数据库·oracle