ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Oracle SCN与检查点:数据库恢复效率的源头与实操

Oracle SCN与检查点:数据库恢复效率的源头与实操 简介面向Oracle DBA、数据库运维人员及备考者的原理性文档系统讲解SCN系统改变号与检查点两大核心机制。内容从SCN定义出发阐明它作为Oracle内部逻辑时钟如何标识事务提交版本解释SCN在事务提交或回滚时变化、不会重置为零等特性并说明SCN广泛存在于事务表、控制文件、数据文件头、日志文件及数据块头等位置随后提供通过dbms_flashback.get_system_change_number获取当前SCN的SQL示例并转入检查点机制说明其目标是减少崩溃恢复时间涉及脏数据写入、DBWR与CKPT进程配合、更新数据文件头和控制文件等关键步骤以及通过v$datafile查询文件检查点SCN的方法。资源为1个PDF文档大小约81KB内容紧凑、附带查询示例可帮助读者快速掌握概念与视图含义理清SCN和检查点在崩溃恢复中的协作关系。目前已有434人学习浏览适合Oracle进阶学习者巩固数据库核心机制。1. 我为什么把 SCN 和检查点放一起讲它俩才是数据库恢复效率的源头不少刚接触 Oracle 的同行都有同感SCN 和检查点这两个词在官方文档里被解释得很抽象——SCN 是系统改变号检查点是个数据库事件看完仍是“名词认得落地不会”。我第一次被拉去处理一个“重启数据库耗时异常久”的现场时花了一整天才把 SCN、检查点、重做日志这三件事串起来。那份《Oracle SCN与检查点详解》文档给我省了不少时间所以这篇笔记就按“它是什么、在哪能看到、怎么验证、踩过哪些坑”的顺序把这份资料里的核心内容重新打成一份能照着操作的版本。一句话先说清关系SCN 是 Oracle 内部的逻辑时钟检查点是拿这个时钟作刻度把脏数据写回磁盘并同时缩短崩溃恢复时重做日志重放范围的手段。整篇不展开分布式事务和 RAC 内部细节面向 oracle 入门阶段最头疼的恢复时间问题也覆盖面试里常问的v$datafile查看检查点 SCN 这类硬核题。2. 先从 SCN 的定义入手为什么它叫逻辑时钟而不是提交号2.1 不必纠结 Change 还是 Commit记住三件事就行文档开头就用了一整段来解释System Change Number和System Commit Number的命名争议。老实说这两个词在官方文档里都出现过考证哪个是原意没有太大意义。你需要记住的是三件事实SCN 是数据库全局唯一的递增编号SCN 用来标识某个确切时刻的数据库版本SCN 在事务提交或回滚时会被分配并作为事务排序依据。这个“排序”作用比字面意思重要得多。Oracle 做一致性读时需要判断某个数据块版本对当前查询是否可见比较的就是块头记录的 SCN 和查询启动时对应的 SCN。事务表、控制文件、数据文件头、日志文件头、数据块头里都记录着不同含义的 SCN 值它们都是同一个全局时钟在不同物理位置的快照。还有一点容易忽略SCN 的值随时间增加但并不是连贯的。两次查询之间可能一次涨几百完全正常。原因也很朴素——Oracle 向全局生成器批量申请一段 SCN 范围避免每次都做全局串行分配。后面排查时看到 SCN 跳变先别慌。2.2 获取 SCN 的正确姿势函数调用别少了括号文档给出的方式是调用DBMS_FLASHBACK包里的函数原脚本如下-- 获取系统当前的近似 SCN SELECT dbms_flashback.get_system_change_number FROM dual;这段在 SQL*Plus、PL/SQL Developer、SQL Developer 里都能执行输出通常是一个很大的数字例如文档现场拿到的是6051905241299。这个数字本身没有跨库可比性你只需要关注它在本库内随时间的变化趋势。有一点必须提醒GET_SYSTEM_CHANGE_NUMBER是一个无参函数在 SQL 的 select 列表里可以省略括号但你要是把它写进 PL/SQL 赋值语句就必须补上空括号。常见翻车写法是-- 在存储过程或匿名块中必须加括号否则编译报错 DECLARE l_scn NUMBER; BEGIN l_scn : dbms_flashback.get_system_change_number(); -- 这里要括号 dbms_output.put_line(l_scn); END; /我的习惯是查完之后再用v$database交叉验证一次两个来源应该非常接近-- 从控制文件角度读取当前 SCN SELECT current_scn FROM v$database;注意v$database.current_scn在 10g 之后才普遍可用较早的库还是以DBMS_FLASHBACK为主。交叉验证的意义在于如果两个值差异过大说明你查询的会话可能正巧跨过一个 redo 生成峰值或者控制文件与内存视图存在同步延迟需要结合 alert log 再看。2.3 SCN 在库里无处不在各个“SCN 前缀名”分别对应哪个物理位置文档列出的几个位置很典型事务表、控制文件、数据文件头、日志文件、数据块头。这里我用一张表把它们对应起来方便你以后看文档时不会被各种前缀绕晕物理位置常见名称记录内容与作用数据文件头Checkpoint SCN、Stop SCN、Checkpoint Cnt最近一次检查点完成时的 SCN崩溃恢复时从这里开始找 redo控制文件Checkpoint SCN、Resetlogs SCN全库检查点进度、日志历史、resetlogs 之后的序号记录日志文件头First Change#、Next Change#该日志内第一条和最后一条 redo 对应的 SCN 边界redo 记录Change Vector 中的 SCN逐块记录变更时点的 SCN用于前滚时判断是否需要应用数据块头Block Cleanout SCN事务提交后延迟清理时标记块版本事务表事务 SCN提交时分配的唯一事务编号这块最容易犯的错是拿不同物理位置的 SCN 直接做大小比较。比如数据文件头的 Checkpoint SCN 和 redo 日志里的 First Change# 本来就不该相等一个表示“已写盘的进度”另一个表示“日志起点”比较它们没有业务意义。真正有意义的比较是当前 SCN 和检查点 SCN 之间的跨度这个跨度才决定恢复时要重放多少 redo。3. 检查点本质是为了缩短崩溃恢复从重做日志重放说起3.1 为什么提交时不立刻写盘redo 的存在是前提很多文档把检查点讲得复杂其实核心就一句话它是一个数据库事件存在的根本意义是减少崩溃恢复时间。理解这件事要先接受一个设计前提Oracle 不会在每次事务提交时把修改的数据块写回数据文件那样写 I/O 会高到无法接受。事务提交时真正必须落盘的是重做日志因为 redo 记录的是“怎么把这次修改重演出来”日志一旦持久化事务就安全了。数据块可以先留在 Buffer Cache 里等后续检查点或空间压力再把脏块刷出。断电崩溃时Buffer Cache 里那些已提交但没写进数据文件的修改会丢Oracle 启动后会做前滚也就是把崩溃前那一段 redo 再应用一遍把数据块恢复成崩溃前的状态然后回滚未提交事务。这个过程叫崩溃恢复大家最关心的就是它要多久。耗时取决于什么取决于要读多少 redo。如果能确定一个较早的“安全起点”告诉前滚引擎在这个点之前的数据块都已经落盘了恢复时就可以只从这个点开始往后应用 redo。这个点就是检查点。没有检查点作为锚点崩溃恢复就得从开天辟地开始重放显然不现实。3.2 检查点发生时的一条完整链路DBWR 与 CKPT 各自干什么文档把过程描述得很清楚检查点发生时Oracle 会通知 DBWR 进程把这个检查点 SCN 之前的脏数据从 Buffer Cache 写回磁盘写完之后CKPT 进程更新控制文件和数据文件头把检查点相关信息记录下来。注意两个进程分工不同。DBWR 负责搬运脏块它关注的是“哪些块要写”CKPT 负责记账它更新的是“写到哪个 SCN 了”。这也是为什么你查看v$datafile里的CHECKPOINT_CHANGE#时看到的是 CKPT 进程记录的值而不是 DBWR 实时刷盘的值。检查点完成之后一个重要变化是检查点之前对应的 redo 记录对崩溃恢复不再有用日志文件里的这些位置可以被覆盖复用。所以检查点频率越高崩溃恢复要应用的 redo 越少恢复时间越短。反过来频繁检查点会带来额外的写 I/O因为每次检查点都会触发大量脏块写入尤其是在更新密集的库上代价不可忽视。文档里那句话很到位数据库内部操作相关性极强优化是系统工程不能草率。3.3 先跑一遍 v$datafile数据文件头的 Checkpoint SCN 是怎么一步步更新的拿到这份文档后我第一次动手验证的就是下面这条 SQL。建议你也亲自跑一次因为看一百遍概念不如看一眼真实数据-- 查看每个数据文件当前的检查点 SCN 与时间 SELECT file#, NAME, CHECKPOINT_CHANGE#, to_char(CHECKPOINT_TIME, yyyy-mm-dd hh24:mi:ss) AS cpt FROM v$datafile ORDER BY file#;文档现场的简化输出如下FILE# NAME CHECKPOINT_CHANGE# CPT ----- -------------------- ---------------------- -------------------- 1 /u01/.../system01.dbf 6051905239995 2016-05-05 04:14:32 2 /u01/.../sysaux01.dbf 6051905239995 2016-05-05 04:14:32 3 /u01/.../undotbs01.dbf 6051905239995 2016-05-05 04:14:32 ... 8 rows selected这里有个观察点值得展开输出里所有文件头的CHECKPOINT_CHANGE#完全一致是因为现场刚刚经历过一次全库检查点。在正常运行的库上不同文件的检查点 SCN 经常不一致尤其是加了数据文件或做过离线备份之后个别文件头会落后于其他文件。这本身不是异常判断它是否健康要看“落后多少”以及“是否一直落后”。CHECKPOINT_TIME表示该文件最近一次完成检查点的时间。很多人问怎么确认检查点是否真的发生过最简单就是隔几分钟再查一次看这两个字段有没有往前推。注意数据文件头的检查点 SCN 是逐步推的不是一个 session 里执行一条命令就能立刻跳到最后DBWR 写脏、CKPT 记账都需要时间。4. 把检查点状态查透视图、参数与差值计算4.1 四个视图串起来日志离检查点有多远单纯看v$datafile只能知道“检查点走到哪了”要判断“离恢复目标还有多远”还得把当前 SCN 和 redo 日志的边界信息拉进来。我一般会把下面几条 SQL 作为一套组合拳来跑。-- 组合查询当前 SCN 与各数据文件检查点 SCN 的差距 SELECT df.file#, df.name, df.checkpoint_change# AS file_cp_scn, (SELECT current_scn FROM v$database) AS db_current_scn, (SELECT current_scn FROM v$database) - df.checkpoint_change# AS redo_span, df.checkpoint_time FROM v$datafile df ORDER BY redo_span DESC;这个redo_span就是我前面说的“跨度”它表示从上一次检查点确认写盘之后数据库又产生了多少新的变化。恢复时至少要重放这些对应的 redo。跨度越大前滚时间越长。这只是逻辑判断不是性能指标。跨度大不等于一定会慢还得看 redo 量本身有多大。比如同样跨度 100 万 SCN事务量小的库对应的 redo 可能只有几百 MB事务量大的库可能就是几个 GB。所以接下来要看另一个东西——日志文件的边界。4.2 把恢复需要的日志范围画出来日志文件的 first/next changeredo 日志按 thread 和 sequence 组织每份日志文件都有明确的 SCN 边界。查看这些边界的标准查询是-- 查看当前所有 redo 日志的 SCN 边界与状态 SELECT thread#, sequence#, first_change#, next_change#, status FROM v$log ORDER BY thread#, sequence#;字段含义不复杂first_change#是这份日志里第一条 redo 的 SCNnext_change#是下一条 redo 的 SCN。STATUSCURRENT表示正在写ACTIVE表示日志已写完但内容对崩溃恢复还有用INACTIVE表示对应的脏块已经完成检查点日志可以复用。这就是检查点和日志切换的协同关系检查点推进到某个 SCN 之后那些next_change#小于该 SCN 的日志就不再需要保留状态会从 ACTIVE 变 INACTIVE。所以当你看到大量 ACTIVE 日志积压时不用急着怀疑 redo 有问题先回去看v$datafile的检查点 SCN 是否卡住了多半是脏块刷盘跟不上。增量检查点的概念也在这里体现Oracle 不会等所有脏块都写完才更新检查点位置而是让检查点 SCN 像一个游标一样顺着 redo 流逐步推进。只要 DBWR 写脏持续推进检查点就会慢慢追着 CURRENT 日志跑如果检查点长期停在某个 SCN 不动前滚范围就会拉长。4.3 参数面fast_start_mttr_target 与 log_checkpoint_timeout 的取舍拜访维护过 Oracle 的同行几乎都会被提醒这两个参数。它们都属于“检查点触发策略”的调节开关定位完全不同。fast_start_mttr_target的单位是秒表示你期望的崩溃恢复目标时间。Oracle 会在后台估算要在这个时间内完成恢复检查点得分推到哪里、脏块要提前写多少。它是目标导向有点“自己看着办”的意思。log_checkpoint_timeout的单位也是秒表示最多隔多久必须触发一次检查点。它是时间导向不管事务量大小到点就触发。查看当前值的标准写法-- 查看检查点相关参数当前值 SELECT name, value, isdefault, description FROM v$parameter WHERE name IN (fast_start_mttr_target, log_checkpoint_timeout) ORDER BY name;实测中很多库两个参数都没显式设置走的是默认值和自动调整。需要调整时我的习惯是先在业务低谷把fast_start_mttr_target从默认值改到 300 秒左右观察一段时间不要上来就改到 60 秒。恢复时间和性能是邻居关系你压缩恢复时间脏块就更频繁落盘I/O 压力就上升。下面这条命令可以用来做临时调整重启后失效适合用来验证影响-- 临时设置 MTTR 目标为 300 秒仅当前实例生效 ALTER SYSTEM SET fast_start_mttr_target 300 SCOPE MEMORY;验证期建议配合v$mttr_target_advice观察预估恢复时间。老版本里这个顾问视图比手工估算靠谱得多能看到不同目标值下的预估恢复时长和额外写 I/O 量。调整参数不是拍脑袋先看顾问数据再动手。提示新手常见的迷惑点在于把fast_start_mttr_target当成性能开关以为调小就能让系统变快。它换来的是恢复时间缩短代价是刷脏频率上升慢查询和 I/O 等待可能跟着变多。只有当你确知“恢复时间长”是痛点时才有必要调它。5. 避坑清单SCN 与检查点实际踩过的五个现场5.1 打开数据库慢到怀疑人生现象一台测试库断电后启动前滚阶段跑了 40 分钟业务方不停催。原因检查v$datafile时发现数据文件头的CHECKPOINT_CHANGE#停留在好几个小时之前的 SCN而这几个小时内日志文件疯狂切换导致需要重放的 redo 横跨几十份归档日志。说明库在崩溃前检查点推进已经明显滞后。解决等库正常打开后先查redo_span确认滞后量再把fast_start_mttr_target下调到一个可控范围比如 300 秒同时确认归档目录空间充足避免下次崩溃后日志早被清理。从那以后我遇到慢启动第一反应不再是怀疑磁盘性能而是先看检查点距离。5.2 没跑大事务SCN 却一直在涨现象有人反馈数据库 SCN“异常增长”两次查询之间跳了几十万怀疑出了 bug甚至想通过重置数据库来“归零”。原因SCN 本来就不是按提交次数逐个分配的Oracle 会批量预分配一段编号减少全局争用而且大量日志切换、内部递归操作也会消耗 SCN。文档里写得很明白除非重建数据库SCN 永远不会被重置为 0。数值大不是问题涨得快才要关注它背后的 redo 生成量。解决不用做任何处置。只需要连续采样一段时间确认 SCN 增长和日志生成量在同一节奏如果哪一天 SCN 暴涨但日志量平稳再回过头查是否存在反复 resetlogs 或时钟跳变类的外部因素。5.3 数据文件头和控制文件对不上现象同一时间查v$datafile和v$datafile_header发现CHECKPOINT_CHANGE#不一致有人立刻断定数据文件坏了准备跑恢复。原因这两个视图的数据来源根本不同。v$datafile读的是控制文件v$datafile_header读的是数据文件头。在做 backup、restore 或控制文件重建之后两边记录的检查点 SCN 本来就允许有差异差异大小取决于最后一次检查点发生的位置和谁先被更新。解决先确认刚才是不是做过控制文件相关操作。正常库中两者短期不一致是合理的长时间不一致且越来越远才需要警惕。排查时用下面这条命令逐文件核对两次结果-- 从数据文件头读检查点 SCN与 v$datafile 做交叉对比 SELECT file#, checkpoint_change#, checkpoint_time FROM v$datafile_header ORDER BY file#;5.4 日志切得勤快检查点却没跟上现象日志每十几分钟切换一次看起来系统很“活跃”但v$datafile的检查点 SCN 半天不动ACTIVE 状态的日志越积越多。原因日志切换和检查点是两码事。日志写满了就切切完只代表 redo 落盘结束检查点则由时间、MTTR 目标、日志切换等多种条件触发最终要等 DBWR 把脏块刷完才更新文件头。日志切得快但脏块刷不完检查点就会卡住。解决先看 ACTIVE 日志数量再回到redo_span计算跨度。如果是脏块刷盘跟不上则需要关注 DBWR 写等待和 buffer cache 是否存在不合理的大扫描把缓存挤爆盲目增加日志文件大小或切换频率解决不了根因。5.5 改完 MTTR性能没变好反而变差现象运维同事把fast_start_mttr_target从默认值调到 120 秒本意是缩短恢复时间结果业务高峰期的 DB File Sequential Read 等待上涨查询明显变慢。原因恢复时间和运行性能在这个参数上是一个零和博弈。目标时间越短检查点越积极脏块提前写盘的频率越高占用的 I/O 资源就越多。文档原文也强调过过于频繁的检查点会给高频更新库带来性能问题。解决把目标值调回去或者用SCOPE MEMORY做临时验证观察一个完整业务周期再决定是否保留。调参之前务必先看v$mttr_target_advice预估的额外 I/O 量不要用生产环境试错。6. 顺手把验证做全建立“检查点距离”巡检习惯这篇文档读完之后真正能留在手里的应该是一套可重复执行的验证动作。整理出来就是每次做检查或排查时固定先跑下面这段 SQL-- 日常巡检一次性拿到文件级检查点距离和日志边界 SELECT df.file#, df.name, df.checkpoint_change# AS file_cp_scn, (SELECT current_scn FROM v$database) AS db_scn, (SELECT current_scn FROM v$database) - df.checkpoint_change# AS span, df.checkpoint_time FROM v$datafile df ORDER BY span DESC;判断标准可以套用一条经验规则任意两次巡检之间如果span持续放大说明检查点推进跟不上 redo 生成速度如果span在一个区间内上下波动说明系统处于稳态。配合v$log的 ACTIVE 状态日志数量一起看基本就能判断恢复时间压力是否在积累。这里再补充一个文档里没有展开、但实测很受用的习惯拿到任何一份 Oracle 恢复类资料都先不要背结论而是把里面的所有 SQL 在测试环境跑一遍记录真实数字。只有亲自看到 SCN 跳变、检查点推进、日志状态翻转才能真正明白这三个东西是怎么咬合在一起的。这份《Oracle SCN与检查点详解》最核心的贡献是把我从“只听说 Checkpoint 能缩短恢复时间”推进到了“能自己查证检查点走到哪一步”。从那以后我每次拿到类似的 Oracle 笔记或 DBA 心得都强制自己先花十分钟把里面的 SQL 全部跑一遍确认数据长什么样子再谈优化而不是照抄参数或命令。希望帮到你。本文还有配套的精品资源点击获取
返回列表