Oracle 表、块、行与 rowid

Oracle 表、块、行与 rowid:一次从堆组织到直接路径读取的漫游


  1. 堆表:最熟悉的陌生人

  • 堆(Heap) 的字面意思是“随便放”。 Oracle 把新来的行扔进第一个能塞下它的块,不维护任何插入顺序
  • 带来的后果:
    • SELECT * FROM t 出来的顺序可能和插入顺序完全不同。
    • 大量并发插入时,ASSM(Automatic Segment Space Management) 会把插入打散到不同块,避免“热点块”。

  1. 块、区、段:一行数据的物理“户籍”

  • 块(Block):最小 I/O 单元,常见 8 KB。
  • 区(Extent):逻辑上连续的块集合,磁盘上不一定连续。
  • 段(Segment):一个对象(表、索引、LOB…)占用的所有区的总和。

示意图(逻辑 → 物理):

Segment
├── Extent #1 (块 1000-1007)
├── Extent #2 (块 3000-3015)
└── ...

  1. rowid:10 字节的 GPS 坐标

rowid 格式(以 AAAPecAAFAAAABSAAA 为例):

数据对象号 AAAPec
文件号     AAF
块号       AAAABS
行号       AAA   ← 行目录条目索引
  • 索引叶块里存的就是 (索引键, rowid),因此索引读可以做到单块 I/O 直接定位。
  • 行在块内移动时,只改行目录指针,rowid 不变(行链接)。

  1. 行迁移 vs. 行链接:什么时候多走一段路

场景定义对索引读对全表扫描
行迁移 Row Migration行太长或更新后变大,原块放不下,整行搬到新块,原块留一个“转发地址”。先到原块,再按转发地址到新块,额外一次逻辑/物理 I/O全表扫描本来就把段里所有块都扫一遍,转发地址被忽略,无额外代价
行链接 Row Chaining行本身比块还大,必须拆成多块存储。rowid 不变。读整行时需要把多块拼起来;如果只查的列都在第一块,则不受影响。同索引读。

一句话记忆: 迁移 = 搬家后留纸条;链接 = 一家人分房睡。


  1. 高水位与低高水位:全表扫描的边界

  • 高水位(HWM):段中已格式化/使用过的最末块。
  • 低高水位(Low HWM):已知全部格式化的最末块。

全表扫描流程:

  1. 从段头拿到 Low HWM,顺序读所有 ≤ Low HWM 的块(肯定格式化过)。
  2. 读位图,挑出 Low HWM ~ HWM 之间已格式化的块。
  3. 跳过 >HWM 的块,它们尚未格式化,读出来也没用。

  1. 直接路径读取:绕过 SGA 的“绿色通道”

  • 常规路径:块 → SGA Buffer Cache → PGA。
  • 直接路径读取(Direct Path Read):磁盘 → PGA,完全跳过 Buffer Cache

触发场景:

  • 并行查询
  • CTAS / ALTER INDEX REBUILD / ALTER TABLE MOVE
  • LOB 读取、临时表空间扫描

优势:

  • 减少 Buffer Cache 的闩竞争。
  • 支持“多块读”(Multi-block I/O),一次性把大量块读进 PGA。

注意区分:

  • 多块读是一个进程一次 I/O 读多个块。
  • 并行读是多个进程各读各的块,每个进程可以单块或多块。

把 Oracle 存储层拆开来看,其实就是一部“地址学”:

  • 块号告诉你住哪条街;
  • rowid 告诉你门牌号;
  • 行迁移/链接告诉你可能要多拐几个弯;
  • HWM 告诉你别去空街区;
  • 直接路径读取则像一条 VIP 通道,让大部队绕过市中心直达目的地。

理解这些概念后,再面对执行计划里的 TABLE ACCESS FULL, TABLE ACCESS BY INDEX ROWID, TABLE FETCH CONTINUED ROW 或等待事件里的 direct path read,就不会只看到冷冰冰的单词,而能脑补出行在磁盘上“真实行走”的路线。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部