Skip to content

Oracle 核心全过程原理 ​

Oracle 不能只背“实例、SGA、PGA、Undo、Redo、SCN”。这些词如果不串到一条 SQL 的执行过程里,遇到慢 SQL、锁等待、归档日志暴涨、snapshot too old、执行计划变化时还是会懵。

这一页按商业项目视角,把 Oracle 的核心链路讲清楚:

  1. 客户端连接进来后由谁处理。
  2. 一条 select 怎么解析、优化、读块、做一致性读。
  3. 一条 update 怎么产生 Undo、Redo、锁和 SCN。
  4. B-tree 索引为什么通过 ROWID 回表。
  5. Shared Pool、Buffer Cache、Redo Log Buffer、PGA 分别在什么时候参与。
  6. 提交时为什么不是直接把所有数据块写到磁盘。
  7. 慢 SQL 和锁等待怎么按层排查。

学习目标 ​

问题学完后要能说清
实例和数据库区别实例是内存和进程,数据库是物理文件
SGA/PGA 怎么参与 SQLSGA 共享缓存 SQL、数据块和 redo,PGA 保存会话私有排序 Hash 等
select 怎么执行解析、优化、执行、读块、必要时一致性读
update 怎么执行找行、加锁、写 Undo、改 Buffer Cache、写 Redo、提交
Undo 和 Redo 区别Undo 保存旧值用于回滚和一致性读,Redo 记录变化用于恢复
SCN 解决什么标识一致性时间点、提交顺序和恢复位置
B-tree 索引怎么定位行root/branch/leaf 找 key,leaf 里拿 ROWID,再访问表块
性能怎么排查执行计划、等待事件、Buffer Gets、物理读、锁、统计信息

Oracle 总体结构 ​

Oracle 要先分清两个概念:实例和数据库。

mermaid
flowchart TD
    A["客户端会话"] --> B["Server Process"]
    B --> C["Oracle Instance"]
    C --> D["SGA 共享内存"]
    C --> E["后台进程"]
    D --> F["Shared Pool"]
    D --> G["Database Buffer Cache"]
    D --> H["Redo Log Buffer"]
    B --> I["PGA 私有内存"]
    E --> J["DBWR / LGWR / CKPT / SMON / PMON"]
    C --> K["Oracle Database"]
    K --> L["Data Files"]
    K --> M["Control Files"]
    K --> N["Redo Log Files"]
部分作用类比
Server Process处理用户会话 SQL专门服务员
SGA多会话共享内存公共工作区
Shared Pool缓存 SQL、执行计划、数据字典SQL 和元数据缓存
Buffer Cache缓存数据块和索引块数据页缓存
Redo Log Buffer暂存 redo 记录日志缓冲
PGA会话私有内存,排序、Hash、会话状态私人草稿纸
DBWR把脏块写回数据文件刷数据块
LGWR把 redo 写到 redo log 文件刷日志
CKPT协调检查点标记恢复位置

一条 SELECT 的全过程 ​

示例:

sql
select id, asset_no
from asset
where owner_id = 1001
order by created_at desc
fetch first 20 rows only;

执行流程:

mermaid
flowchart TD
    A["客户端发送 SQL"] --> B["Server Process 接收"]
    B --> C["检查 Shared Pool 是否已有可复用游标"]
    C --> D{"能否软解析"}
    D -- "是" --> E["复用执行计划"]
    D -- "否" --> F["硬解析:语法、语义、权限检查"]
    F --> G["优化器生成执行计划"]
    G --> H["执行计划开始执行"]
    E --> H
    H --> I["通过索引或全表扫描读取块"]
    I --> J["Buffer Cache 命中则读内存"]
    I --> K["未命中则物理读数据文件"]
    J --> L["一致性读检查"]
    K --> L
    L --> M["PGA 中排序或取 TopN"]
    M --> N["返回结果"]

软解析和硬解析 ​

解析方式做什么性能影响
软解析SQL 和执行计划可复用成本较低
硬解析语法语义检查、权限检查、优化器生成计划成本高,消耗 CPU 和 Shared Pool

为什么商业项目强调绑定变量:

sql
-- 不推荐:每个值都是不同 SQL 文本,容易硬解析多
select * from asset where owner_id = 1001;
select * from asset where owner_id = 1002;

-- 推荐:SQL 文本稳定,变量变化
select * from asset where owner_id = :owner_id;

如果高并发系统里大量拼接字面量 SQL,会造成硬解析压力、Shared Pool 抖动和 Library Cache 竞争。

Buffer Cache 怎么参与读取 ​

Oracle 的数据不是一行一行从磁盘读,而是以 block 为基本单位。

mermaid
flowchart TD
    A["执行器需要某个数据块"] --> B{"Buffer Cache 是否有该块"}
    B -- "有" --> C["逻辑读 buffer get"]
    B -- "没有" --> D["从数据文件物理读"]
    D --> E["放入 Buffer Cache"]
    E --> C
    C --> F["读取行数据"]
指标含义
Buffer Gets逻辑读,从 Buffer Cache 访问块的次数
Physical Reads物理读,从磁盘读取块
Consistent Gets一致性读,需要按查询 SCN 读取一致版本
Current Gets当前读,通常用于更新、锁定等需要当前版本的场景

SQL 慢不一定是物理读高。一个 SQL 即使都命中缓存,如果 Buffer Gets 特别高,也说明扫描块太多,CPU 和缓存压力仍然大。

B-tree 索引和 ROWID 回表 ​

Oracle B-tree 索引一般由 root block、branch block、leaf block 组成。leaf block 中保存 key 和 ROWID。

mermaid
flowchart TD
    A["Root Block"] --> B["Branch Block"]
    B --> C["Leaf Block"]
    C --> D["Key + ROWID"]
    D --> E["根据 ROWID 访问表数据块"]
    E --> F["读取完整行"]

ROWID 可以理解为 Oracle 定位一行物理位置的地址信息。索引查找分两步:

  1. 先在索引树里找到 key 对应的 ROWID。
  2. 再根据 ROWID 去表数据块读取完整行。

如果查询字段都在索引里,可能避免访问表块;如果需要其他列,就会通过 ROWID 回表。少量回表没问题,大量回表会造成大量随机访问。

一致性读和 SCN ​

Oracle 的一致性读依赖 SCN 和 Undo。

SCN 可以理解为 Oracle 内部的一致性时间点。查询开始时会确定一个查询 SCN,读取时要看到这个时间点已经提交的数据。

mermaid
flowchart TD
    A["select 开始"] --> B["确定查询 SCN"]
    B --> C["读取数据块"]
    C --> D{"块中行版本是否晚于查询 SCN"}
    D -- "否" --> E["直接返回当前块中的版本"]
    D -- "是" --> F["根据 Undo 构造旧版本"]
    F --> G["返回查询 SCN 可见版本"]

为什么需要 Undo:

场景Undo 的作用
事务回滚用旧值撤销修改
一致性读构造查询开始时可见的旧版本
闪回能力在一定范围内查询历史状态

如果 Undo 中需要的旧版本已经被覆盖,可能出现:

text
ORA-01555: snapshot too old

这通常和长查询、Undo 保留不足、更新量大有关。

一条 UPDATE 的全过程 ​

示例:

sql
update asset
set status = 'USED',
    updated_at = sysdate
where asset_no = 'A202607050001'
  and status = 'IDLE';

过程:

mermaid
flowchart TD
    A["定位目标行"] --> B["对目标行加锁"]
    B --> C["生成 Undo 旧值"]
    C --> D["在 Buffer Cache 修改数据块"]
    D --> E["生成 Redo 记录"]
    E --> F["Redo 进入 Redo Log Buffer"]
    F --> G["事务提交"]
    G --> H["LGWR 写 redo 到日志文件"]
    H --> I["返回提交成功"]
    I --> J["DBWR 后续写脏块到数据文件"]

每一步为什么需要:

步骤作用
定位目标行通过索引或扫描找到要改的数据
加锁防止其他事务同时修改同一行
生成 Undo支持回滚和一致性读
修改 Buffer Cache先改内存块,提高性能
生成 Redo记录修改,用于崩溃恢复
LGWR 写日志提交时保证 redo 持久化
DBWR 写脏块数据块可稍后刷盘,不阻塞每次提交

重点:提交成功不要求数据块马上写入数据文件,但要求相关 Redo 能恢复这次修改。

Undo 和 Redo 的区别 ​

对比UndoRedo
保存内容修改前的旧值或反向操作信息数据块变化记录
主要作用回滚、一致性读、闪回崩溃恢复、介质恢复
读场景查询旧版本时可能用恢复时重放
写场景DML 修改时产生几乎所有数据块变化都会产生
类比后悔药事故恢复录像

面试不要只说“Undo 回滚,Redo 恢复”。要补一句:

Oracle 一致性读也依赖 Undo。如果当前块中的版本太新,Oracle 会根据 Undo 构造查询 SCN 对应的旧版本。

Commit 到底做了什么 ​

很多人以为 commit 会把所有修改的数据块刷盘。实际更关键的是 redo。

mermaid
flowchart TD
    A["事务执行 DML"] --> B["修改 Buffer Cache 中的块"]
    B --> C["Redo 进入 Redo Log Buffer"]
    C --> D["commit"]
    D --> E["LGWR 写 redo 到 redo log file"]
    E --> F["生成提交 SCN"]
    F --> G["返回 commit 成功"]
    G --> H["DBWR 后台择机刷脏块"]

为什么这样设计:

如果 commit 刷所有数据块问题
大量随机 IO提交性能很差
同一块可能被反复刷浪费
高并发事务延迟高吞吐下降

Oracle 选择先保证 redo 落盘。宕机后如果数据块没刷,也可以通过 redo 重放恢复。

锁和阻塞怎么理解 ​

Oracle 更新一行会对行加锁。其他事务如果也要更新同一行,会等待。

mermaid
sequenceDiagram
    participant A as 事务A
    participant B as 事务B
    A->>A: update asset set status='USED' where id=1
    A->>A: 持有行锁,未提交
    B->>B: update same row
    B-->>A: 等待事务A提交或回滚
    A->>A: commit
    B->>B: 继续执行

常见阻塞原因:

原因后果
事务长时间不提交其他更新等待
更新条件没走索引扫描和锁范围变大
批量大事务Undo/Redo 压力大,锁持有久
应用异常未关闭事务会话挂住,锁一直不释放

执行计划怎么看 ​

Oracle 执行计划要关注访问路径、连接方式、估算行数和实际行数。

常见访问方式:

访问方式含义关注点
TABLE ACCESS FULL全表扫描大表高频 SQL 要警惕
INDEX RANGE SCAN索引范围扫描范围是否过大
INDEX UNIQUE SCAN唯一索引定位通常较好
TABLE ACCESS BY INDEX ROWID通过 ROWID 回表回表次数是否过多
NESTED LOOPS嵌套循环连接外层行数大时可能慢
HASH JOIN哈希连接PGA、临时空间可能压力大
SORT ORDER BY排序大排序可能用临时表空间

查看执行计划:

sql
explain plan for
select id, asset_no
from asset
where owner_id = 1001
order by created_at desc;

select *
from table(dbms_xplan.display);

如果要看实际执行情况,生产常结合 AWR、ASH、SQL Monitor 或 dbms_xplan.display_cursor。

商业场景:资产状态更新 ​

医疗资产平台中,资产状态从空闲改为使用中:

sql
update asset
set status = 'USED',
    used_by = :user_id,
    updated_at = sysdate
where asset_no = :asset_no
  and status = 'IDLE';

推荐索引:

sql
create unique index uk_asset_no on asset(asset_no);
create index idx_asset_owner_time on asset(owner_id, created_at);

这个 SQL 背后的过程:

  1. 通过 uk_asset_no 快速定位资产行。
  2. 检查 status = 'IDLE' 是否满足。
  3. 对该行加锁。
  4. 写 Undo 保存旧状态。
  5. 修改 Buffer Cache 中的数据块。
  6. 写 Redo 到 Redo Log Buffer。
  7. commit 时 LGWR 刷 redo。
  8. 返回成功。
  9. 后台 DBWR 后续刷脏块。

如果没有 asset_no 唯一索引,更新可能扫描更多块,持锁更久,Redo/Undo 更多,其他事务等待概率也更高。

线上排查流程 ​

慢 SQL ​

mermaid
flowchart TD
    A["SQL 慢"] --> B["看执行计划"]
    B --> C{"访问路径合理吗"}
    C -- "全表扫描大表" --> D["检查索引和谓词"]
    C -- "回表很多" --> E["考虑覆盖或减少返回列"]
    C -- "排序重" --> F["检查 order by 和 PGA/临时表空间"]
    C -- "Join 重" --> G["检查驱动表、统计信息、连接列索引"]
    D --> H["更新统计信息并验证计划"]

锁等待 ​

mermaid
flowchart TD
    A["接口卡住"] --> B["查等待事件"]
    B --> C{"是否 enq: TX row lock contention"}
    C -- "是" --> D["找阻塞会话"]
    D --> E["看未提交事务 SQL"]
    E --> F["提交、回滚或修复应用事务边界"]
    C -- "否" --> G["继续查 IO、CPU、临时空间、日志等待"]

Redo 或归档暴涨 ​

可能原因排查方向
批量更新/删除是否大事务,能否分批
索引过多DML 同时维护大量索引
大字段频繁更新是否拆表或减少更新
归档目的地慢检查磁盘和归档进程

常见坑 ​

坑后果正确做法
拼接字面量 SQL硬解析多,Shared Pool 压力大使用绑定变量
事务不及时提交锁等待、Undo 占用缩短事务
更新条件没索引扫描多、锁持有久高频更新条件建合适索引
只看是否有索引大量回表仍慢看执行计划和行数
误以为 commit 刷数据块无法理解 redo 恢复commit 关键是 redo 持久化
长查询加高频更新可能 snapshot too old调整 Undo、缩短查询、降低冲突
Bitmap 索引用在 OLTP 高频更新锁和维护成本高Bitmap 更适合低更新的数据仓库

面试标准回答 ​

Oracle 一条 SQL 怎么执行 ​

text
Oracle 客户端连接通常由 Server Process 处理。SQL 进入后会先在 Shared Pool 中查找是否可以复用游标和执行计划,能复用就是软解析,不能复用就要硬解析,包括语法、语义、权限检查和优化器生成执行计划。执行时会通过索引或全表扫描访问数据块,优先从 Database Buffer Cache 读取,未命中才物理读。排序、Hash 等会使用 PGA。查询还要根据 SCN 和 Undo 做一致性读。

Oracle update 和 commit 过程 ​

text
Oracle 更新一行时,会先定位目标行并加锁,然后生成 Undo 保存旧值,用于回滚和一致性读;再修改 Buffer Cache 中的数据块,同时生成 Redo 记录写入 Redo Log Buffer。提交时 LGWR 把相关 Redo 刷到 redo log 文件并生成提交 SCN,然后返回提交成功。数据块本身不一定在 commit 时立即写入数据文件,后续由 DBWR 后台刷盘。宕机后可以通过 Redo 恢复已提交修改。

Undo、Redo、SCN 的关系 ​

text
Undo 保存修改前的旧值,用于事务回滚和一致性读;Redo 记录数据块变化,用于崩溃恢复;SCN 是 Oracle 内部的一致性时间点,用来标识提交顺序、查询可见性和恢复位置。查询开始时基于某个 SCN 做一致性读,如果当前数据块中的版本太新,Oracle 可以利用 Undo 构造查询 SCN 对应的旧版本。

关联知识点 ​

本章小结 ​

Oracle 的核心链路可以概括为:Server Process 执行 SQL,Shared Pool 负责解析和计划复用,Buffer Cache 缓存数据块,PGA 支撑会话私有计算,Undo 支持回滚和一致性读,Redo 保证崩溃恢复,SCN 串起一致性时间点。真正理解这些过程后,再看执行计划、锁等待、归档日志、snapshot too old、硬解析和慢 SQL,就不再是背概念,而是能定位问题发生在哪一层。