WR Blog 加载中...
返回文章
数据库Oracle架构后端

Oracle 数据库架构:Instance、SGA、PGA、数据文件与后台进程

Oracle 数据库架构:Instance、SGA、PGA、数据文件与后台进程封面

数据库基础体系 · 第 26/139 篇。文章以各产品官方稳定版本的公开语义为准;示例会明确引擎、事务与部署边界。

Oracle 数据库架构:Instance、SGA、PGA、数据文件与后台进程

Oracle 的“数据库架构”容易被简化成一张组件图:Instance 包含 SGA 和后台进程,Database 包含数据文件。但真正理解 Oracle,需要继续回答几个问题:

  • SQL 到底由谁执行,执行过程中数据放在哪里?
  • 一个事务修改的数据何时写入数据文件?
  • COMMIT 为什么通常不等于立即写数据文件?
  • 会话、服务器进程、PGA 和 SGA 是什么关系?
  • 实例崩溃后,Oracle 如何利用 Redo 和 Undo 恢复?
  • 单实例、RAC、CDB/PDB 的边界分别在哪里?

本文以 Oracle Database 的通用架构语义为基础,示例使用常见的单实例、专用服务器连接和 SQL*Plus/SQLcl 风格语法。具体后台进程名称、内存组件和可配置参数会随版本、平台、部署模式和启用的功能变化。


一、先区分 Oracle Database 与 Oracle Instance

1. Database:持久化的数据集合

在 Oracle 语境中,database 是存储在磁盘上的一组物理文件,主要包括:

  • 数据文件(data files)
  • 控制文件(control files)
  • 在线重做日志文件(online redo log files)

此外还可能有:

  • 临时文件(tempfiles)
  • 归档重做日志(archived redo logs)
  • 参数文件、密码文件
  • Flashback、备份和恢复相关文件

其中,控制文件和在线重做日志从恢复角度看非常关键,但它们不是普通表数据所在的数据文件。

数据文件中保存的是表、索引、Undo 段以及数据字典等数据库对象对应的块。控制文件记录数据库物理结构和恢复所需的元数据,例如数据库名称、数据文件和日志文件信息、检查点相关信息等。在线重做日志保存数据库变更产生的 Redo。

2. Instance:访问 Database 的运行环境

instance 是一组运行时内存结构和后台进程:

Instance=SGA+Oracle processes\text{Instance} = \text{SGA} + \text{Oracle processes}

在单实例部署中,通常一个 Instance 打开一个 Database。Instance 停止后,Database 的文件仍然存在,但没有运行中的 Oracle 进程访问它。

因此:

  • Database 是持久化文件集合;
  • Instance 是运行中的内存和进程集合;
  • 数据库文件可以在实例停止后继续存在;
  • 实例不能脱离数据库文件完成正常的数据访问。

在 Oracle RAC 中,多个 Instance 可以同时访问同一个 Database:

One DatabaseMultiple Instances\text{One Database} \leftarrow \text{Multiple Instances}

这些实例通常位于不同节点,共享数据库文件,但各自拥有本地 SGA 和本地后台进程。实例之间通过集群互联协调缓存块、锁和全局资源。


二、数据库启动过程:从 NOMOUNT 到 OPEN

理解 Instance 与 Database 的边界,最直观的方法是看启动状态。

1. NOMOUNT:只启动 Instance

执行:

STARTUP NOMOUNT;

Oracle 会:

  1. 读取初始化参数文件或服务器参数文件;
  2. 分配 SGA;
  3. 启动后台进程;
  4. 进入 NOMOUNT 状态。

此时 Instance 已经存在,但还没有读取控制文件。因此不能正常访问数据库中的数据文件、表和用户对象。

NOMOUNT 常用于创建数据库、重建控制文件等操作。

2. MOUNT:Instance 读取控制文件

执行:

ALTER DATABASE MOUNT;

Oracle 会打开并读取控制文件,确认数据库的物理结构,包括:

  • 数据文件列表;
  • 在线重做日志组和成员;
  • 数据库名称和相关标识;
  • 检查点及恢复所需信息。

此时数据库已挂载,但数据文件尚未对普通用户开放。

MOUNT 状态可以执行部分恢复操作,例如介质恢复前的准备。

3. OPEN:打开数据文件并提供服务

执行:

ALTER DATABASE OPEN;

Oracle 会根据控制文件检查和打开数据文件、在线重做日志等资源,并使数据库进入可访问状态。

因此状态之间的关系是:

NOMOUNT
  └─ Instance 已启动,尚未读取控制文件
       ↓ MOUNT
MOUNT
  └─ 控制文件已读取,数据库已挂载,数据文件尚未开放
       ↓ OPEN
OPEN
  └─ 数据库可正常提供 SQL 服务

如果上一次实例异常终止,Oracle 可能需要在 OPEN 前或 OPEN 过程中执行实例恢复。实例恢复主要使用在线重做日志把已提交或需要重放的变更重新应用,再利用 Undo 回滚未提交事务。


三、SGA:实例级共享内存

1. SGA 的定义

SGA(System Global Area) 是 Instance 的共享内存区域。连接到同一个实例的多个服务器进程可以访问其中的相同数据和控制结构。

SGA 中的内容通常包括:

  • Database Buffer Cache
  • Shared Pool
  • Redo Log Buffer
  • Large Pool
  • Java Pool
  • Streams Pool
  • Fixed SGA
  • 其他版本或功能相关的内存区域

不是每个数据库都以同样方式使用所有组件。例如,Java Pool 和 Streams Pool 是否重要,取决于是否启用了相关功能。

2. Database Buffer Cache

Database Buffer Cache 保存从数据文件读取到内存中的数据库块。

假设表中的某个数据块位于数据文件的第 1000 个块:

  1. 服务器进程需要访问该块;
  2. 先在 Buffer Cache 中查找;
  3. 如果命中,直接读取内存中的块;
  4. 如果未命中,从数据文件读取块到 Buffer Cache;
  5. 后续访问可能复用这个缓存块。

缓存中的块可能处于不同状态。一个重要状态是 dirty buffer:内存中的内容已经被修改,但修改尚未写入数据文件。

需要注意,Buffer Cache 是缓存,不是事务日志。事务提交是否完成,不由“数据块是否已经写入数据文件”直接决定。

3. Shared Pool

Shared Pool 保存多个会话和 SQL 执行所需的共享结构,主要包括:

  • Library Cache
  • Data Dictionary Cache
  • 其他共享控制信息

对一条 SQL,Oracle 通常需要完成:

  1. 语法和语义检查;
  2. 对象解析;
  3. 权限检查;
  4. 选择或生成执行计划;
  5. 共享或创建游标。

如果相同 SQL 能够复用已有游标,通常可以减少重复解析成本。但“SQL 文本相同”并不保证一定共享,还可能受对象、绑定变量、会话环境、权限和其他游标匹配条件影响。

例如,以下两类 SQL 在语义上可能查询同样的数据:

SELECT * FROM employees WHERE employee_id = 10;
SELECT * FROM employees WHERE employee_id = 20;

如果应用把常量直接拼接进 SQL,可能产生多个不同的 SQL 文本。更适合共享的形式通常是绑定变量:

SELECT * FROM employees WHERE employee_id = :employee_id;

但绑定变量不是“必然更快”的开关。它影响游标复用、优化器估算和自适应行为,最终效果仍取决于数据分布、统计信息和版本。

4. Redo Log Buffer

Redo Log Buffer 是 SGA 中保存 Redo 的区域。

Redo 描述的是数据库变更如何重做,而不是一份面向用户的 SQL 文本。例如一次更新可能产生:

  • 数据块变化对应的 Redo;
  • Undo 块变化对应的 Redo;
  • 索引块变化对应的 Redo;
  • 事务提交记录对应的 Redo。

Redo 的目标是支持持久性和恢复。它不等价于 Undo:

  • Redo:重做变更,用于恢复;
  • Undo:保存旧版本或回滚信息,用于回滚和读一致性。

5. Large Pool、Java Pool 与其他区域

Large Pool 可用于某些大型共享内存需求,例如共享服务器、并行执行和备份恢复相关操作。使用 Large Pool 的具体范围依赖 Oracle 版本和功能配置。

Java Pool 用于数据库 Java 相关功能。

Streams Pool 与特定流式复制或相关功能有关。现代版本中,Oracle 的复制和数据集成能力较多,不能仅凭某个内存区域的名称推断全部功能。

6. SGA 的管理方式

Oracle 可以采用自动或手工方式管理 SGA。常见参数包括:

SHOW PARAMETER sga;
SHOW PARAMETER memory_target;
SHOW PARAMETER memory_max_target;

不同管理模式的语义不同:

  • 自动内存管理可能由 MEMORY_TARGET 等参数控制实例总内存;
  • 自动共享内存管理通常由 SGA_TARGET 管理 SGA 内部组件;
  • 手工管理则分别设置各组件大小。

这些参数不是所有平台和部署都适合直接启用。例如操作系统、HugePages、容器资源限制和 Oracle 版本都会影响选择。PGA_AGGREGATE_TARGET 也不是“每个会话 PGA 的硬上限”,不能简单理解成所有会话都能各自使用该值。

可以用动态性能视图查看实例内存:

SELECT component,
       current_size,
       min_size,
       max_size,
       user_specified_size,
       granule_size
FROM   v$sga_dynamic_components
ORDER BY component;

查询通常需要相应的数据字典权限。输出中的 CURRENT_SIZE 是组件当前大小,MIN_SIZEMAX_SIZE 反映自动调整范围,但不能据此推断每条 SQL 的实际内存消耗。


四、PGA:服务器进程的私有内存

1. PGA 的定义

PGA(Program Global Area) 是 Oracle 服务器进程使用的非共享内存区域。

在专用服务器模式下,一个客户端会话通常对应一个服务器进程:

客户端进程
    │
    │ 网络连接
    ▼
服务器进程 ── 私有 PGA
    │
    └── 共享 SGA

PGA 中可能包含:

  • 会话状态;
  • 游标状态;
  • 栈空间;
  • SQL 工作区;
  • 排序、哈希、位图合并等操作使用的内存。

PGA 不是一个所有进程共享的“实例级缓存”。一个会话在 PGA 中创建的排序区,不能直接被另一个会话复用。

2. PGA 与执行计划

例如执行:

SELECT department_id, COUNT(*)
FROM   employees
GROUP BY department_id;

执行计划可能包含排序或哈希聚合:

  • 哈希聚合需要构建哈希表;
  • 排序聚合需要排序工作区;
  • 工作区优先使用 PGA;
  • 如果内存不足,可能产生临时段,溢出到临时文件。

因此,PGA 不足的表现不只是“查询变慢”,还可能出现临时表空间使用增加。

可以查看会话级 PGA:

SELECT sid,
       serial#,
       pga_used_mem,
       pga_alloc_mem,
       pga_max_mem
FROM   v$process
WHERE  addr = (SELECT paddr
               FROM   v$session
               WHERE  sid = :sid);

实际使用时需要替换 :sid,并具备查询动态性能视图的权限。PGA_USED_MEM 表示当前使用量,PGA_ALLOC_MEM 表示已分配量,PGA_MAX_MEM 表示进程曾达到的峰值;它们不等同于某条 SQL 单独使用的内存。

3. 专用服务器与共享服务器

上面的“一个会话对应一个服务器进程”是**专用服务器(dedicated server)**中的常见模型。

在**共享服务器(shared server)**中:

多个客户端会话
       │
       ▼
调度器 ── 共享服务器进程池
       │
       ├── 请求队列
       └── 共享服务器按请求处理

会话状态不能简单地只放在某一个长期绑定的服务器进程中,因此会使用 SGA 中的相关区域和共享服务器机制。共享服务器适合某些大量短请求场景,但并不适合所有工作负载,尤其是长时间运行、需要大量会话私有状态或特定连接行为的场景。

因此,看到“每个连接都有 PGA”时,应理解为:每个服务器进程都有自己的 PGA;连接与服务器进程的映射取决于连接模式。


五、从 SQL 到物理文件:一次查询和一次更新

1. 查询路径

以查询为例:

SELECT salary
FROM   employees
WHERE  employee_id = 100;

一个简化的执行过程如下:

  1. 客户端把 SQL 发送给服务器进程;
  2. 服务器进程在 Shared Pool 中查找可复用的游标;
  3. 如果需要,进行解析、权限检查和优化器计划生成;
  4. 根据执行计划访问索引或表;
  5. 访问所需的数据库块;
  6. 先查 Buffer Cache;
  7. 缓存未命中时,从数据文件读取块;
  8. 服务器进程在 PGA 中维护会话和执行工作区;
  9. 根据块中的数据和 Undo 信息构造满足读一致性的结果;
  10. 把结果返回给客户端。

这里有两个容易混淆的事实:

  • SQL 执行计划决定“如何找到块”,不决定数据库块永久存放在哪个物理磁盘位置;
  • 数据文件读取通常以 Oracle 数据块为单位,而不是按某一行单独读取。

2. 更新路径

执行:

UPDATE employees
SET    salary = salary * 1.1
WHERE  employee_id = 100;

简化后的内部过程可以表示为:

  1. 定位目标行所在的数据块;
  2. 将相关块读入 Buffer Cache;
  3. 获取必要的行级锁和事务资源;
  4. 在 Undo 段中记录旧值或恢复所需的信息;
  5. 修改内存中的数据块;
  6. 生成描述这些变化的 Redo;
  7. 将对应块标记为 dirty;
  8. 等待提交或继续执行其他语句。

修改通常先发生在内存中的缓存块里,并不意味着数据文件已经同步改变。


六、后台进程:谁负责把运行时状态推进到持久化状态

Oracle 的后台进程不是简单的“定时写文件线程”。它们共同维护缓存、日志、恢复、监控、并发和网络注册等机制。具体进程会随版本和功能变化,下面介绍与核心架构直接相关的类别。

1. DBWn:把脏数据块写入数据文件

DBWn(Database Writer) 将 Buffer Cache 中的 dirty buffer 写入数据文件。

典型触发条件包括:

  • 缓存中需要空间装入新块;
  • 检查点推进;
  • 数据库正常关闭;
  • 其他内部写出需求。

DBWn 不负责“每次更新后立即写数据文件”。如果每个更新都同步写数据文件,随机写和并发开销会非常高,也会使事务提交被数据文件 I/O 直接拖住。

DBWn 写入数据块前,必须遵守写前日志(write-ahead logging)约束:对应的 Redo 至少要已经安全写入在线重做日志。否则,数据库块已经包含新内容,但恢复所需的 Redo 还不存在,可能无法保证一致恢复。

2. LGWR:写在线重做日志

LGWR(Log Writer) 将 Redo Log Buffer 中的 Redo 写入在线重做日志文件。

LGWR 在以下情况下会写日志:

  • 事务提交;
  • Redo 缓冲区达到一定占用程度;
  • 距离上次写日志经过一定时间;
  • DBWn 需要写出包含相关变化的 dirty buffer;
  • 其他内部条件触发。

事务提交的核心持久性路径是:

事务修改
  ↓
生成 Redo,进入 Redo Log Buffer
  ↓ COMMIT
LGWR 将相关 Redo 写入在线重做日志并完成所需同步
  ↓
向客户端报告提交成功

因此,提交成功主要意味着提交相关 Redo 已达到 Oracle 要求的持久化条件,而不是所有修改的数据块都已经写入数据文件。

3. CKPT:推进检查点信息

CKPT(Checkpoint Process) 协调检查点相关工作,例如:

  • 更新控制文件中的检查点信息;
  • 更新数据文件头中的检查点信息;
  • 通知 DBWn 写出相关脏块。

检查点的目标之一是限制实例恢复需要扫描的 Redo 范围。检查点不是“把所有内存数据立即写干净”的同义词,也不是每次提交都会发生的操作。

4. SMON:系统监控与实例恢复

SMON(System Monitor) 负责多种系统级任务,其中最重要的概念是实例恢复。

实例崩溃后,部分已提交事务的脏块可能还未写入数据文件,但其 Redo 已写入日志;部分未提交事务的修改也可能已经进入数据文件。恢复过程需要:

  1. 从适当的检查点开始扫描 Redo;
  2. 重放需要重做的变化,使数据文件包含必要的最新状态;
  3. 使用 Undo 回滚崩溃时未提交的事务;
  4. 使数据库重新达到事务一致状态。

现代版本中,恢复任务可能由多个进程并行协作,不能把所有恢复行为机械地归因于某一个进程。

5. PMON:进程监控

PMON(Process Monitor) 处理服务器进程或会话异常终止后的清理和资源回收工作,例如:

  • 清理异常终止会话持有的资源;
  • 释放相关锁和状态;
  • 协调部分进程恢复工作。

PMON 不等于“回滚所有数据库恢复”。实例级恢复和事务级清理是相关但不同的过程。

6. ARCn:归档在线重做日志

在归档模式下,ARCn(Archiver) 将已经填满或切换下来的在线重做日志复制为归档日志。

归档日志使数据库能够进行更完整的介质恢复,例如:

备份中的数据文件
  + 归档 Redo
  + 必要的在线 Redo
  → 恢复到故障前或指定时间点

如果归档目标不可用,可能出现日志无法归档、日志切换受阻,最终影响数据库正常运行。归档日志不是“备份的替代品”,它通常需要与数据文件备份、控制文件备份等共同使用。

7. 其他常见后台进程

不同版本中还可能看到:

  • MMON、MMNL:性能监控、AWR 等相关任务;
  • LREG:向监听器注册服务;
  • RECO:分布式事务恢复;
  • RVWR:Flashback Database 相关写出;
  • VKTM:时间管理;
  • GEN0、DIAG 等通用或诊断进程。

不能仅凭进程名称判断某个问题的根因。例如发现 DBWn CPU 较高,不一定说明“DBWn 有故障”,也可能是缓存压力、检查点压力、I/O 能力或工作负载变化的结果。

查看当前实例和进程:

SELECT instance_name,
       status,
       database_status,
       startup_time
FROM   v$instance;

SELECT pname,
       program,
       background
FROM   v$process
ORDER BY pname;

V$PROCESS 的列和输出会因版本、平台和权限而变化;进程列表不应作为跨版本固定清单。


七、数据文件、表空间、数据块与逻辑对象

1. 表空间是逻辑存储容器

用户通常通过表空间管理逻辑存储:

Database
  └── Tablespace
        └── Segment
              └── Extent
                    └── Data Block

这些概念的关系是:

  • 表空间:数据库中的逻辑存储容器;
  • 数据文件:表空间对应的物理文件;
  • 段(segment):表、索引、Undo 等对象实际占用的空间;
  • 区(extent):段一次分配的一组连续或相关数据块;
  • 块(block):Oracle 管理数据的基本 I/O 单位之一。

一个表空间通常可以由一个或多个数据文件组成:

USERS tablespace
  ├── users01.dbf
  └── users02.dbf

一个数据文件只属于一个数据库和一个表空间。表的数据和索引的数据通常位于不同段中,段再分布到表空间的数据文件中。

2. 数据文件不是所有持久化内容的总称

以下内容不能混为“数据文件”:

  • 表和索引数据:主要在数据文件;
  • Undo:在 Undo 表空间的数据文件中;
  • 临时排序空间:通常在临时文件中;
  • Redo:在线重做日志文件和归档日志;
  • 数据库结构元数据:控制文件;
  • 参数:参数文件。

例如查看表空间与文件:

SELECT tablespace_name,
       file_name,
       bytes,
       autoextensible,
       maxbytes
FROM   dba_data_files
ORDER BY tablespace_name, file_name;

查看临时文件:

SELECT tablespace_name,
       file_name,
       bytes,
       autoextensible,
       maxbytes
FROM   dba_temp_files
ORDER BY tablespace_name, file_name;

AUTOEXTENSIBLE=YES 表示文件允许自动扩展,但是否能成功扩展还取决于文件系统、ASM 磁盘组、最大文件大小和数据库限制。自动扩展不是无限空间。

3. 数据块与行不是一一对应

一个数据块可以包含多行,也可能因为行迁移、行链接、压缩和块空间管理等原因出现更复杂的存储情况。索引条目也不等于表行本身。

因此,执行计划中的“访问表”通常意味着访问一批数据块,而不是直接从文件中定位一行并完成读取。


八、完整事务算例:为什么 COMMIT 不等于立即写数据文件

下面用一个简化事务说明 SGA、PGA、Undo、Redo、DBWn 和 LGWR 如何配合。

假设表 accounts 中存在:

account_id = 1
balance    = 100

执行:

UPDATE accounts
SET    balance = 80
WHERE  account_id = 1;

第一步:读取目标块

服务器进程根据执行计划定位目标行所在的数据块:

  • 如果块已在 Buffer Cache 中,直接使用;
  • 否则从数据文件读入 Buffer Cache;
  • 服务器进程在 PGA 中保存执行状态。

第二步:生成 Undo

Oracle 需要保留修改前的信息,例如:

旧值:balance = 100

这些信息写入 Undo 段。Undo 本身也位于数据库块中,通常存放在 Undo 表空间的数据文件里。

Undo 有两个主要用途:

  1. 回滚未提交事务;
  2. 为其他会话构造过去某个一致读时间点的数据版本。

第三步:修改缓存中的数据块

Buffer Cache 中的块变成:

account_id = 1
balance    = 80

该块被标记为 dirty。此时数据文件中的旧块可能仍然是:

balance = 100

第四步:生成 Redo

Oracle 为数据块变化和 Undo 变化生成 Redo。概念上可以表示为:

Redo =
  数据块从 100 变为 80 的变化
  + Undo 块变化
  + 事务相关信息

Redo 进入 Redo Log Buffer。

第五步:提交

执行:

COMMIT;

LGWR 将提交所需的 Redo 写入在线重做日志,并完成提交要求的同步。成功返回后,事务具有持久性保证:即使此时实例崩溃,恢复过程也可以根据 Redo 重建必要变化。

但此时 DBWn 可能还没有把 balance = 80 的数据块写入数据文件。

第六步:稍后写数据文件

随后,DBWn 可能因为缓存空间、检查点或其他条件,把 dirty buffer 写入数据文件。

于是会出现这个合法的中间状态:

在线 Redo 日志:包含 balance 从 100 到 80 的变化
数据文件:可能仍暂存旧块或尚未写入新块
事务状态:已提交

这不是数据丢失,因为 Redo 提供了恢复依据。

反例:没有 COMMIT 就发生实例崩溃

如果事务执行了 UPDATE,但没有 COMMIT,随后实例崩溃:

  • 修改可能已经在 Buffer Cache 中;
  • 甚至可能已经被 DBWn 写入数据文件;
  • 但事务没有提交记录;
  • 恢复时 Oracle 会识别该事务未完成;
  • 使用 Undo 回滚它的影响。

所以,“数据块已经落盘”也不等于“事务已经提交”。

反例:COMMIT 后立即断电

如果 COMMIT 已经成功返回,相关 Redo 按要求写入了在线重做日志,但数据块尚未写入数据文件:

  1. 实例崩溃;
  2. Oracle 启动实例恢复;
  3. 从检查点后扫描 Redo;
  4. 重放已提交变更;
  5. 对未提交事务执行回滚;
  6. 数据库重新打开。

这正是 Redo 与数据文件异步写入设计的价值。


九、读一致性、SCN、锁、Redo 和 Undo 的边界

这些概念经常被混在一起,但职责不同。

1. SCN:数据库逻辑时间

SCN(System Change Number) 是 Oracle 使用的逻辑变化编号,用于标识数据库状态和事务顺序相关信息。

一个查询需要基于某个一致的数据库状态读取数据。若查询开始时的逻辑时间早于某个并发事务的修改,Oracle 可能需要通过 Undo 构造修改前版本,使查询看到一致结果。

可以把一个查询的目标抽象为:

Visible version=最新版本中满足 SCNversionSCNquery 的版本\text{Visible version} = \text{最新版本中满足 } SCN_{\text{version}} \le SCN_{\text{query}} \text{ 的版本}

这是简化模型。真实可见性还涉及事务提交状态、块内事务槽、Undo 链和查询执行过程中的一致性规则。

2. Undo:旧版本与回滚信息

Undo 解决的是:

  • 事务失败时撤销修改;
  • 读一致性需要查看旧版本。

如果某个长查询需要的旧版本已经被覆盖,可能出现:

ORA-01555: snapshot too old

这不是“查询读到了脏数据”,而是查询所需的历史版本无法再从 Undo 中构造。

3. 锁:并发修改协调

锁主要用于保护并发修改的一致性,例如两个事务同时修改同一行时,一个事务可能等待另一个事务结束。

锁等待和读一致性不是同一个机制:

  • 普通一致性读通常不因另一个事务未提交而读取其未提交数据;
  • 对同一行进行冲突修改时,可能发生锁等待;
  • SELECT ... FOR UPDATE 是锁定读取,语义不同于普通查询。

4. Redo:恢复已发生的变化

Redo 用于重做变化,关注数据库恢复:

数据文件状态 + Redo
    → 恢复到一致状态

Redo 不负责直接提供查询历史版本;查询历史版本主要依赖 Undo。


十、实例恢复与介质恢复不是一回事

1. 实例恢复

实例恢复针对的是实例异常停止,例如:

  • 服务器断电;
  • Oracle 进程异常退出;
  • 操作系统故障。

数据库文件通常仍然存在,但部分数据块与 Redo 状态不一致。重新启动实例后,Oracle 利用在线 Redo 和 Undo 完成恢复。

其核心是:

重做:让数据文件包含恢复所需的变化
回滚:撤销崩溃时未提交事务

2. 介质恢复

介质恢复针对数据文件损坏或丢失,例如:

  • 磁盘故障;
  • 数据文件被删除;
  • 存储系统损坏。

此时仅靠内存和当前在线文件可能不够,需要:

  1. 从备份恢复数据文件;
  2. 应用归档 Redo;
  3. 必要时应用在线 Redo;
  4. 完成恢复并打开数据库。

实例恢复与介质恢复的区别可以概括为:

类型 主要故障 主要输入
实例恢复 实例异常停止,文件通常仍在 在线 Redo、Undo
介质恢复 数据文件损坏或丢失 备份、归档 Redo、在线 Redo

不能因为数据库启用了归档模式,就认为不需要备份。归档日志记录的是变化,不是完整的数据文件。


十一、容器数据库与 PDB:文件和实例边界的变化

在多租户架构中,CDB(Container Database) 可以包含多个 PDB(Pluggable Database)

常见层次是:

一个 Oracle Instance
  └── 一个 CDB
        ├── CDB$ROOT
        ├── PDB$SEED
        └── 一个或多个用户 PDB

从用户角度看,PDB 像一个相对独立的数据库容器,但它并不等于拥有一套完全独立的 Instance。多个 PDB 通常共享:

  • SGA;
  • 后台进程;
  • 实例级资源;
  • CDB 级别的部分控制结构。

PDB 可以拥有自己的用户对象、表空间和数据文件范围,但数据库级资源、内存和后台进程仍受 CDB/Instance 级别影响。

连接到 PDB 后,查询当前容器:

SHOW CON_NAME;

SELECT sys_context('USERENV', 'CON_NAME') AS container_name
FROM   dual;

查询容器信息:

SELECT con_id,
       name,
       open_mode
FROM   v$containers
ORDER BY con_id;

因此,排查“某个数据库实例的内存”时,需要明确你说的是:

  • 整个 Instance;
  • CDB;
  • 某一个 PDB;
  • 某个会话或服务器进程。

PDB 级别的资源限制不能简单替代实例级资源管理。


十二、RAC:多个 Instance 访问一个 Database

单实例架构可以抽象为:

一个 Instance
  ├── 一个 SGA
  ├── 一组后台进程
  └── 一组服务器进程
           │
           ▼
      一个 Database 的文件

RAC 则是:

Instance 1 ── 本地 SGA/进程 ──┐
                              ├── 共享 Database 文件
Instance 2 ── 本地 SGA/进程 ──┘

每个实例都有自己的 Buffer Cache。一个实例可能需要访问另一个实例最近修改过的缓存块。RAC 使用 Cache Fusion 等机制在实例间传递缓存块和协调全局资源。

因此:

  • SGA 不是整个 RAC 集群共享的一块普通内存;
  • 每个 Instance 有自己的 SGA;
  • 数据文件、控制文件和日志通常位于集群可访问的存储;
  • 跨实例并发会引入全局缓存和全局锁协调成本。

单实例中看到的 Buffer Cache 命中和锁等待,不能直接推导 RAC 中的全局缓存行为。RAC 诊断通常还需要关注实例间通信、全局缓存等待和服务分布。


十三、用动态性能视图建立架构观察

Oracle 提供一组动态性能视图,通常以 V$ 开头。它们不是普通业务表,而是由实例运行状态提供的信息视图。

1. 查看实例状态

SELECT instance_name,
       host_name,
       version,
       status,
       database_status,
       instance_role
FROM   v$instance;

可能看到的信息包括:

  • 实例名;
  • 主机名;
  • 数据库版本;
  • 实例状态;
  • 数据库状态;
  • RAC 中的实例角色。

2. 查看数据库文件和日志

SELECT name
FROM   v$datafile
ORDER BY file#;

SELECT name
FROM   v$controlfile;

SELECT group#,
       thread#,
       sequence#,
       bytes,
       members,
       status
FROM   v$log
ORDER BY group#;

V$LOG 描述在线重做日志组状态。RAC 环境可能涉及多个 redo thread,单实例示例不能直接套用到所有集群配置。

查看日志成员:

SELECT group#,
       member,
       type,
       status
FROM   v$logfile
ORDER BY group#, member;

3. 查看会话与服务器进程关系

SELECT s.sid,
       s.serial#,
       s.username,
       s.server,
       s.status,
       p.spid,
       p.program
FROM   v$session s
LEFT JOIN v$process p
       ON p.addr = s.paddr
WHERE  s.type = 'USER';

这里可以观察:

  • V$SESSION 中的逻辑会话;
  • SERVER 显示连接模式信息;
  • V$PROCESS 中的 Oracle 进程;
  • SPID 是操作系统进程标识,但具体类型和可见性依赖平台与权限。

会话被杀掉后,资源清理可能不是瞬间完成。看到会话状态变化,不应立即推断所有事务、锁和 PGA 已同步清零。


十四、常见误解与失败表现

误解一:数据库就是数据文件

不完整。数据库还包括控制文件和在线重做日志等关键物理文件。数据文件丢失时可以尝试介质恢复;控制文件或在线重做日志损坏也会影响数据库可用性和恢复路径。

误解二:SGA 是每个会话私有内存

错误。SGA 是实例级共享内存。会话私有执行状态、排序和哈希工作区主要属于服务器进程的 PGA。

误解三:PGA 越大,所有 SQL 都越快

错误。PGA 主要帮助需要私有工作区的操作。SQL 是否使用排序、哈希、并行执行,首先由执行计划和数据规模决定。即使 PGA 足够大,错误的连接条件、缺失索引、过期统计信息或低选择性谓词仍可能导致高成本计划。

误解四:COMMIT 后 DBWn 已经写完数据文件

不成立。提交的关键持久性点是相关 Redo 达到要求的持久化状态。数据块可以稍后由 DBWn 写入数据文件。

误解五:Redo 和 Undo 是同一种日志

错误。Redo 面向恢复重做,Undo 面向回滚和一致性读。Undo 的变化本身也会产生 Redo,所以二者不是互斥关系。

误解六:检查点就是一次完整刷盘

不准确。检查点推进恢复位置并协调相关脏块写出,但其实现和范围不能简化为“所有缓存一次性写入所有数据文件”。

误解七:后台进程列表在所有版本都固定

错误。进程会因版本、操作系统、RAC、归档、备份、Flashback、Data Guard 和其他功能变化。排查问题时应结合当前版本和具体进程职责,而不是只背诵某一张旧架构图。

误解八:数据文件空间足够就不会出现空间错误

不一定。可能出现:

  • 表空间剩余空间不足;
  • 数据文件达到最大大小;
  • 临时表空间不足;
  • Undo 空间不足;
  • ASM 磁盘组或文件系统空间不足;
  • 单个段、区或块受到限制。

诊断空间问题时,应同时查看表空间、数据文件、临时文件、Undo 和底层存储。


十五、把架构映射到诊断路径

遇到性能或故障问题时,先根据现象定位层次,而不是直接调整参数。

1. SQL 解析或执行计划异常

重点观察:

  • Shared Pool 和游标;
  • SQL 是否使用绑定变量;
  • 对象权限和失效;
  • 优化器统计信息;
  • 执行计划和实际行数;
  • PGA 工作区是否溢出到临时表空间。

相关视图示例:

SELECT sql_id,
       executions,
       elapsed_time,
       cpu_time,
       buffer_gets,
       disk_reads,
       rows_processed
FROM   v$sql
ORDER BY elapsed_time DESC
FETCH FIRST 20 ROWS ONLY;

这只能提供实例当前缓存中的统计信息,不等价于完整历史数据;SQL 游标被淘汰后,相关统计可能不再可见。

2. I/O 等待明显

需要区分:

  • 数据文件读取:可能与 Buffer Cache 未命中或物理 I/O 有关;
  • 在线重做日志写入:可能与 LGWR、存储延迟或提交频率有关;
  • 临时文件 I/O:可能与 PGA 不足或执行计划中的排序、哈希溢出有关;
  • 归档日志写出:可能与归档目标和存储故障有关。

不能把所有 I/O 都归结为“Buffer Cache 太小”。

3. 提交延迟明显

应检查:

  • 在线重做日志写入延迟;
  • 日志文件所在存储;
  • 提交频率;
  • 日志组切换和归档是否受阻;
  • 是否存在高并发提交。

因为提交路径依赖 LGWR,盲目增加 Buffer Cache 通常不能解决 Redo 写延迟。

4. Undo 或读一致性错误

如果出现 ORA-01555 或 Undo 空间压力,应分析:

  • 长时间运行的查询;
  • Undo 保留策略;
  • DML 产生 Undo 的速率;
  • Undo 表空间大小和自动扩展;
  • 是否存在异常长事务。

仅仅增加 Buffer Cache 不会直接增加历史版本保存时间。

5. 实例异常停止后无法打开

需要区分:

  • Instance 是否已启动;
  • 数据库当前处于 NOMOUNT、MOUNT 还是 OPEN;
  • 控制文件是否可读;
  • 数据文件是否缺失或需要恢复;
  • 在线重做日志是否可用;
  • 是否正在进行实例恢复或介质恢复。

可以先查看:

SELECT status,
       database_status
FROM   v$instance;

然后结合数据库告警日志、操作系统日志、存储错误和恢复操作结果判断。恢复操作具有破坏性风险,尤其是重建控制文件、重置日志和不完整恢复,不能仅凭“数据库打不开”就直接执行。


十六、一个可复用的整体心智模型

可以把一次数据库修改压缩成下面这条数据流:

客户端
  ↓
会话 / 服务器进程
  ↓
PGA:会话状态、执行工作区
  ↓
Shared Pool:解析、游标、数据字典信息
  ↓
Buffer Cache:读取和修改数据库块
  ├── Undo:旧版本、回滚信息
  └── Redo Log Buffer:变化记录
          ↓
        LGWR
          ↓
在线重做日志
          
Buffer Cache 中的 dirty buffer
          ↓
        DBWn
          ↓
数据文件

同时:

CKPT:推进检查点
SMON:实例恢复和系统监控
PMON:异常进程清理
ARCn:归档在线重做日志
其他后台进程:监控、注册、分布式事务、Flashback 等功能

这套模型能够解释几个关键事实:

  1. Instance 是运行时环境,不是磁盘上的数据库文件。
  2. SGA 负责实例级共享,PGA 主要服务于服务器进程私有执行。
  3. 数据文件保存数据库块,但不是所有变化都先写数据文件。
  4. LGWR 负责 Redo 持久化,DBWn 负责脏数据块写出。
  5. Redo 支持恢复,Undo 支持回滚和读一致性。
  6. 检查点缩短恢复路径,但不改变事务提交的基本语义。
  7. 后台进程的具体数量和职责边界随版本与功能变化。
  8. 在 CDB/PDB 和 RAC 中,Instance、Database、容器与节点的边界会进一步分层。

当 SQL 执行、事务提交、实例恢复、空间管理和性能诊断都能映射回这条数据流时,Oracle 的内存、进程和文件架构就不再只是组件名称,而成为可以用于解释实际行为的运行模型。


系列导航与关联阅读

官方资料

本文依据数据库官方文档重新梳理;正文、示例与生产检查清单由 WR BLOG 编写。

评论

0 条讨论
0/1000
还没有评论,来聊聊你的看法
WR Blog 加载中...
返回文章
数据库Oracle架构后端

Oracle 数据库架构:Instance、SGA、PGA、数据文件与后台进程

Oracle 数据库架构:Instance、SGA、PGA、数据文件与后台进程封面

数据库基础体系 · 第 26/139 篇。文章以各产品官方稳定版本的公开语义为准;示例会明确引擎、事务与部署边界。

Oracle 数据库架构:Instance、SGA、PGA、数据文件与后台进程

Oracle 的“数据库架构”容易被简化成一张组件图:Instance 包含 SGA 和后台进程,Database 包含数据文件。但真正理解 Oracle,需要继续回答几个问题:

  • SQL 到底由谁执行,执行过程中数据放在哪里?
  • 一个事务修改的数据何时写入数据文件?
  • COMMIT 为什么通常不等于立即写数据文件?
  • 会话、服务器进程、PGA 和 SGA 是什么关系?
  • 实例崩溃后,Oracle 如何利用 Redo 和 Undo 恢复?
  • 单实例、RAC、CDB/PDB 的边界分别在哪里?

本文以 Oracle Database 的通用架构语义为基础,示例使用常见的单实例、专用服务器连接和 SQL*Plus/SQLcl 风格语法。具体后台进程名称、内存组件和可配置参数会随版本、平台、部署模式和启用的功能变化。


一、先区分 Oracle Database 与 Oracle Instance

1. Database:持久化的数据集合

在 Oracle 语境中,database 是存储在磁盘上的一组物理文件,主要包括:

  • 数据文件(data files)
  • 控制文件(control files)
  • 在线重做日志文件(online redo log files)

此外还可能有:

  • 临时文件(tempfiles)
  • 归档重做日志(archived redo logs)
  • 参数文件、密码文件
  • Flashback、备份和恢复相关文件

其中,控制文件和在线重做日志从恢复角度看非常关键,但它们不是普通表数据所在的数据文件。

数据文件中保存的是表、索引、Undo 段以及数据字典等数据库对象对应的块。控制文件记录数据库物理结构和恢复所需的元数据,例如数据库名称、数据文件和日志文件信息、检查点相关信息等。在线重做日志保存数据库变更产生的 Redo。

2. Instance:访问 Database 的运行环境

instance 是一组运行时内存结构和后台进程:

Instance=SGA+Oracle processes\text{Instance} = \text{SGA} + \text{Oracle processes}

在单实例部署中,通常一个 Instance 打开一个 Database。Instance 停止后,Database 的文件仍然存在,但没有运行中的 Oracle 进程访问它。

因此:

  • Database 是持久化文件集合;
  • Instance 是运行中的内存和进程集合;
  • 数据库文件可以在实例停止后继续存在;
  • 实例不能脱离数据库文件完成正常的数据访问。

在 Oracle RAC 中,多个 Instance 可以同时访问同一个 Database:

One DatabaseMultiple Instances\text{One Database} \leftarrow \text{Multiple Instances}

这些实例通常位于不同节点,共享数据库文件,但各自拥有本地 SGA 和本地后台进程。实例之间通过集群互联协调缓存块、锁和全局资源。


二、数据库启动过程:从 NOMOUNT 到 OPEN

理解 Instance 与 Database 的边界,最直观的方法是看启动状态。

1. NOMOUNT:只启动 Instance

执行:

STARTUP NOMOUNT;

Oracle 会:

  1. 读取初始化参数文件或服务器参数文件;
  2. 分配 SGA;
  3. 启动后台进程;
  4. 进入 NOMOUNT 状态。

此时 Instance 已经存在,但还没有读取控制文件。因此不能正常访问数据库中的数据文件、表和用户对象。

NOMOUNT 常用于创建数据库、重建控制文件等操作。

2. MOUNT:Instance 读取控制文件

执行:

ALTER DATABASE MOUNT;

Oracle 会打开并读取控制文件,确认数据库的物理结构,包括:

  • 数据文件列表;
  • 在线重做日志组和成员;
  • 数据库名称和相关标识;
  • 检查点及恢复所需信息。

此时数据库已挂载,但数据文件尚未对普通用户开放。

MOUNT 状态可以执行部分恢复操作,例如介质恢复前的准备。

3. OPEN:打开数据文件并提供服务

执行:

ALTER DATABASE OPEN;

Oracle 会根据控制文件检查和打开数据文件、在线重做日志等资源,并使数据库进入可访问状态。

因此状态之间的关系是:

NOMOUNT
  └─ Instance 已启动,尚未读取控制文件
       ↓ MOUNT
MOUNT
  └─ 控制文件已读取,数据库已挂载,数据文件尚未开放
       ↓ OPEN
OPEN
  └─ 数据库可正常提供 SQL 服务

如果上一次实例异常终止,Oracle 可能需要在 OPEN 前或 OPEN 过程中执行实例恢复。实例恢复主要使用在线重做日志把已提交或需要重放的变更重新应用,再利用 Undo 回滚未提交事务。


三、SGA:实例级共享内存

1. SGA 的定义

SGA(System Global Area) 是 Instance 的共享内存区域。连接到同一个实例的多个服务器进程可以访问其中的相同数据和控制结构。

SGA 中的内容通常包括:

  • Database Buffer Cache
  • Shared Pool
  • Redo Log Buffer
  • Large Pool
  • Java Pool
  • Streams Pool
  • Fixed SGA
  • 其他版本或功能相关的内存区域

不是每个数据库都以同样方式使用所有组件。例如,Java Pool 和 Streams Pool 是否重要,取决于是否启用了相关功能。

2. Database Buffer Cache

Database Buffer Cache 保存从数据文件读取到内存中的数据库块。

假设表中的某个数据块位于数据文件的第 1000 个块:

  1. 服务器进程需要访问该块;
  2. 先在 Buffer Cache 中查找;
  3. 如果命中,直接读取内存中的块;
  4. 如果未命中,从数据文件读取块到 Buffer Cache;
  5. 后续访问可能复用这个缓存块。

缓存中的块可能处于不同状态。一个重要状态是 dirty buffer:内存中的内容已经被修改,但修改尚未写入数据文件。

需要注意,Buffer Cache 是缓存,不是事务日志。事务提交是否完成,不由“数据块是否已经写入数据文件”直接决定。

3. Shared Pool

Shared Pool 保存多个会话和 SQL 执行所需的共享结构,主要包括:

  • Library Cache
  • Data Dictionary Cache
  • 其他共享控制信息

对一条 SQL,Oracle 通常需要完成:

  1. 语法和语义检查;
  2. 对象解析;
  3. 权限检查;
  4. 选择或生成执行计划;
  5. 共享或创建游标。

如果相同 SQL 能够复用已有游标,通常可以减少重复解析成本。但“SQL 文本相同”并不保证一定共享,还可能受对象、绑定变量、会话环境、权限和其他游标匹配条件影响。

例如,以下两类 SQL 在语义上可能查询同样的数据:

SELECT * FROM employees WHERE employee_id = 10;
SELECT * FROM employees WHERE employee_id = 20;

如果应用把常量直接拼接进 SQL,可能产生多个不同的 SQL 文本。更适合共享的形式通常是绑定变量:

SELECT * FROM employees WHERE employee_id = :employee_id;

但绑定变量不是“必然更快”的开关。它影响游标复用、优化器估算和自适应行为,最终效果仍取决于数据分布、统计信息和版本。

4. Redo Log Buffer

Redo Log Buffer 是 SGA 中保存 Redo 的区域。

Redo 描述的是数据库变更如何重做,而不是一份面向用户的 SQL 文本。例如一次更新可能产生:

  • 数据块变化对应的 Redo;
  • Undo 块变化对应的 Redo;
  • 索引块变化对应的 Redo;
  • 事务提交记录对应的 Redo。

Redo 的目标是支持持久性和恢复。它不等价于 Undo:

  • Redo:重做变更,用于恢复;
  • Undo:保存旧版本或回滚信息,用于回滚和读一致性。

5. Large Pool、Java Pool 与其他区域

Large Pool 可用于某些大型共享内存需求,例如共享服务器、并行执行和备份恢复相关操作。使用 Large Pool 的具体范围依赖 Oracle 版本和功能配置。

Java Pool 用于数据库 Java 相关功能。

Streams Pool 与特定流式复制或相关功能有关。现代版本中,Oracle 的复制和数据集成能力较多,不能仅凭某个内存区域的名称推断全部功能。

6. SGA 的管理方式

Oracle 可以采用自动或手工方式管理 SGA。常见参数包括:

SHOW PARAMETER sga;
SHOW PARAMETER memory_target;
SHOW PARAMETER memory_max_target;

不同管理模式的语义不同:

  • 自动内存管理可能由 MEMORY_TARGET 等参数控制实例总内存;
  • 自动共享内存管理通常由 SGA_TARGET 管理 SGA 内部组件;
  • 手工管理则分别设置各组件大小。

这些参数不是所有平台和部署都适合直接启用。例如操作系统、HugePages、容器资源限制和 Oracle 版本都会影响选择。PGA_AGGREGATE_TARGET 也不是“每个会话 PGA 的硬上限”,不能简单理解成所有会话都能各自使用该值。

可以用动态性能视图查看实例内存:

SELECT component,
       current_size,
       min_size,
       max_size,
       user_specified_size,
       granule_size
FROM   v$sga_dynamic_components
ORDER BY component;

查询通常需要相应的数据字典权限。输出中的 CURRENT_SIZE 是组件当前大小,MIN_SIZEMAX_SIZE 反映自动调整范围,但不能据此推断每条 SQL 的实际内存消耗。


四、PGA:服务器进程的私有内存

1. PGA 的定义

PGA(Program Global Area) 是 Oracle 服务器进程使用的非共享内存区域。

在专用服务器模式下,一个客户端会话通常对应一个服务器进程:

客户端进程
    │
    │ 网络连接
    ▼
服务器进程 ── 私有 PGA
    │
    └── 共享 SGA

PGA 中可能包含:

  • 会话状态;
  • 游标状态;
  • 栈空间;
  • SQL 工作区;
  • 排序、哈希、位图合并等操作使用的内存。

PGA 不是一个所有进程共享的“实例级缓存”。一个会话在 PGA 中创建的排序区,不能直接被另一个会话复用。

2. PGA 与执行计划

例如执行:

SELECT department_id, COUNT(*)
FROM   employees
GROUP BY department_id;

执行计划可能包含排序或哈希聚合:

  • 哈希聚合需要构建哈希表;
  • 排序聚合需要排序工作区;
  • 工作区优先使用 PGA;
  • 如果内存不足,可能产生临时段,溢出到临时文件。

因此,PGA 不足的表现不只是“查询变慢”,还可能出现临时表空间使用增加。

可以查看会话级 PGA:

SELECT sid,
       serial#,
       pga_used_mem,
       pga_alloc_mem,
       pga_max_mem
FROM   v$process
WHERE  addr = (SELECT paddr
               FROM   v$session
               WHERE  sid = :sid);

实际使用时需要替换 :sid,并具备查询动态性能视图的权限。PGA_USED_MEM 表示当前使用量,PGA_ALLOC_MEM 表示已分配量,PGA_MAX_MEM 表示进程曾达到的峰值;它们不等同于某条 SQL 单独使用的内存。

3. 专用服务器与共享服务器

上面的“一个会话对应一个服务器进程”是**专用服务器(dedicated server)**中的常见模型。

在**共享服务器(shared server)**中:

多个客户端会话
       │
       ▼
调度器 ── 共享服务器进程池
       │
       ├── 请求队列
       └── 共享服务器按请求处理

会话状态不能简单地只放在某一个长期绑定的服务器进程中,因此会使用 SGA 中的相关区域和共享服务器机制。共享服务器适合某些大量短请求场景,但并不适合所有工作负载,尤其是长时间运行、需要大量会话私有状态或特定连接行为的场景。

因此,看到“每个连接都有 PGA”时,应理解为:每个服务器进程都有自己的 PGA;连接与服务器进程的映射取决于连接模式。


五、从 SQL 到物理文件:一次查询和一次更新

1. 查询路径

以查询为例:

SELECT salary
FROM   employees
WHERE  employee_id = 100;

一个简化的执行过程如下:

  1. 客户端把 SQL 发送给服务器进程;
  2. 服务器进程在 Shared Pool 中查找可复用的游标;
  3. 如果需要,进行解析、权限检查和优化器计划生成;
  4. 根据执行计划访问索引或表;
  5. 访问所需的数据库块;
  6. 先查 Buffer Cache;
  7. 缓存未命中时,从数据文件读取块;
  8. 服务器进程在 PGA 中维护会话和执行工作区;
  9. 根据块中的数据和 Undo 信息构造满足读一致性的结果;
  10. 把结果返回给客户端。

这里有两个容易混淆的事实:

  • SQL 执行计划决定“如何找到块”,不决定数据库块永久存放在哪个物理磁盘位置;
  • 数据文件读取通常以 Oracle 数据块为单位,而不是按某一行单独读取。

2. 更新路径

执行:

UPDATE employees
SET    salary = salary * 1.1
WHERE  employee_id = 100;

简化后的内部过程可以表示为:

  1. 定位目标行所在的数据块;
  2. 将相关块读入 Buffer Cache;
  3. 获取必要的行级锁和事务资源;
  4. 在 Undo 段中记录旧值或恢复所需的信息;
  5. 修改内存中的数据块;
  6. 生成描述这些变化的 Redo;
  7. 将对应块标记为 dirty;
  8. 等待提交或继续执行其他语句。

修改通常先发生在内存中的缓存块里,并不意味着数据文件已经同步改变。


六、后台进程:谁负责把运行时状态推进到持久化状态

Oracle 的后台进程不是简单的“定时写文件线程”。它们共同维护缓存、日志、恢复、监控、并发和网络注册等机制。具体进程会随版本和功能变化,下面介绍与核心架构直接相关的类别。

1. DBWn:把脏数据块写入数据文件

DBWn(Database Writer) 将 Buffer Cache 中的 dirty buffer 写入数据文件。

典型触发条件包括:

  • 缓存中需要空间装入新块;
  • 检查点推进;
  • 数据库正常关闭;
  • 其他内部写出需求。

DBWn 不负责“每次更新后立即写数据文件”。如果每个更新都同步写数据文件,随机写和并发开销会非常高,也会使事务提交被数据文件 I/O 直接拖住。

DBWn 写入数据块前,必须遵守写前日志(write-ahead logging)约束:对应的 Redo 至少要已经安全写入在线重做日志。否则,数据库块已经包含新内容,但恢复所需的 Redo 还不存在,可能无法保证一致恢复。

2. LGWR:写在线重做日志

LGWR(Log Writer) 将 Redo Log Buffer 中的 Redo 写入在线重做日志文件。

LGWR 在以下情况下会写日志:

  • 事务提交;
  • Redo 缓冲区达到一定占用程度;
  • 距离上次写日志经过一定时间;
  • DBWn 需要写出包含相关变化的 dirty buffer;
  • 其他内部条件触发。

事务提交的核心持久性路径是:

事务修改
  ↓
生成 Redo,进入 Redo Log Buffer
  ↓ COMMIT
LGWR 将相关 Redo 写入在线重做日志并完成所需同步
  ↓
向客户端报告提交成功

因此,提交成功主要意味着提交相关 Redo 已达到 Oracle 要求的持久化条件,而不是所有修改的数据块都已经写入数据文件。

3. CKPT:推进检查点信息

CKPT(Checkpoint Process) 协调检查点相关工作,例如:

  • 更新控制文件中的检查点信息;
  • 更新数据文件头中的检查点信息;
  • 通知 DBWn 写出相关脏块。

检查点的目标之一是限制实例恢复需要扫描的 Redo 范围。检查点不是“把所有内存数据立即写干净”的同义词,也不是每次提交都会发生的操作。

4. SMON:系统监控与实例恢复

SMON(System Monitor) 负责多种系统级任务,其中最重要的概念是实例恢复。

实例崩溃后,部分已提交事务的脏块可能还未写入数据文件,但其 Redo 已写入日志;部分未提交事务的修改也可能已经进入数据文件。恢复过程需要:

  1. 从适当的检查点开始扫描 Redo;
  2. 重放需要重做的变化,使数据文件包含必要的最新状态;
  3. 使用 Undo 回滚崩溃时未提交的事务;
  4. 使数据库重新达到事务一致状态。

现代版本中,恢复任务可能由多个进程并行协作,不能把所有恢复行为机械地归因于某一个进程。

5. PMON:进程监控

PMON(Process Monitor) 处理服务器进程或会话异常终止后的清理和资源回收工作,例如:

  • 清理异常终止会话持有的资源;
  • 释放相关锁和状态;
  • 协调部分进程恢复工作。

PMON 不等于“回滚所有数据库恢复”。实例级恢复和事务级清理是相关但不同的过程。

6. ARCn:归档在线重做日志

在归档模式下,ARCn(Archiver) 将已经填满或切换下来的在线重做日志复制为归档日志。

归档日志使数据库能够进行更完整的介质恢复,例如:

备份中的数据文件
  + 归档 Redo
  + 必要的在线 Redo
  → 恢复到故障前或指定时间点

如果归档目标不可用,可能出现日志无法归档、日志切换受阻,最终影响数据库正常运行。归档日志不是“备份的替代品”,它通常需要与数据文件备份、控制文件备份等共同使用。

7. 其他常见后台进程

不同版本中还可能看到:

  • MMON、MMNL:性能监控、AWR 等相关任务;
  • LREG:向监听器注册服务;
  • RECO:分布式事务恢复;
  • RVWR:Flashback Database 相关写出;
  • VKTM:时间管理;
  • GEN0、DIAG 等通用或诊断进程。

不能仅凭进程名称判断某个问题的根因。例如发现 DBWn CPU 较高,不一定说明“DBWn 有故障”,也可能是缓存压力、检查点压力、I/O 能力或工作负载变化的结果。

查看当前实例和进程:

SELECT instance_name,
       status,
       database_status,
       startup_time
FROM   v$instance;

SELECT pname,
       program,
       background
FROM   v$process
ORDER BY pname;

V$PROCESS 的列和输出会因版本、平台和权限而变化;进程列表不应作为跨版本固定清单。


七、数据文件、表空间、数据块与逻辑对象

1. 表空间是逻辑存储容器

用户通常通过表空间管理逻辑存储:

Database
  └── Tablespace
        └── Segment
              └── Extent
                    └── Data Block

这些概念的关系是:

  • 表空间:数据库中的逻辑存储容器;
  • 数据文件:表空间对应的物理文件;
  • 段(segment):表、索引、Undo 等对象实际占用的空间;
  • 区(extent):段一次分配的一组连续或相关数据块;
  • 块(block):Oracle 管理数据的基本 I/O 单位之一。

一个表空间通常可以由一个或多个数据文件组成:

USERS tablespace
  ├── users01.dbf
  └── users02.dbf

一个数据文件只属于一个数据库和一个表空间。表的数据和索引的数据通常位于不同段中,段再分布到表空间的数据文件中。

2. 数据文件不是所有持久化内容的总称

以下内容不能混为“数据文件”:

  • 表和索引数据:主要在数据文件;
  • Undo:在 Undo 表空间的数据文件中;
  • 临时排序空间:通常在临时文件中;
  • Redo:在线重做日志文件和归档日志;
  • 数据库结构元数据:控制文件;
  • 参数:参数文件。

例如查看表空间与文件:

SELECT tablespace_name,
       file_name,
       bytes,
       autoextensible,
       maxbytes
FROM   dba_data_files
ORDER BY tablespace_name, file_name;

查看临时文件:

SELECT tablespace_name,
       file_name,
       bytes,
       autoextensible,
       maxbytes
FROM   dba_temp_files
ORDER BY tablespace_name, file_name;

AUTOEXTENSIBLE=YES 表示文件允许自动扩展,但是否能成功扩展还取决于文件系统、ASM 磁盘组、最大文件大小和数据库限制。自动扩展不是无限空间。

3. 数据块与行不是一一对应

一个数据块可以包含多行,也可能因为行迁移、行链接、压缩和块空间管理等原因出现更复杂的存储情况。索引条目也不等于表行本身。

因此,执行计划中的“访问表”通常意味着访问一批数据块,而不是直接从文件中定位一行并完成读取。


八、完整事务算例:为什么 COMMIT 不等于立即写数据文件

下面用一个简化事务说明 SGA、PGA、Undo、Redo、DBWn 和 LGWR 如何配合。

假设表 accounts 中存在:

account_id = 1
balance    = 100

执行:

UPDATE accounts
SET    balance = 80
WHERE  account_id = 1;

第一步:读取目标块

服务器进程根据执行计划定位目标行所在的数据块:

  • 如果块已在 Buffer Cache 中,直接使用;
  • 否则从数据文件读入 Buffer Cache;
  • 服务器进程在 PGA 中保存执行状态。

第二步:生成 Undo

Oracle 需要保留修改前的信息,例如:

旧值:balance = 100

这些信息写入 Undo 段。Undo 本身也位于数据库块中,通常存放在 Undo 表空间的数据文件里。

Undo 有两个主要用途:

  1. 回滚未提交事务;
  2. 为其他会话构造过去某个一致读时间点的数据版本。

第三步:修改缓存中的数据块

Buffer Cache 中的块变成:

account_id = 1
balance    = 80

该块被标记为 dirty。此时数据文件中的旧块可能仍然是:

balance = 100

第四步:生成 Redo

Oracle 为数据块变化和 Undo 变化生成 Redo。概念上可以表示为:

Redo =
  数据块从 100 变为 80 的变化
  + Undo 块变化
  + 事务相关信息

Redo 进入 Redo Log Buffer。

第五步:提交

执行:

COMMIT;

LGWR 将提交所需的 Redo 写入在线重做日志,并完成提交要求的同步。成功返回后,事务具有持久性保证:即使此时实例崩溃,恢复过程也可以根据 Redo 重建必要变化。

但此时 DBWn 可能还没有把 balance = 80 的数据块写入数据文件。

第六步:稍后写数据文件

随后,DBWn 可能因为缓存空间、检查点或其他条件,把 dirty buffer 写入数据文件。

于是会出现这个合法的中间状态:

在线 Redo 日志:包含 balance 从 100 到 80 的变化
数据文件:可能仍暂存旧块或尚未写入新块
事务状态:已提交

这不是数据丢失,因为 Redo 提供了恢复依据。

反例:没有 COMMIT 就发生实例崩溃

如果事务执行了 UPDATE,但没有 COMMIT,随后实例崩溃:

  • 修改可能已经在 Buffer Cache 中;
  • 甚至可能已经被 DBWn 写入数据文件;
  • 但事务没有提交记录;
  • 恢复时 Oracle 会识别该事务未完成;
  • 使用 Undo 回滚它的影响。

所以,“数据块已经落盘”也不等于“事务已经提交”。

反例:COMMIT 后立即断电

如果 COMMIT 已经成功返回,相关 Redo 按要求写入了在线重做日志,但数据块尚未写入数据文件:

  1. 实例崩溃;
  2. Oracle 启动实例恢复;
  3. 从检查点后扫描 Redo;
  4. 重放已提交变更;
  5. 对未提交事务执行回滚;
  6. 数据库重新打开。

这正是 Redo 与数据文件异步写入设计的价值。


九、读一致性、SCN、锁、Redo 和 Undo 的边界

这些概念经常被混在一起,但职责不同。

1. SCN:数据库逻辑时间

SCN(System Change Number) 是 Oracle 使用的逻辑变化编号,用于标识数据库状态和事务顺序相关信息。

一个查询需要基于某个一致的数据库状态读取数据。若查询开始时的逻辑时间早于某个并发事务的修改,Oracle 可能需要通过 Undo 构造修改前版本,使查询看到一致结果。

可以把一个查询的目标抽象为:

Visible version=最新版本中满足 SCNversionSCNquery 的版本\text{Visible version} = \text{最新版本中满足 } SCN_{\text{version}} \le SCN_{\text{query}} \text{ 的版本}

这是简化模型。真实可见性还涉及事务提交状态、块内事务槽、Undo 链和查询执行过程中的一致性规则。

2. Undo:旧版本与回滚信息

Undo 解决的是:

  • 事务失败时撤销修改;
  • 读一致性需要查看旧版本。

如果某个长查询需要的旧版本已经被覆盖,可能出现:

ORA-01555: snapshot too old

这不是“查询读到了脏数据”,而是查询所需的历史版本无法再从 Undo 中构造。

3. 锁:并发修改协调

锁主要用于保护并发修改的一致性,例如两个事务同时修改同一行时,一个事务可能等待另一个事务结束。

锁等待和读一致性不是同一个机制:

  • 普通一致性读通常不因另一个事务未提交而读取其未提交数据;
  • 对同一行进行冲突修改时,可能发生锁等待;
  • SELECT ... FOR UPDATE 是锁定读取,语义不同于普通查询。

4. Redo:恢复已发生的变化

Redo 用于重做变化,关注数据库恢复:

数据文件状态 + Redo
    → 恢复到一致状态

Redo 不负责直接提供查询历史版本;查询历史版本主要依赖 Undo。


十、实例恢复与介质恢复不是一回事

1. 实例恢复

实例恢复针对的是实例异常停止,例如:

  • 服务器断电;
  • Oracle 进程异常退出;
  • 操作系统故障。

数据库文件通常仍然存在,但部分数据块与 Redo 状态不一致。重新启动实例后,Oracle 利用在线 Redo 和 Undo 完成恢复。

其核心是:

重做:让数据文件包含恢复所需的变化
回滚:撤销崩溃时未提交事务

2. 介质恢复

介质恢复针对数据文件损坏或丢失,例如:

  • 磁盘故障;
  • 数据文件被删除;
  • 存储系统损坏。

此时仅靠内存和当前在线文件可能不够,需要:

  1. 从备份恢复数据文件;
  2. 应用归档 Redo;
  3. 必要时应用在线 Redo;
  4. 完成恢复并打开数据库。

实例恢复与介质恢复的区别可以概括为:

类型 主要故障 主要输入
实例恢复 实例异常停止,文件通常仍在 在线 Redo、Undo
介质恢复 数据文件损坏或丢失 备份、归档 Redo、在线 Redo

不能因为数据库启用了归档模式,就认为不需要备份。归档日志记录的是变化,不是完整的数据文件。


十一、容器数据库与 PDB:文件和实例边界的变化

在多租户架构中,CDB(Container Database) 可以包含多个 PDB(Pluggable Database)

常见层次是:

一个 Oracle Instance
  └── 一个 CDB
        ├── CDB$ROOT
        ├── PDB$SEED
        └── 一个或多个用户 PDB

从用户角度看,PDB 像一个相对独立的数据库容器,但它并不等于拥有一套完全独立的 Instance。多个 PDB 通常共享:

  • SGA;
  • 后台进程;
  • 实例级资源;
  • CDB 级别的部分控制结构。

PDB 可以拥有自己的用户对象、表空间和数据文件范围,但数据库级资源、内存和后台进程仍受 CDB/Instance 级别影响。

连接到 PDB 后,查询当前容器:

SHOW CON_NAME;

SELECT sys_context('USERENV', 'CON_NAME') AS container_name
FROM   dual;

查询容器信息:

SELECT con_id,
       name,
       open_mode
FROM   v$containers
ORDER BY con_id;

因此,排查“某个数据库实例的内存”时,需要明确你说的是:

  • 整个 Instance;
  • CDB;
  • 某一个 PDB;
  • 某个会话或服务器进程。

PDB 级别的资源限制不能简单替代实例级资源管理。


十二、RAC:多个 Instance 访问一个 Database

单实例架构可以抽象为:

一个 Instance
  ├── 一个 SGA
  ├── 一组后台进程
  └── 一组服务器进程
           │
           ▼
      一个 Database 的文件

RAC 则是:

Instance 1 ── 本地 SGA/进程 ──┐
                              ├── 共享 Database 文件
Instance 2 ── 本地 SGA/进程 ──┘

每个实例都有自己的 Buffer Cache。一个实例可能需要访问另一个实例最近修改过的缓存块。RAC 使用 Cache Fusion 等机制在实例间传递缓存块和协调全局资源。

因此:

  • SGA 不是整个 RAC 集群共享的一块普通内存;
  • 每个 Instance 有自己的 SGA;
  • 数据文件、控制文件和日志通常位于集群可访问的存储;
  • 跨实例并发会引入全局缓存和全局锁协调成本。

单实例中看到的 Buffer Cache 命中和锁等待,不能直接推导 RAC 中的全局缓存行为。RAC 诊断通常还需要关注实例间通信、全局缓存等待和服务分布。


十三、用动态性能视图建立架构观察

Oracle 提供一组动态性能视图,通常以 V$ 开头。它们不是普通业务表,而是由实例运行状态提供的信息视图。

1. 查看实例状态

SELECT instance_name,
       host_name,
       version,
       status,
       database_status,
       instance_role
FROM   v$instance;

可能看到的信息包括:

  • 实例名;
  • 主机名;
  • 数据库版本;
  • 实例状态;
  • 数据库状态;
  • RAC 中的实例角色。

2. 查看数据库文件和日志

SELECT name
FROM   v$datafile
ORDER BY file#;

SELECT name
FROM   v$controlfile;

SELECT group#,
       thread#,
       sequence#,
       bytes,
       members,
       status
FROM   v$log
ORDER BY group#;

V$LOG 描述在线重做日志组状态。RAC 环境可能涉及多个 redo thread,单实例示例不能直接套用到所有集群配置。

查看日志成员:

SELECT group#,
       member,
       type,
       status
FROM   v$logfile
ORDER BY group#, member;

3. 查看会话与服务器进程关系

SELECT s.sid,
       s.serial#,
       s.username,
       s.server,
       s.status,
       p.spid,
       p.program
FROM   v$session s
LEFT JOIN v$process p
       ON p.addr = s.paddr
WHERE  s.type = 'USER';

这里可以观察:

  • V$SESSION 中的逻辑会话;
  • SERVER 显示连接模式信息;
  • V$PROCESS 中的 Oracle 进程;
  • SPID 是操作系统进程标识,但具体类型和可见性依赖平台与权限。

会话被杀掉后,资源清理可能不是瞬间完成。看到会话状态变化,不应立即推断所有事务、锁和 PGA 已同步清零。


十四、常见误解与失败表现

误解一:数据库就是数据文件

不完整。数据库还包括控制文件和在线重做日志等关键物理文件。数据文件丢失时可以尝试介质恢复;控制文件或在线重做日志损坏也会影响数据库可用性和恢复路径。

误解二:SGA 是每个会话私有内存

错误。SGA 是实例级共享内存。会话私有执行状态、排序和哈希工作区主要属于服务器进程的 PGA。

误解三:PGA 越大,所有 SQL 都越快

错误。PGA 主要帮助需要私有工作区的操作。SQL 是否使用排序、哈希、并行执行,首先由执行计划和数据规模决定。即使 PGA 足够大,错误的连接条件、缺失索引、过期统计信息或低选择性谓词仍可能导致高成本计划。

误解四:COMMIT 后 DBWn 已经写完数据文件

不成立。提交的关键持久性点是相关 Redo 达到要求的持久化状态。数据块可以稍后由 DBWn 写入数据文件。

误解五:Redo 和 Undo 是同一种日志

错误。Redo 面向恢复重做,Undo 面向回滚和一致性读。Undo 的变化本身也会产生 Redo,所以二者不是互斥关系。

误解六:检查点就是一次完整刷盘

不准确。检查点推进恢复位置并协调相关脏块写出,但其实现和范围不能简化为“所有缓存一次性写入所有数据文件”。

误解七:后台进程列表在所有版本都固定

错误。进程会因版本、操作系统、RAC、归档、备份、Flashback、Data Guard 和其他功能变化。排查问题时应结合当前版本和具体进程职责,而不是只背诵某一张旧架构图。

误解八:数据文件空间足够就不会出现空间错误

不一定。可能出现:

  • 表空间剩余空间不足;
  • 数据文件达到最大大小;
  • 临时表空间不足;
  • Undo 空间不足;
  • ASM 磁盘组或文件系统空间不足;
  • 单个段、区或块受到限制。

诊断空间问题时,应同时查看表空间、数据文件、临时文件、Undo 和底层存储。


十五、把架构映射到诊断路径

遇到性能或故障问题时,先根据现象定位层次,而不是直接调整参数。

1. SQL 解析或执行计划异常

重点观察:

  • Shared Pool 和游标;
  • SQL 是否使用绑定变量;
  • 对象权限和失效;
  • 优化器统计信息;
  • 执行计划和实际行数;
  • PGA 工作区是否溢出到临时表空间。

相关视图示例:

SELECT sql_id,
       executions,
       elapsed_time,
       cpu_time,
       buffer_gets,
       disk_reads,
       rows_processed
FROM   v$sql
ORDER BY elapsed_time DESC
FETCH FIRST 20 ROWS ONLY;

这只能提供实例当前缓存中的统计信息,不等价于完整历史数据;SQL 游标被淘汰后,相关统计可能不再可见。

2. I/O 等待明显

需要区分:

  • 数据文件读取:可能与 Buffer Cache 未命中或物理 I/O 有关;
  • 在线重做日志写入:可能与 LGWR、存储延迟或提交频率有关;
  • 临时文件 I/O:可能与 PGA 不足或执行计划中的排序、哈希溢出有关;
  • 归档日志写出:可能与归档目标和存储故障有关。

不能把所有 I/O 都归结为“Buffer Cache 太小”。

3. 提交延迟明显

应检查:

  • 在线重做日志写入延迟;
  • 日志文件所在存储;
  • 提交频率;
  • 日志组切换和归档是否受阻;
  • 是否存在高并发提交。

因为提交路径依赖 LGWR,盲目增加 Buffer Cache 通常不能解决 Redo 写延迟。

4. Undo 或读一致性错误

如果出现 ORA-01555 或 Undo 空间压力,应分析:

  • 长时间运行的查询;
  • Undo 保留策略;
  • DML 产生 Undo 的速率;
  • Undo 表空间大小和自动扩展;
  • 是否存在异常长事务。

仅仅增加 Buffer Cache 不会直接增加历史版本保存时间。

5. 实例异常停止后无法打开

需要区分:

  • Instance 是否已启动;
  • 数据库当前处于 NOMOUNT、MOUNT 还是 OPEN;
  • 控制文件是否可读;
  • 数据文件是否缺失或需要恢复;
  • 在线重做日志是否可用;
  • 是否正在进行实例恢复或介质恢复。

可以先查看:

SELECT status,
       database_status
FROM   v$instance;

然后结合数据库告警日志、操作系统日志、存储错误和恢复操作结果判断。恢复操作具有破坏性风险,尤其是重建控制文件、重置日志和不完整恢复,不能仅凭“数据库打不开”就直接执行。


十六、一个可复用的整体心智模型

可以把一次数据库修改压缩成下面这条数据流:

客户端
  ↓
会话 / 服务器进程
  ↓
PGA:会话状态、执行工作区
  ↓
Shared Pool:解析、游标、数据字典信息
  ↓
Buffer Cache:读取和修改数据库块
  ├── Undo:旧版本、回滚信息
  └── Redo Log Buffer:变化记录
          ↓
        LGWR
          ↓
在线重做日志
          
Buffer Cache 中的 dirty buffer
          ↓
        DBWn
          ↓
数据文件

同时:

CKPT:推进检查点
SMON:实例恢复和系统监控
PMON:异常进程清理
ARCn:归档在线重做日志
其他后台进程:监控、注册、分布式事务、Flashback 等功能

这套模型能够解释几个关键事实:

  1. Instance 是运行时环境,不是磁盘上的数据库文件。
  2. SGA 负责实例级共享,PGA 主要服务于服务器进程私有执行。
  3. 数据文件保存数据库块,但不是所有变化都先写数据文件。
  4. LGWR 负责 Redo 持久化,DBWn 负责脏数据块写出。
  5. Redo 支持恢复,Undo 支持回滚和读一致性。
  6. 检查点缩短恢复路径,但不改变事务提交的基本语义。
  7. 后台进程的具体数量和职责边界随版本与功能变化。
  8. 在 CDB/PDB 和 RAC 中,Instance、Database、容器与节点的边界会进一步分层。

当 SQL 执行、事务提交、实例恢复、空间管理和性能诊断都能映射回这条数据流时,Oracle 的内存、进程和文件架构就不再只是组件名称,而成为可以用于解释实际行为的运行模型。


系列导航与关联阅读

官方资料

本文依据数据库官方文档重新梳理;正文、示例与生产检查清单由 WR BLOG 编写。

评论

0 条讨论
0/1000
还没有评论,来聊聊你的看法
开头。它们不是普通业务表,而是由实例运行状态提供的信息视图。\n\n### 1. 查看实例状态\n\n```sql\nSELECT instance_name,\n host_name,\n version,\n status,\n database_status,\n instance_role\nFROM v$instance;\n```\n\n可能看到的信息包括:\n\n- 实例名;\n- 主机名;\n- 数据库版本;\n- 实例状态;\n- 数据库状态;\n- RAC 中的实例角色。\n\n### 2. 查看数据库文件和日志\n\n```sql\nSELECT name\nFROM v$datafile\nORDER BY file#;\n\nSELECT name\nFROM v$controlfile;\n\nSELECT group#,\n thread#,\n sequence#,\n bytes,\n members,\n status\nFROM v$log\nORDER BY group#;\n```\n\n`V$LOG` 描述在线重做日志组状态。RAC 环境可能涉及多个 redo thread,单实例示例不能直接套用到所有集群配置。\n\n查看日志成员:\n\n```sql\nSELECT group#,\n member,\n type,\n status\nFROM v$logfile\nORDER BY group#, member;\n```\n\n### 3. 查看会话与服务器进程关系\n\n```sql\nSELECT s.sid,\n s.serial#,\n s.username,\n s.server,\n s.status,\n p.spid,\n p.program\nFROM v$session s\nLEFT JOIN v$process p\n ON p.addr = s.paddr\nWHERE s.type = 'USER';\n```\n\n这里可以观察:\n\n- `V$SESSION` 中的逻辑会话;\n- `SERVER` 显示连接模式信息;\n- `V$PROCESS` 中的 Oracle 进程;\n- `SPID` 是操作系统进程标识,但具体类型和可见性依赖平台与权限。\n\n会话被杀掉后,资源清理可能不是瞬间完成。看到会话状态变化,不应立即推断所有事务、锁和 PGA 已同步清零。\n\n---\n\n## 十四、常见误解与失败表现\n\n### 误解一:数据库就是数据文件\n\n不完整。数据库还包括控制文件和在线重做日志等关键物理文件。数据文件丢失时可以尝试介质恢复;控制文件或在线重做日志损坏也会影响数据库可用性和恢复路径。\n\n### 误解二:SGA 是每个会话私有内存\n\n错误。SGA 是实例级共享内存。会话私有执行状态、排序和哈希工作区主要属于服务器进程的 PGA。\n\n### 误解三:PGA 越大,所有 SQL 都越快\n\n错误。PGA 主要帮助需要私有工作区的操作。SQL 是否使用排序、哈希、并行执行,首先由执行计划和数据规模决定。即使 PGA 足够大,错误的连接条件、缺失索引、过期统计信息或低选择性谓词仍可能导致高成本计划。\n\n### 误解四:COMMIT 后 DBWn 已经写完数据文件\n\n不成立。提交的关键持久性点是相关 Redo 达到要求的持久化状态。数据块可以稍后由 DBWn 写入数据文件。\n\n### 误解五:Redo 和 Undo 是同一种日志\n\n错误。Redo 面向恢复重做,Undo 面向回滚和一致性读。Undo 的变化本身也会产生 Redo,所以二者不是互斥关系。\n\n### 误解六:检查点就是一次完整刷盘\n\n不准确。检查点推进恢复位置并协调相关脏块写出,但其实现和范围不能简化为“所有缓存一次性写入所有数据文件”。\n\n### 误解七:后台进程列表在所有版本都固定\n\n错误。进程会因版本、操作系统、RAC、归档、备份、Flashback、Data Guard 和其他功能变化。排查问题时应结合当前版本和具体进程职责,而不是只背诵某一张旧架构图。\n\n### 误解八:数据文件空间足够就不会出现空间错误\n\n不一定。可能出现:\n\n- 表空间剩余空间不足;\n- 数据文件达到最大大小;\n- 临时表空间不足;\n- Undo 空间不足;\n- ASM 磁盘组或文件系统空间不足;\n- 单个段、区或块受到限制。\n\n诊断空间问题时,应同时查看表空间、数据文件、临时文件、Undo 和底层存储。\n\n---\n\n## 十五、把架构映射到诊断路径\n\n遇到性能或故障问题时,先根据现象定位层次,而不是直接调整参数。\n\n### 1. SQL 解析或执行计划异常\n\n重点观察:\n\n- Shared Pool 和游标;\n- SQL 是否使用绑定变量;\n- 对象权限和失效;\n- 优化器统计信息;\n- 执行计划和实际行数;\n- PGA 工作区是否溢出到临时表空间。\n\n相关视图示例:\n\n```sql\nSELECT sql_id,\n executions,\n elapsed_time,\n cpu_time,\n buffer_gets,\n disk_reads,\n rows_processed\nFROM v$sql\nORDER BY elapsed_time DESC\nFETCH FIRST 20 ROWS ONLY;\n```\n\n这只能提供实例当前缓存中的统计信息,不等价于完整历史数据;SQL 游标被淘汰后,相关统计可能不再可见。\n\n### 2. I/O 等待明显\n\n需要区分:\n\n- 数据文件读取:可能与 Buffer Cache 未命中或物理 I/O 有关;\n- 在线重做日志写入:可能与 LGWR、存储延迟或提交频率有关;\n- 临时文件 I/O:可能与 PGA 不足或执行计划中的排序、哈希溢出有关;\n- 归档日志写出:可能与归档目标和存储故障有关。\n\n不能把所有 I/O 都归结为“Buffer Cache 太小”。\n\n### 3. 提交延迟明显\n\n应检查:\n\n- 在线重做日志写入延迟;\n- 日志文件所在存储;\n- 提交频率;\n- 日志组切换和归档是否受阻;\n- 是否存在高并发提交。\n\n因为提交路径依赖 LGWR,盲目增加 Buffer Cache 通常不能解决 Redo 写延迟。\n\n### 4. Undo 或读一致性错误\n\n如果出现 `ORA-01555` 或 Undo 空间压力,应分析:\n\n- 长时间运行的查询;\n- Undo 保留策略;\n- DML 产生 Undo 的速率;\n- Undo 表空间大小和自动扩展;\n- 是否存在异常长事务。\n\n仅仅增加 Buffer Cache 不会直接增加历史版本保存时间。\n\n### 5. 实例异常停止后无法打开\n\n需要区分:\n\n- Instance 是否已启动;\n- 数据库当前处于 NOMOUNT、MOUNT 还是 OPEN;\n- 控制文件是否可读;\n- 数据文件是否缺失或需要恢复;\n- 在线重做日志是否可用;\n- 是否正在进行实例恢复或介质恢复。\n\n可以先查看:\n\n```sql\nSELECT status,\n database_status\nFROM v$instance;\n```\n\n然后结合数据库告警日志、操作系统日志、存储错误和恢复操作结果判断。恢复操作具有破坏性风险,尤其是重建控制文件、重置日志和不完整恢复,不能仅凭“数据库打不开”就直接执行。\n\n---\n\n## 十六、一个可复用的整体心智模型\n\n可以把一次数据库修改压缩成下面这条数据流:\n\n```text\n客户端\n ↓\n会话 / 服务器进程\n ↓\nPGA:会话状态、执行工作区\n ↓\nShared Pool:解析、游标、数据字典信息\n ↓\nBuffer Cache:读取和修改数据库块\n ├── Undo:旧版本、回滚信息\n └── Redo Log Buffer:变化记录\n ↓\n LGWR\n ↓\n在线重做日志\n \nBuffer Cache 中的 dirty buffer\n ↓\n DBWn\n ↓\n数据文件\n```\n\n同时:\n\n```text\nCKPT:推进检查点\nSMON:实例恢复和系统监控\nPMON:异常进程清理\nARCn:归档在线重做日志\n其他后台进程:监控、注册、分布式事务、Flashback 等功能\n```\n\n这套模型能够解释几个关键事实:\n\n1. **Instance 是运行时环境,不是磁盘上的数据库文件。**\n2. **SGA 负责实例级共享,PGA 主要服务于服务器进程私有执行。**\n3. **数据文件保存数据库块,但不是所有变化都先写数据文件。**\n4. **LGWR 负责 Redo 持久化,DBWn 负责脏数据块写出。**\n5. **Redo 支持恢复,Undo 支持回滚和读一致性。**\n6. **检查点缩短恢复路径,但不改变事务提交的基本语义。**\n7. **后台进程的具体数量和职责边界随版本与功能变化。**\n8. **在 CDB/PDB 和 RAC 中,Instance、Database、容器与节点的边界会进一步分层。**\n\n当 SQL 执行、事务提交、实例恢复、空间管理和性能诊断都能映射回这条数据流时,Oracle 的内存、进程和文件架构就不再只是组件名称,而成为可以用于解释实际行为的运行模型。\n\n---\n\n## 系列导航与关联阅读\n\n- 系列入口:[数据库完整学习路线:从关系模型、事务索引到分布式与向量检索](https://wrblog.cn/articles/e04c40d6-ba22-5c0c-8442-2252df05d216)\n- 上一篇:[PostgreSQL JSONB 与扩展生态:全文检索、分区、FDW 和扩展边界](https://wrblog.cn/articles/0cc1aa8f-095b-5caf-bf28-7a54ea7a1325)\n- 下一篇:[Oracle SQL 与 PL/SQL:数据类型、包、过程、异常和批处理](https://wrblog.cn/articles/644d8825-6f08-5858-a2c0-e1e74c7e1d05)\n- 延伸:[Oracle 事务与 Undo:读一致性、SCN、锁、Redo 和恢复机制](https://wrblog.cn/articles/279768cc-1349-533a-a8ff-36255003b028)\n- 延伸:[Oracle 索引与优化器:统计信息、执行计划、Hint 和 SQL 调优](https://wrblog.cn/articles/12c91cb6-fc93-5b85-a4ec-1a8a1d8fd239)\n\n## 官方资料\n\n- [Oracle Database Documentation](https://docs.oracle.com/en/database/oracle/oracle-database/)\n- [Oracle Database Concepts](https://docs.oracle.com/en/database/oracle/oracle-database/23/cncpt/)\n\n> 本文依据数据库官方文档重新梳理;正文、示例与生产检查清单由 WR BLOG 编写。\n","tags":["数据库","Oracle","架构","后端"],"likeCount":0,"commentCount":0,"createdByUserId":"10000000000","createdByDisplayName":"小郝","createdByAvatar":"/public/profile/10000000000/avatar/2026/08/04/db02b81c-42f2-441b-8a80-61370cdbb581.webp","publishTime":"2026-09-01 14:13:19","updateTime":"2026-09-01 14:13:19"}},"status":200,"locale":"zh-CN","theme":"light"}