Oracle 检查点(Checkpoint)深度解析
一、检查点是什么
检查点(Checkpoint) :一种同步机制 ,确保 Buffer Cache 中的脏块 被DBWR 写入数据文件,并把这个一致性时间点记录到数据文件头和控制文件中。
核心目的
1. 缩短崩溃恢复时间 (Crash Recovery)
2. 让 Redo Log 可以被覆盖重用
3. 保证数据文件的"一致性时间点"标记
二、为什么需要检查点
Oracle 的写入策略:延迟写
用户提交事务
↓
① Redo Log Buffer → Redo Log File (LGWR 立即同步)
↓
② Buffer Cache 中的数据块标记为"脏块"(Dirty Block)
↓
③ 提交完成,返回客户端 ✅
↓
(异步,过一会儿)
↓
④ DBWR 把脏块写入数据文件 ← Checkpoint 触发
关键洞察:
- Redo Log 是同步写(保证不丢数据)
- 数据文件是异步写(提高性能)
没有检查点会发生什么?
如果不强制把脏块写入数据文件:
问题 1: 崩溃恢复时,需要从"无穷远"的过去 Redo Log 开始重放,恢复时间极长
问题 2: Redo Log 无法循环重用,磁盘空间无限增长
问题 3: 数据文件永远落后于实际数据,不一致
三、检查点的内部机制
涉及的核心后台进程
┌─────────────────────────────────────────────────┐
│ Oracle Instance │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ CKPT │ → │ DBWR │ → │ LGWR │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ 检查点进程 写脏块进程 写日志进程 │
│ │ │ │ │
│ ↓ ↓ ↓ │
│ 更新控制文件 写入数据文件 写入日志文件 │
│ 和数据文件头 │
└─────────────────────────────────────────────────┘
| 进程 | 职责 |
|---|---|
| CKPT | 触发检查点,更新控制文件和数据文件头 (不写脏块) |
| DBWR | 把 Buffer Cache 中的脏块写入数据文件 |
| LGWR | 把 Redo Log Buffer 写入 Redo Log 文件 |
⚠️ 常见误解 :CKPT 进程不写数据 !它只是通知 DBWR 写脏块 ,并更新控制文件。
四、SCN:检查点的核心标识
SCN(System Change Number):Oracle 内部全局递增的时钟,标识数据库的每个状态变化。
SCN = 1000 → 事务 A 提交
SCN = 1001 → 事务 B 提交
SCN = 1002 → Checkpoint 完成 ← 记录到控制文件
SCN = 1003 → 事务 C 提交
SCN = 1004 → Checkpoint 完成
检查点的几个关键 SCN
| SCN 类型 | 含义 |
|---|---|
| System Checkpoint SCN | 数据库级别检查点 SCN,记录在控制文件 |
| Datafile Checkpoint SCN | 每个数据文件的检查点 SCN,记录在控制文件 |
| Start SCN | 数据文件头记录的 SCN,启动时校验 |
| Stop SCN | 数据库关闭时记录的 SCN |
| Checkpoint Progress SCN | 当前正在进行的检查点位置 |
查看 SCN
sql
-- 当前数据库 SCN
SELECT CURRENT_SCN FROM V$DATABASE;
-- 系统检查点 SCN
SELECT CHECKPOINT_CHANGE# FROM V$DATABASE;
-- 各数据文件检查点 SCN
SELECT FILE#, NAME, CHECKPOINT_CHANGE#
FROM V$DATAFILE
ORDER BY FILE#;
-- 数据文件头的 Start SCN
SELECT FILE#, NAME, CHECKPOINT_CHANGE#
FROM V$DATAFILE_HEADER;
启动数据库时,三个 SCN 必须全部一致才能正常打开:
控制文件中的 System Checkpoint SCN
===
控制文件中的 Datafile Checkpoint SCN
===
数据文件头的 Checkpoint SCN
否则会触发实例恢复(Instance Recovery)或介质恢复(Media Recovery)。
五、检查点类型
Oracle 有多种检查点,触发条件和影响范围不同:
1. 完全检查点(Full Checkpoint)
触发:
ALTER SYSTEM CHECKPOINT- 数据库正常关闭(
SHUTDOWN NORMAL/IMMEDIATE) ALTER DATABASE BEGIN BACKUP(10g 前)
行为 :把 Buffer Cache 所有脏块写入数据文件。
sql
-- 手动触发完全检查点
ALTER SYSTEM CHECKPOINT;
2. 增量检查点(Incremental Checkpoint)⭐ 默认机制
触发 :自 Oracle 8i 起的默认机制 ,由 CKPT 进程持续后台运行。
行为:
- 不是一次性写完所有脏块
- 而是持续地、增量地推进检查点位置
- 用一个"RBA(Redo Byte Address)"指针追踪进度
优势:
- ✅ 避免完全检查点带来的性能尖峰(IO 抖动)
- ✅ 平滑写入,IO 负载稳定
3. 日志切换检查点(Log Switch Checkpoint)
触发 :Redo Log 文件切换时(ALTER SYSTEM SWITCH LOGFILE)。
行为:写入被切换的日志组所"保护"的所有脏块。
sql
-- 手动切换日志
ALTER SYSTEM SWITCH LOGFILE;
4. 表空间检查点(Tablespace Checkpoint)
触发:
ALTER TABLESPACE ... OFFLINE NORMALALTER TABLESPACE ... BEGIN BACKUPALTER TABLESPACE ... READ ONLY
行为:只写该表空间相关的脏块。
5. 对象检查点(Object Checkpoint)
触发:
DROP TABLETRUNCATE TABLE- 索引重建等
行为:只写涉及该对象的脏块。
6. 并行查询检查点(Parallel Query Checkpoint)
触发:并行直接路径读取(Direct Path Read)前。
原因 :直接路径读取绕过 Buffer Cache,必须先把相关脏块刷盘。
六、控制检查点的关键参数
1. FAST_START_MTTR_TARGET(首选)⭐
含义:期望的最大崩溃恢复时间(秒)。
sql
-- 设置崩溃恢复目标为 300 秒
ALTER SYSTEM SET FAST_START_MTTR_TARGET = 300;
- 值越小,检查点越频繁,IO 越多,但恢复越快
- 值越大,检查点越稀疏,IO 少,但恢复时间长
- 推荐值:60 ~ 300 秒
sql
-- 查看实际效果(MTTR 估算)
SELECT TARGET_MTTR, ESTIMATED_MTTR,
CKPT_BLOCK_WRITES, LOG_FILE_SIZE_REDO_BLKS
FROM V$INSTANCE_RECOVERY;
2. LOG_CHECKPOINT_TIMEOUT(旧参数)
含义:检查点最大间隔时间(秒),0 表示禁用。
sql
-- 1800 秒检查点一次
ALTER SYSTEM SET LOG_CHECKPOINT_TIMEOUT = 1800;
-- 0 = 禁用基于时间的检查点
3. LOG_CHECKPOINT_INTERVAL(旧参数)
含义:每多少 Redo Log 块触发一次检查点,0 表示禁用。
sql
-- 每写 10000 个 OS 块(每块 512 字节)后触发
ALTER SYSTEM SET LOG_CHECKPOINT_INTERVAL = 10000;
4. LOG_CHECKPOINTS_TO_ALERT
含义:是否把检查点信息写入 alert.log。
sql
-- 启用,便于监控
ALTER SYSTEM SET LOG_CHECKPOINTS_TO_ALERT = TRUE;
七、监控检查点
1. V$INSTANCE_RECOVERY(最重要)⭐
sql
SELECT
RECOVERY_ESTIMATED_IOS AS "恢复需 IO 次数",
ACTUAL_REDO_BLKS AS "实际 Redo 块数",
TARGET_REDO_BLKS AS "目标 Redo 块数",
LOG_FILE_SIZE_REDO_BLKS AS "日志文件大小(块)",
LOG_CHKPT_TIMEOUT_REDO_BLKS AS "超时阈值",
LOG_CHKPT_INTERVAL_REDO_BLKS AS "间隔阈值",
FAST_START_IO_TARGET_REDO_BLKS AS "FAST_START_MTTR 阈值",
TARGET_MTTR AS "目标恢复时间(秒)",
ESTIMATED_MTTR AS "预估恢复时间(秒)",
CKPT_BLOCK_WRITES AS "检查点写入次数"
FROM V$INSTANCE_RECOVERY;
2. alert.log 中的检查点信息
启用 LOG_CHECKPOINTS_TO_ALERT=TRUE 后,告警日志会出现:
Thu Apr 29 14:30:00 2026
Beginning log switch checkpoint up to RBA [0x35.2.10], SCN: 12345678
Thread 1 advanced to log sequence 53
Current log# 2 seq# 53 mem# 0: /u01/oradata/orcl/redo02.log
Completed checkpoint up to RBA [0x35.2.10], SCN: 12345678
3. 等待事件诊断
sql
-- 检查点相关的等待事件
SELECT EVENT, TOTAL_WAITS, TIME_WAITED, AVERAGE_WAIT
FROM V$SYSTEM_EVENT
WHERE EVENT IN (
'log file switch (checkpoint incomplete)', -- 严重!
'checkpoint completed',
'write complete waits',
'free buffer waits',
'log file switch completion'
)
ORDER BY TIME_WAITED DESC;
关键等待事件:
| 等待事件 | 含义 | 处理 |
|---|---|---|
| log file switch (checkpoint incomplete) | 切换日志时上次检查点未完成,阻塞写入 | 增大日志文件、加快检查点 |
| write complete waits | 等待 DBWR 完成写入 | DBWR 性能瓶颈 |
| free buffer waits | 等待空闲缓冲区 | 加大 Buffer Cache 或更频繁检查点 |
4. 监控检查点进度
sql
SELECT
FILE#,
CHECKPOINT_CHANGE#,
CHECKPOINT_TIME,
LAST_CHANGE#
FROM V$DATAFILE
ORDER BY CHECKPOINT_TIME DESC;
八、检查点与崩溃恢复
崩溃恢复流程
数据库崩溃
↓
重启实例 (STARTUP)
↓
读取控制文件中的 Checkpoint SCN (例如 SCN=1000)
↓
扫描 Redo Log,从 SCN=1000 之后的所有 Redo
↓
前滚 (Roll Forward): 重放所有 Redo,把数据库恢复到崩溃时刻
↓
打开数据库 (OPEN)
↓
回滚 (Roll Back): 利用 Undo 回滚未提交的事务
↓
恢复完成,可用
检查点频率与恢复时间
检查点频繁 → Redo Log 中"未应用"的部分少 → 恢复快 → IO 多
检查点稀疏 → Redo Log 中"未应用"的部分多 → 恢复慢 → IO 少
配置建议
sql
-- 期望最长恢复时间 60 秒
ALTER SYSTEM SET FAST_START_MTTR_TARGET = 60;
-- 关闭老参数,避免冲突
ALTER SYSTEM SET LOG_CHECKPOINT_TIMEOUT = 0;
ALTER SYSTEM SET LOG_CHECKPOINT_INTERVAL = 0;
-- 启用 alert 监控
ALTER SYSTEM SET LOG_CHECKPOINTS_TO_ALERT = TRUE;
九、常见问题与调优
问题 1:log file switch (checkpoint incomplete) 严重
现象:
Thread 1 cannot allocate new log, sequence 100
Checkpoint not complete
原因:
Redo Log 写满,要切换到下一组
↓
但下一组的脏块还没被 DBWR 写完(检查点未完成)
↓
LGWR 被迫等待,业务事务也被阻塞 ❌
解决:
sql
-- 方案 1: 增大 Redo Log 文件大小(推荐)
-- 让单个日志能撑更久,给检查点更多时间
ALTER DATABASE ADD LOGFILE GROUP 4
('/u01/oradata/orcl/redo04.log') SIZE 2G;
-- 方案 2: 增加 Redo Log 组数
-- 从 3 组增加到 5 组
-- 方案 3: 加快检查点(更频繁,平滑写入)
ALTER SYSTEM SET FAST_START_MTTR_TARGET = 60;
-- 方案 4: 提升 DBWR 性能(增加 DBWR 进程数)
ALTER SYSTEM SET DB_WRITER_PROCESSES = 4 SCOPE=SPFILE;
-- 需要重启
-- 方案 5: 优化 IO 性能(放在更快的存储)
问题 2:检查点导致的 IO 抖动
现象:业务平稳运行时,每隔一段时间 IO 突增。
解决:
- 使用
FAST_START_MTTR_TARGET(默认增量检查点) - 不要使用老的
LOG_CHECKPOINT_INTERVAL与TIMEOUT
问题 3:DBWR 写入慢
sql
-- 检查 DBWR 是否成为瓶颈
SELECT EVENT, TOTAL_WAITS, TIME_WAITED
FROM V$SYSTEM_EVENT
WHERE EVENT LIKE 'db file%write%'
OR EVENT IN ('free buffer waits', 'write complete waits');
优化:
- 增加
DB_WRITER_PROCESSES(默认按 CPU 核数自动计算) - 使用异步 IO(
DISK_ASYNCH_IO=TRUE) - 优化存储 IO 性能
问题 4:实例恢复时间过长
sql
-- 测量恢复时间
SELECT ESTIMATED_MTTR, TARGET_MTTR
FROM V$INSTANCE_RECOVERY;
-- 如果预估值 > 业务可接受值,调小 FAST_START_MTTR_TARGET
十、检查点全流程图解
事务执行阶段:
─────────────────────────────────────────────────────
T1 INSERT → Redo Buffer → Buffer Cache(脏块)
T2 UPDATE → Redo Buffer → Buffer Cache(脏块)
T1 COMMIT → LGWR 同步写 Redo Log ✅ 提交完成
─────────────────────────────────────────────────────
检查点触发:
─────────────────────────────────────────────────────
① CKPT 进程发起检查点(基于 FAST_START_MTTR_TARGET)
↓
② DBWR 把 Buffer Cache 的脏块写入数据文件
↓
③ DBWR 完成后通知 CKPT
↓
④ CKPT 更新控制文件: 记录新的 Checkpoint SCN
↓
⑤ CKPT 更新所有数据文件头: 记录新的 Checkpoint SCN
↓
⑥ 检查点完成,这之前的 Redo 可以被覆盖重用
─────────────────────────────────────────────────────
崩溃恢复:
─────────────────────────────────────────────────────
实例崩溃 → 重启
↓
读取控制文件的 Checkpoint SCN = X
↓
扫描 Redo Log 中 SCN > X 的所有记录
↓
前滚: 重放 Redo 到崩溃时刻
↓
打开数据库
↓
回滚: 用 Undo 回滚未提交事务
↓
恢复完成
─────────────────────────────────────────────────────
十一、检查点与 RAC
在 RAC 环境中,检查点机制略有不同:
全局检查点
- 每个实例独立进行检查点
- 但 GCS(Global Cache Service) 协调跨节点的脏块所有权
- 通过 PI(Past Image) 机制保证一致性
关键差异
sql
-- RAC 中查看各节点检查点
SELECT INST_ID, CHECKPOINT_CHANGE#
FROM GV$DATABASE
ORDER BY INST_ID;
-- 各节点恢复时间
SELECT INST_ID, ESTIMATED_MTTR, TARGET_MTTR
FROM GV$INSTANCE_RECOVERY;
十二、检查点 vs 提交(重要区分)
| 概念 | 触发者 | 作用对象 | 频率 |
|---|---|---|---|
| COMMIT | 用户事务 | Redo Log Buffer → Redo Log File | 每个事务 |
| Checkpoint | CKPT 进程 | Buffer Cache 脏块 → Data File | 周期性 |
COMMIT ≠ Checkpoint
持久化保证 一致性时间点标记
提交时:
- Redo Log 立即落盘 ✅
- 数据文件 不一定落盘 ❌(依赖 Checkpoint)
只要 Redo Log 安全,数据就不会丢:
即使数据文件没更新,崩溃后重放 Redo 也能恢复
十三、面试常见问题
Q1: 为什么 COMMIT 不直接写数据文件?
A:性能。Redo Log 是顺序写(快),数据文件是随机写(慢)。先快速持久化 Redo,再异步批量写数据文件,整体性能更好。
Q2: 增量检查点 vs 完全检查点?
A:
- 增量检查点:持续平滑写入,默认机制,避免 IO 抖动
- 完全检查点:一次性把所有脏块写完,只在关闭数据库等特殊场景
Q3: 检查点完成的标志是什么?
A:控制文件的 Checkpoint SCN 和所有数据文件头的 Checkpoint SCN 全部更新一致。
Q4: FAST_START_MTTR_TARGET 设小好还是大好?
A :根据业务对崩溃恢复时间的容忍度。一般 OLTP 建议 60~300 秒。设置过小会导致检查点过于频繁,增加 IO 负担。
Q5: 什么是检查点 RBA?
A:Redo Byte Address,标识 Redo Log 中的位置。检查点 RBA 表示"这个位置之前的脏块都已经写入数据文件"。
一句话总结
检查点是 Oracle 在性能(异步写)和安全(崩溃可恢复)之间的平衡机制:
- 作用:把 Buffer Cache 的脏块同步到数据文件,并记录一致性时间点(SCN)到控制文件和数据文件头
- 目的:① 缩短崩溃恢复时间;② 让 Redo Log 可循环重用;③ 维持数据文件一致性
- 机制 :默认是增量检查点,由 CKPT 持续后台推进;DBWR 真正写脏块,CKPT 只更新元数据
- 关键参数 :
FAST_START_MTTR_TARGET(推荐 60~300 秒)控制检查点频率- 核心监控 :关注
log file switch (checkpoint incomplete)等待事件,出现就要调大 Redo Log 或加快检查点记住核心公式:
检查点 = 脏块刷盘 + SCN 同步标记,COMMIT 只保证 Redo 落盘,数据落盘靠检查点。