Oracle 数据块读一致性判断流程(正确版)
假设:
Query SCN = 查询开始的 SCN(Query SCN)
lastSubbmit SCN = 行中最新的提交scn
Row SCN = 行最后修改的 SCN(存储在行头,通过 ITL 推导)
UBA = Undo Block Address(Undo 文件号、块号、槽号)
Record SCN = 某条 Undo Record 中的 SCN
行头要关注的字段
Lock Byte + UBA 是读一致性的核心:
Lock Byte → 找 ITL 槽 → 得到事务提交状态、提交 SCN
UBA → 找 Undo Record → 获取旧版本数据
步骤 1:块级快速判断
读取 块头:
如果 lastSubbmit SCN ≤ Query SCN 并且 块中所有事务槽(ITL entries):
都已提交
→ 整个块可直接使用,无需逐行回溯
否则 → 进入逐行判断
步骤 2:逐行判断
对块中的每一行(或通过索引定位到的行):
获取锁字节(Lock Byte) → 对应 ITL 槽号
如果无法定位到事物槽,直接走uba的undo record逻辑
获取该行的 UBA(行级 Undo 指针,指向该行上一次修改的 Undo Record)
如果事务已提交:
从 ITL 槽获得 Row SCN(提交 SCN)
如果 Row SCN ≤ Query SCN → 当前行可见,直接返回
如果 Row SCN > Query SCN → 需要回溯 Undo
如果事务未提交 → 必须回溯 Undo(查询要看到事务开始前的旧值)
步骤 3:回溯 Undo 链
用行头中的 UBA 定位到对应的 Undo Block
在 Undo Block 中找到目标 Undo Record:
获取该记录的 SCN(Record SCN)
如果 Record SCN ≤ Query SCN → 这是可见版本,使用该 Undo Record 的数据构造一致性读副本(CR Copy)
如果 Record SCN > Query SCN → 沿着该记录的 Prev UBA 指向的上一条 Undo Record 继续回溯
重复步骤 2 直到:
找到 Record SCN ≤ Query SCN 的版本 → 返回该版本
或 Undo 已被覆盖 → 报 ORA-01555 snapshot too old
整体逻辑图(简化)
读取块头
├── 如果 lastSubbmit SCN ≤ Query SCN 且 所有 ITL 已提交 且 提交 SCN ≤ Query SCN → 直接使用块数据
└── 否则 → 遍历每行:
├── 行头 → 锁字节 → ITL 槽
├── 判断事务提交状态和 Row SCN
├── 若需回溯 → 行头 UBA → Undo Block → Undo Record
├── 若 Undo Record.SCN ≤ Query SCN → 返回版本
└── 若 Undo Record.SCN > Query SCN → Prev UBA 回溯
通过实体中的uba 定位到undo块具体记录信息,在通过record 中的Prev UBA(指向上一条记录),
找到上1条undo record.每个record 中有scn记录,这里叫做Record SCN.如果Record SCN<=Query SCN.则取该undo record 记录.
下面是undo bock记录:
Undo Block
├─ Undo Block Header(元信息,段号、事务表、事务状态…)
├─ Undo Records[]
│ ├─ Record Header
│ │ ├─ UBA(本记录的地址:文件号+块号+记录号)
│ │ ├─ Prev UBA(指向上一条记录)
│ │ ├─ SCN(修改发生时的 SCN)
│ │ ├─ Object Number(表/索引对象 ID)
│ │ ├─ RowID(被修改行的物理位置)
│ └─ Old Column Values(修改前的列值)
└─ ...
重要实现是Prev UBA 构成undo record 链,可以回溯上一个版本.