Skip to content

Oracle 架构与存储

Oracle 里要先区分两个概念:实例 Instance数据库 Database

零基础最容易把 Oracle 理解成“一个数据库进程加一堆表”。这不够。Oracle 的核心模型是:

实例负责运行,数据库负责存储。实例包含内存和后台进程;数据库包含数据文件、控制文件、Redo 文件等物理文件。SQL 通过实例执行,数据最终保存在数据库文件里。

SQL 从连接到返回的总流程

mermaid
flowchart TD
    A["客户端连接"] --> B["Server Process"]
    B --> C["Shared Pool 查找或解析 SQL"]
    C --> D["优化器生成执行计划"]
    D --> E["Buffer Cache 读取数据块"]
    E --> F{"是否修改"}
    F -- "查询" --> G["基于 SCN 和 Undo 一致性读"]
    F -- "更新" --> H["生成 Undo 和 Redo"]
    H --> I["commit 时 LGWR 刷 Redo"]
    G --> J["返回结果"]
    I --> J

这条链路后面会反复出现:Shared Pool、Buffer Cache、PGA、Undo、Redo、SCN、LGWR、DBWR。

实例和数据库

mermaid
flowchart TD
    A["Oracle Instance"] --> B["SGA 共享内存"]
    A --> C["后台进程"]
    D["Oracle Database"] --> E["数据文件"]
    D --> F["控制文件"]
    D --> G["Redo Log 文件"]
    A --> D
概念说明
Instance内存结构 + 后台进程
Database数据文件、控制文件、redo log 文件等

实例启动后加载数据库,用户连接实例执行 SQL。

为什么要区分实例和数据库

问题如果不区分会怎样
数据在哪里以为数据在进程内存里,无法理解数据文件
宕机怎么恢复无法理解实例没了但文件还在,可以通过 redo 恢复
RAC 是什么无法理解多个实例访问同一个数据库
Data Guard 是什么无法理解主备通过 redo 保持数据库文件一致

实例是运行时能力,数据库是持久化文件。实例可以关闭再启动,数据库文件仍然存在。

常见后台进程

进程作用为什么重要
DBWn把脏数据块写回数据文件commit 不直接刷所有数据块
LGWR把 Redo Log Buffer 写入 redo log 文件commit 是否成功依赖 redo 持久化
CKPT触发检查点并更新控制文件/数据文件头缩短恢复时间
SMON系统监控,做实例恢复等宕机后恢复相关
PMON进程监控,清理异常会话资源会话异常退出后释放资源
ARCn归档进程,归档 redo log归档模式下支持恢复

记忆方式:DBWR 写数据块,LGWR 写日志,ARCn 管归档,SMON/PMON 做清理和恢复。

SGA 和 PGA

内存作用
SGA多个进程共享,包括 Database Buffer Cache、Shared Pool、Redo Log Buffer
PGA单个服务进程私有,用于排序、Hash、会话状态等
mermaid
flowchart TD
    A["SQL 执行"] --> B["Shared Pool 解析和缓存执行计划"]
    A --> C["Buffer Cache 读取数据块"]
    A --> D["PGA 排序 / Hash"]
    A --> E["Redo Log Buffer 记录变更"]

SGA 内部关键区域

区域做什么相关问题
Database Buffer Cache缓存数据块和索引块逻辑读、物理读、缓存命中
Shared Pool缓存 SQL、执行计划、数据字典信息软解析、硬解析、绑定变量
Redo Log Buffer暂存 redo 记录commit、LGWR、日志写等待
Large Pool大内存操作、备份恢复、并行等大任务资源

PGA 做什么

PGA 是单个服务进程私有内存,常用于:

  1. 排序。
  2. Hash Join。
  3. 会话变量和游标状态。
  4. 连接会话的私有工作区。

如果排序或 Hash 数据太大,PGA 放不下,就可能使用临时表空间,导致 SQL 明显变慢。

Shared Pool、软解析和硬解析

Oracle 执行 SQL 前要解析。相同 SQL 如果能复用已有游标和执行计划,就是软解析;如果不能复用,需要语法检查、语义检查、权限检查、优化器生成计划,就是硬解析。

mermaid
flowchart TD
    A["SQL进入 Oracle"] --> B{"Shared Pool 有可复用游标吗"}
    B -- "有" --> C["软解析"]
    B -- "没有" --> D["硬解析"]
    D --> E["语法、语义、权限检查"]
    E --> F["优化器生成执行计划"]
    C --> G["执行"]
    F --> G

为什么推荐绑定变量:

sql
select * from asset where asset_no = :assetNo;

如果应用每次拼接字面量:

sql
select * from asset where asset_no = 'A001';
select * from asset where asset_no = 'A002';

SQL 文本不同,游标复用率下降,Shared Pool 压力上升,高并发下可能出现大量硬解析。

表空间、段、区、块

mermaid
flowchart TD
    A["Tablespace 表空间"] --> B["Segment 段"]
    B --> C["Extent 区"]
    C --> D["Block 块"]
    D --> E["Row 行"]

Oracle 最小 IO 单位是块。理解块之后,才能理解索引扫描、全表扫描、Buffer Cache 和行迁移。

存储层级怎么理解

层级类比说明
Tablespace仓库区域逻辑空间,包含一个或多个数据文件
Segment一类对象的存储表段、索引段、Undo 段
Extent一批连续块空间分配单位
Block货架格子Oracle 读写的基本数据块
Row具体货物表中的一行数据

查询一行数据,底层不是“只读一行”,而是读取它所在的数据块。一个块里可能有多行。

ROWID 是什么

ROWID 可以理解为 Oracle 定位一行物理位置的地址信息。普通 B-tree 索引叶子块保存 key 和 ROWID。查询通过索引找到 ROWID 后,再根据 ROWID 回表读取数据块。

mermaid
flowchart TD
    A["索引 key: asset_no"] --> B["找到 ROWID"]
    B --> C["根据 ROWID 定位数据块和行"]
    C --> D["读取表数据"]

这解释了为什么返回行数少时索引快,返回行数非常多时,索引 + 大量 ROWID 回表可能不如全表扫描。

Redo 和 Undo

机制作用
Redo记录数据块变化,用于崩溃恢复
Undo保存旧值,用于回滚和一致性读
mermaid
flowchart TD
    A["update 一行"] --> B["生成 Undo 旧值"]
    A --> C["生成 Redo 变更记录"]
    B --> D["支持回滚 / 一致性读"]
    C --> E["支持崩溃恢复"]

Undo 怎么支持一致性读

Oracle 查询开始时会确定一个一致性读 SCN。若查询过程中遇到的数据块版本比这个 SCN 更新,就需要通过 Undo 找旧值,构造出查询开始时能看到的数据版本。

mermaid
flowchart TD
    A["查询开始,获得 SCN=100"] --> B["读取数据块"]
    B --> C{"数据块版本是否晚于100"}
    C -- "否" --> D["直接读取"]
    C -- "是" --> E["沿 Undo 找旧值"]
    E --> F["构造 SCN=100 时的版本"]

这就是为什么 Oracle 普通查询通常不会被正在更新的事务阻塞,因为查询可以读一致性旧版本。

Redo 怎么支持崩溃恢复

更新时数据块可能只在 Buffer Cache 中修改,还没写回数据文件。只要 commit 时 Redo 已经落盘,宕机后 Oracle 就可以根据 Redo 把已提交修改重做出来。

如果没有 Redo,数据块没来得及写盘就宕机,已提交事务就可能丢失。

SCN

SCN 是 System Change Number,可以理解为 Oracle 内部的一致性时间点。Oracle 用 SCN 判断数据版本、恢复位置和一致性读。

SCN 出现在很多地方:

  1. 查询一致性读。
  2. 事务提交。
  3. 数据文件头。
  4. 控制文件。
  5. 归档日志和恢复。
  6. Data Guard 主备同步。

可以把 SCN 理解成数据库内部的“逻辑时间戳”。它不是用户时间,但用来判断数据版本先后。

Commit 到底做了什么

mermaid
flowchart TD
    A["业务执行 update"] --> B["生成 Undo 旧值"]
    B --> C["修改 Buffer Cache 数据块"]
    C --> D["生成 Redo 到 Redo Log Buffer"]
    D --> E["commit"]
    E --> F["LGWR 刷 Redo 到 redo log 文件"]
    F --> G["生成提交 SCN"]
    G --> H["返回提交成功"]
    H --> I["DBWR 后台写数据块"]

重点:

  1. commit 必须保证 Redo 持久化。
  2. commit 不要求数据块马上写到数据文件。
  3. 数据块晚点刷盘没关系,因为 Redo 可以恢复。
  4. 如果 LGWR 慢,commit 会慢。

Checkpoint 是什么

Checkpoint 会推动脏块写盘,并记录数据库恢复到哪里。它的作用不是“提交事务”,而是控制恢复成本。

mermaid
flowchart TD
    A["Buffer Cache 中有脏块"] --> B["Checkpoint 触发"]
    B --> C["DBWR 写部分脏块"]
    C --> D["更新数据文件头和控制文件信息"]
    D --> E["缩短实例恢复需要重做的日志范围"]

Checkpoint 越频繁,恢复时可能更快,但运行时写盘压力可能更高;过少则恢复时间可能变长。

商业场景:为什么查询不阻塞更新

场景:资产采集任务正在更新设备状态,报表用户同时查询当天资产列表。

在很多数据库里,读写阻塞是高频问题。Oracle 的一致性读让普通查询可以基于查询开始时的 SCN 读取一致版本;如果某行已被更新但未提交,查询可以通过 Undo 看到旧版本。

这带来两个结果:

  1. 报表查询通常不会等更新事务提交。
  2. 长查询需要 Undo 保留足够久,否则可能出现 ORA-01555 snapshot too old

所以长报表、大事务、Undo 保留时间是 Oracle 生产排查重点。

常见坑

后果正确理解
分不清实例和数据库无法理解 RAC、恢复、文件结构实例运行,数据库存储
以为 commit 刷数据块无法解释 Redo 和 DBWRcommit 主要刷 Redo
不用绑定变量硬解析多,Shared Pool 压力大高并发用绑定变量
忽略 Undo长查询报快照过旧Undo 支持回滚和一致性读
只看 SQL 不看等待排查方向错误结合执行计划和等待事件

面试标准回答

text
Oracle 架构要区分实例和数据库。实例由 SGA 共享内存和后台进程组成,数据库由数据文件、控制文件和 redo log 文件等组成。SGA 中有 Database Buffer Cache、Shared Pool、Redo Log Buffer,PGA 是进程私有内存,常用于排序、Hash 和会话工作区。Oracle 存储结构是表空间、段、区、块、行,块是重要的 IO 单位。查询通过 Shared Pool 解析和复用执行计划,通过 Buffer Cache 读取数据块;更新会产生 Undo 和 Redo。Undo 用于回滚和一致性读,Redo 用于崩溃恢复,SCN 用于标识数据库一致性时间点。commit 时 LGWR 刷 Redo 并生成提交 SCN,数据块可以由 DBWR 后台稍后写回数据文件。