Oracle Checkpoint 机制——增量检查点、MTTR 与恢复时间的平衡术

Checkpoint 是 Oracle 里最容易被误解的机制。很多 DBA 以为 Checkpoint 就是"把脏块写盘",甚至有人说"Checkpoint 越频繁,数据库性能越好,因为脏块及时写盘"。这话前半句对,后半句错得离谱。Checkpoint 的本质不是写脏块,而是缩短崩溃恢复时间。写脏块只是手段,控制恢复时间(MTTR)才是目的。这篇文章把 Checkpoint 的触发逻辑、增量检查点、DBWn 与 LGWR 的协作,以及 FAST_START_MTTR_TARGET 的调参陷阱拆清楚。

1 Checkpoint 不是写脏块,是推进恢复起点

Oracle 崩溃恢复时,Recovery 进程需要读取 redo 日志,从某个起点开始应用 redo,直到最新状态。这个起点就是 Checkpoint SCN——它表示"在这个 SCN 之前的所有脏块,都已经写到了数据文件"。所以 Recovery 只需要应用 Checkpoint SCN 之后的 redo,之前的 redo 不需要管(因为数据文件里已经有了)。Checkpoint 的频率决定了 Checkpoint SCN 和最新 SCN 之间的差距,差距越小,崩溃时需要恢复的 redo 越少,恢复时间越短。但 Checkpoint 本身有开销:DBWn 写脏块会占用 I/O 带宽,如果 Checkpoint 太频繁,业务 I/O 被挤占,性能反而下降。所以 Checkpoint 是"恢复时间"和"运行性能"的 trade-off。

从数据字典看,Checkpoint 信息存在控制文件和数据文件头里。v$thread 的 checkpoint_change# 是控制文件记录的线程级 Checkpoint SCN;v$datafile_header 的 checkpoint_change# 是每个数据文件自己的 Checkpoint SCN。正常情况下,所有数据文件的 Checkpoint SCN 应该一致(或接近),如果某个数据文件的 Checkpoint SCN 明显落后,说明 DBWn 写这个文件时出了问题,或者文件被离线过。

2 完全检查点 vs 增量检查点:别再把两者混为一谈

Oracle 有两种 Checkpoint:完全检查点(Full Checkpoint):把所有脏块写盘,更新所有数据文件头的 Checkpoint SCN。触发条件:ALTER SYSTEM CHECKPOINT、数据库关闭(SHUTDOWN IMMEDIATE/TRANSACTIONAL)、日志切换(ALTER SYSTEM SWITCH LOGFILE,但 10g 以后日志切换只触发增量检查点,不是完全的)。完全检查点的开销极大,因为所有脏块一起写,I/O 峰值很高。生产环境尽量避免手动触发完全检查点。

增量检查点(Incremental Checkpoint):只写一部分脏块,通常是 Buffer Cache 里最老的脏块(LRU 链的冷端)。增量检查点由 DBWn 的后台写完成,不是一次性写完,而是持续地、平滑地把脏块刷盘。增量检查点的触发条件:FAST_START_MTTR_TARGET 达到阈值(比如设了 300 秒,Oracle 计算当前脏块量,如果按当前写速率需要 400 秒才能写完,就加速 DBWn);Redo Log 切换(虽然不完全,但会触发一次增量写);Buffer Cache 的脏块比例超过阈值(默认 25%)。增量检查点的优点是平滑,不会导致 I/O 尖峰;缺点是恢复时间不如完全检查点可控,因为脏块是分散写的。

这里有个常见误区:很多 DBA 看到 AWR 里 checkpoint completed 等待高,就以为是 Checkpoint 太慢,于是加大 DBWR 进程数。但实际上 checkpoint completed 等待高,通常是因为 LGWR 写日志太快,redo 日志切换频繁,每次切换都触发增量检查点,DBWn 跟不上。解决办法不是加 DBWR,而是加大 redo 日志文件大小,减少日志切换频率。我们给一个客户调优时,redo 日志从 500MB 加到 2GB,checkpoint completed 等待从 15% 降到 2%,而 DBWR 进程数一个没加。这说明了"对症下药"的重要性。

3 FAST_START_MTTR_TARGET:自动计算的魔法与幻觉

FAST_START_MTTR_TARGET 是 Oracle 推荐的参数,表示"期望的实例恢复时间(秒)"。比如设成 300,Oracle 承诺"如果实例现在崩溃,恢复时间不超过 5 分钟"。这个承诺是怎么实现的?Oracle 内部有一个 MTTR Advisory,它根据历史统计信息,估算当前脏块数量和 DBWn 写速率,计算出"以当前速率,写完所有脏块需要多久"。如果计算结果 > MTTR_TARGET,Oracle 就加速 DBWn(增加写频率或启动更多 DBWR 从进程);如果 < MTTR_TARGET,就减速 DBWn,节省 I/O。听起来很智能,但实际有三个坑。

坑一:MTTR Advisory 的估算基于历史平均,不适用于突发负载。比如平常脏块写速率 100MB/s,但大促期间业务 I/O 暴增,DBWn 能抢到的 I/O 带宽降到 30MB/s,MTTR Advisory 还按 100MB/s 估算,结果实际恢复时间远超 300 秒。坑二:FAST_START_MTTR_TARGET 和 LOG_CHECKPOINT_INTERVAL 同时存在时,Oracle 取两者中更严格的那个。很多 DBA 只改了 MTTR_TARGET,没删 LOG_CHECKPOINT_INTERVAL,结果 Checkpoint 行为还是受旧参数控制,MTTR_TARGET 形同虚设。坑三:MTTR_TARGET 设得太低(比如 60 秒),DBWn 会疯狂写脏块,I/O 饱和,业务性能暴跌。我们见过一个客户设了 MTTR_TARGET=60,结果 DBWn 占了 70% 的 I/O 带宽,业务查询从 2 秒变成 20 秒。改成 300 秒后,世界清净了。

4 实验:MTTR 调优对 I/O 和恢复时间的影响

实验环境:Oracle 19c,单实例,Buffer Cache 16GB,SATA 盘(150MB/s 顺序写)。测试方法:用 Swingbench 跑 OLTP 负载 30 分钟,然后 SHUTDOWN ABORT 模拟崩溃,记录启动恢复时间。对比 FAST_START_MTTR_TARGET = 60、300、900 三个档位。

-- 调整 MTTR
ALTER SYSTEM SET FAST_START_MTTR_TARGET = 300 SCOPE = BOTH;

-- 查看当前脏块数量和预估恢复时间
SELECT target_mttr, estimated_mttr, adaptive_flush
FROM   v$instance_recovery;
-- target_mttr: 300(目标)
-- estimated_mttr: 285(估算,接近目标)
-- adaptive_flush: ON(自适应刷新开启)

-- 查看 DBWn 的写速率
SELECT name, value
FROM   v$sysstat WHERE name LIKE 'DBWR%';
-- DBWR checkpoint buffers written: 1250000
-- DBWR transaction table writes: 45000

-- 模拟崩溃恢复
SHUTDOWN ABORT;
STARTUP;
-- 观察 alert log 里的恢复时间
-- Instance recovery: looking for dead threads
-- Instance recovery: lock 1 acquired
-- Recovery of Online Redo Log: Thread 1 Group 1 ...
-- Recovery completed: elapsed time 00:04:32

实验结果整理:MTTR=60 时,运行期 DBWn 写盘速率 120MB/s(占磁盘带宽 80%),业务 TPS 下降 35%,崩溃恢复时间 3 分 15 秒(比目标快,因为平常写得多)。MTTR=300 时,DBWn 写盘速率 45MB/s(占 30%),业务 TPS 几乎无影响,恢复时间 4 分 28 秒(接近目标)。MTTR=900 时,DBWn 写盘速率 15MB/s(占 10%),业务性能最好,但恢复时间 12 分 10 秒(远超目标,因为脏块积累太多)。甜点区在 300-600 秒:既不会过度消耗 I/O,也不会让恢复时间太长。对于核心业务(要求 RTO < 10 分钟),设 300 秒;对于非核心业务,设 600-900 秒,省 I/O。

【踩坑笔记】FAST_START_MTTR_TARGET 设太低(<120 秒)是生产环境常见的自杀式调优。除非你的存储是 NVMe(带宽 2GB/s+),否则别设 60 秒。另外,RAC 环境下 MTTR 的计算更复杂,因为每个节点有自己的脏块集,建议 RAC 设 600 秒以上,给 Cache Fusion 留余地。

5 Checkpoint 与备份恢复:一致性备份的秘密

最后聊聊 Checkpoint 和备份的关系。很多 DBA 用 RMAN 做热备份时,担心"备份期间数据在变化,备份出来不一致怎么办?"Oracle 的解决办法是:备份开始时记录一个 Checkpoint SCN(begin backup checkpoint),备份期间数据库正常跑,脏块正常写盘,redo 正常生成。恢复时,RMAN 先还原备份文件(这些文件是备份开始时刻的一致性快照),然后应用备份开始后的所有 redo,把数据文件推进到最新状态。所以热备份的一致性不是靠"冻结数据",而是靠"记录起点 + 后续 redo"。这个机制的前提是:备份期间产生的 redo 必须完整保留。如果备份期间归档日志被删了,或者 redo 日志被覆盖,恢复时就断链了。所以备份策略必须和归档保留策略配套:备份保留 7 天,归档至少保留 7 天 + 1 天(安全余量)。

另外,ALTER TABLESPACE ... BEGIN BACKUP 时,Oracle 会触发一次增量检查点,把该表空间的脏块写盘。这是为了缩短备份恢复时间——如果备份开始时脏块很多,恢复时要应用的 redo 就多;先做一次增量检查点,脏块少了,redo 也少了。但 BEGIN BACKUP 期间的 I/O 开销很大,因为 DBWn 在写脏块,业务也在写数据,磁盘 I/O 竞争严重。所以建议备份放在低峰期,或者用 RMAN 的增量备份(Incremental Backup),减少 BEGIN BACKUP 的频率。最后送一句话:Checkpoint 是数据库的"刹车片",不是"油门"。它保证你撞墙时能停下来,但踩太猛会翻车。


请使用浏览器的分享功能分享到微信等