尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Oracle实例恢复全解析:SMON与PMON协同、前滚回滚与调优

Oracle实例恢复全解析:SMON与PMON协同、前滚回滚与调优 凌晨两点被电话叫起来说数据库连不上重启之后屏幕上只打了三行字剩下的活儿全在后台悄悄干完了。干这行时间久了会形成一个直觉数据库真正值钱的东西往往藏在那些没人盯着的后台进程里。**SMONSystem Monitor系统监视器**和 **PMONProcess Monitor进程监视器**就是最低调的一对日常巡检几乎没人专门去看它们的状态可一旦遇到实例崩溃、会话异常断连、临时段堆积、并行回滚拉不动这些事能不能干净利落地收场全看这两个进程的协同。这篇内容就围绕Oracle 实例恢复的完整生命周期展开把 SMON 与 PMON 各自负责什么、在什么时机交接、恢复过程中数据库处在什么状态、我们能用哪些视图和事件盯住进度、参数怎么算怎么调一条线捋清楚。适合已经会写增删改查、但没认真研究过实例崩溃之后发生什么的朋友也适合想弄清楚为什么重启后能连上了但还是慢的运维同学。1. 这对守护双星到底在守护什么1.1 从一次异常断电说起先还原一个最常见也最容易被忽视的场景。某个业务库正跑着批量写入机器突然掉电操作系统重启后数据库实例被拉起来。你在客户端敲下startup屏幕上依次出现实例已启动数据库已装载数据库已打开三行提示看起来跟平时没有区别。但在这三行字背后后台至少发生了五件事读控制文件确认检查点位置、从检查点开始扫描联机重做日志、把未落盘的脏块通过重做记录重新构造出来、把崩溃瞬间还没提交的事务全部回滚、把临时段和残留的进程资源清理干净。前四件事的主责是SMON最后一件里进程资源部分的主责是PMON。你能在几秒到几分钟内重新连上库是因为这两个进程把脏活干完了而不是因为磁盘上的数据文件本来就是干净的。理解这一点很关键。很多人以为数据库重启成功意味着数据已经处于一致状态其实打开这个动作只是代表一致性可以由重做和撤销保证了并不代表所有回滚都完成了。前滚redo apply必须在打开之前完成回滚undo apply可以在打开之后慢慢做。这条分界线就是整个实例恢复设计的核心也是后面所有参数调优的落脚点。1.2 SMON 与 PMON 的职责边界把两个进程的活儿列清楚比背概念有用得多。日常运维里遇到的绝大多数说不清是谁干的问题其实都在下面这张表里能找到归属。职责类别SMONPMON实例恢复的缓存恢复前滚主导可拉起并行从属进程不参与实例恢复的事务恢复回滚主导负责大事务与并行回滚只负责自身的轻量清理把大事务移交给 SMON异常终止进程的资源清理不参与主导释放锁、会话状态、PGA临时段清理主导不参与字典管理表空间的空闲区合并主导老版本特性现在基本空转不参与向监听注册服务不参与主导共享服务器模式下调度器看护不参与主导判定实例是否已经崩溃被动等待主动探测并触发恢复这张表里有几个点值得展开。第一PMON 的清理和 SMON 的恢复看起来重叠实际上分工明确一个小事务PMON 自己几毫秒就回滚完了一个已经写了几十万个块的批量事务PMON 不会傻乎乎地在那里回滚它会把这个事务标记为死事务dead transaction挂到 SMON 的队列上由 SMON 调度并行从属进程去干。第二PMON 有一个自杀检测的隐藏职责它会周期性检查其他后台进程是否还活着如果发现自己成了最后一个存活的核心进程就判定实例已经崩溃主动拉起实例恢复流程。第三向监听注册这件事看起来跟恢复无关但它直接决定了故障切换时客户端能不能第一时间发现新实例属于平时不出事、出事要命的一类。1.3 谁适合读这篇内容这篇内容偏实战需要一点基础知道什么是 SCN、知道联机重做日志是循环写的、能在 SQL*Plus 或同类工具里执行查询。如果你只会写业务 SQL读下来也不会太吃力因为涉及原理的地方我都会用生活化的例子垫一下。如果你是负责数据库可用性的角色那这篇里的参数计算、视图观测、故障排查表基本可以当成一份现场手册用。后面所有示例都基于常见的单实例部署形态RAC 环境下 SMON 和 PMON 的行为会更复杂一些涉及全局资源协调的部分我尽量绕开只讲单实例也成立的部分。2. 实例恢复的整体设计思路拆解2.1 前滚与回滚为什么必须分成两步把实例恢复拆成缓存恢复 事务恢复两段不是 Oracle 拍脑袋定的而是被性能逼出来的。想象一个正在跑大批量更新的库崩溃瞬间内存里可能压着几万个脏块、几百个未提交事务。脏块的修改依赖重做记录重做记录又依赖撤销记录而未提交事务的撤销记录本身也可能还没落盘必须先由重做把它补回来。所以顺序只能是先把重做日志从头到尾应用一遍让所有数据块和撤销块都回到崩溃瞬间的物理状态再拿这些撤销块去回滚未提交事务让逻辑状态回到一致。如果反过来先回滚撤销块本身可能还是脏的、缺失的回滚根本无从谈起。这就是为什么前滚不可跳过。那为什么不把回滚也放在打开之前做完因为回滚的耗时跟未提交事务的大小成正比一个跑了四十分钟的大批量更新崩溃后回滚可能也要几十分钟。把用户挡在门外几十分钟代价太大。而回滚没做完数据库在逻辑上依然是可用的——未提交事务的修改对其他会话本来就不可见一致性读会通过撤销块构造前镜像唯一的影响是访问到被锁行时会等待。用可接受的等待换更短的不可用窗口这就是分段设计的全部理由。2.2 检查点、SCN 与恢复起点前滚不需要从日志的第一条记录开始它只需要从所有脏块都已经落盘的那个位置开始。这个位置由检查点确定。检查点推进得越勤崩溃后需要重放的重做就越少恢复就越快但检查点太勤会导致频繁写数据文件、频繁触发日志切换平时性能受影响。这就是一个典型的平时性能 vs 故障恢复时间的权衡点。具体来说恢复起点由控制文件里的检查点信息决定SMON 会从联机重做日志里找到对应的 RBA重做字节地址然后向后扫描。扫描阶段会统计出有多少个数据块需要恢复这个数字会直接打在告警日志里。很多人看告警日志只看最后一句数据库已打开其实中间那段read xxx KB redo, xxx data blocks need recovery才是判断这次恢复规模的关键。需要恢复的块越多前滚耗时越长需要回滚的事务越大事务恢复耗时越长。两类耗时对应两类不同的调优手段混在一起谈就会抓不住重点。2.3 并行恢复的进程编排单进程前滚在日志量大的时候确实慢所以从较早的版本开始前滚阶段就支持由多个从属进程并行处理。SMON 自己充当协调者负责分配日志区间、汇总进度、处理冲突真正干重活的是若干从属进程。这些从属进程在操作系统层面能看到多出来的若干进程条目具体命名因版本而异用ps结合实例名过滤通常就能看出来。并行带来了一个副作用日志里应用重做的顺序被打散了同一数据块的多次修改可能被分配给了不同从属进程。这需要一套协调机制保证块的最终状态正确同时把因为另一个进程正在修改同一块而等待的开销控制住。经验上并行度不是越高越好。中小型库CPU 核数在 8 以下用默认值往往就够了盲目拉高并行度反而会因为争抢和协调开销让恢复变慢。只有在日志生成速率很高、数据文件分散在多个物理设备上的大库上手动引导并行度才有明显收益。较新版本里这个引导参数的作用被弱化了恢复并行度更多由系统自动判断所以先观察、再干预别一上来就改参数。2.4 为什么回滚可以让路事务恢复阶段有一个很容易被忽略的设计回滚是可以被降级、被延后、被并行化的。假设崩溃时有一个往千万级大表里插入了两百万行的事务没提交SMON 如果老老实实一行一行回滚可能要把 CPU 和 IO 吃满十几分钟严重影响刚恢复的业务。所以系统提供了回滚并行度控制可以把回滚分成多个从属进程一起干也可以把它压到很低优先级慢慢磨。这里的关键判断依据是业务恢复的紧急程度。如果数据库刚起来业务方急着要连上来跑查询那就让回滚慢一点别抢资源如果这个库起来之后就是要立刻做一致性校验和批量补数那把回滚并行度拉高反而更划算。参数层面就是两个方向的选择后面第 5 章会给出具体配置和计算思路。需要提醒的是回滚期间撤销表空间不能被回收如果撤销表空间配置得很紧回滚过程可能因为空间不足直接失败这是一个非常典型的坑我在第 6 章会专门讲。3. 核心细节SMON 的四项日常与两项战时任务3.1 实例恢复SMON 的主战场SMON 最有存在感的时刻就是实例恢复。整个流程可以拆成几个可观测的阶段。第一阶段是扫描重做SMON 从检查点位置开始读联机重做日志统计需要恢复的数据块数量和大致工作量第二阶段是应用重做把重做记录逐条应用到缓冲区这个阶段用户在数据库外部等待连接不上第三阶段是打开数据库此时一致性已经由重做保证用户连接被放行第四阶段是事务恢复回滚未提交事务这个阶段用户在库内部能感知到但连接不受阻。这里有一个非常实用的观察技巧如果你在告警日志里看到开始崩溃恢复和完成崩溃恢复之间的时间差特别长说明问题出在前滚如果数据库已打开之后业务明显变慢、大量会话在等行锁说明问题出在事务恢复。两种情况的处理路径完全不同前者要去看日志量、检查点频率、IO 吞吐后者要去看未提交事务大小、回滚并行度、撤销表空间余量。把这两个阶段混为一谈是排查方向跑偏的头号原因。还有一个经常被问到的点崩溃恢复期间能不能中断技术上你当然可以再次强制关闭但下次启动会发现需要的恢复量更大因为检查点又退回去了而且反复崩溃可能导致撤销段状态更复杂。所以我的建议是除非确认恢复已经卡死比如几个小时没有任何进度变化否则让 SMON 跑完。判断卡死和慢的区别靠的是盯住重做应用位置有没有前进而不是靠感觉。3.2 死事务恢复与并行回滚死事务这个概念值得单独讲。当一个会话非正常终止客户端崩溃、网络断开、被强制杀掉它手上未提交的事务就成了死事务。PMON 会先接手做一轮清理释放行锁、释放库缓存中的锁资源、清理会话状态。但如果这个事务已经修改了大量数据块逐块回滚太慢PMON 就会把它标记为需要 SMON 处理然后自己去干别的活。SMON 拿到死事务之后会决定用几个从属进程来回滚。这个决策受回滚并行度参数影响也受事务实际规模影响。回滚的进度可以在动态视图里看到能看到已完成的撤销块数和总撤销块数两个数字一比就知道还剩多少。这个视图在排查为什么这个表一直被锁着的时候特别好用因为它能直接告诉你回滚是不是在推进、推进得多快。需要强调一个容易误解的地方死事务回滚不完全等于锁被释放。未提交事务所持有的行锁在回滚完成之前一直存在其他会话访问这些行会等待。所以如果一个大批量更新崩溃了你会发现相关业务持续报等待超时直到回滚结束。这不是故障是设计使然。如果业务上不能接受这种等待那说明这个大事务本身就不该做成一个大事务——把大事务拆小是比调回滚参数更根本的解法这也是我在实际项目里反复强调的一条。3.3 临时段清理与空闲空间合并SMON 的日常任务里临时段清理是最有存在感的一项。排序、哈希连接、临时表操作都会在临时表空间里分配临时段。正常情况下会话结束时会自己清理但会话异常终止时临时段就留在了那里占着空间不放。SMON 会周期性地扫描并回收这些没人认领的临时段。这件事在什么情况下会变成问题典型场景是临时表空间使用率莫名居高不下查会话又找不到对应的活动会话。这时候很多人第一反应是加数据文件其实只要等 SMON 清理一轮或者手动触发一次检查空间就回来了。判断方法是查临时段视图看有没有对应不到任何活动会话的段如果有那就是待清理的残留不需要扩容。当然如果残留清理得很慢说明 SMON 的巡检节拍被其他重活拖住了这时候要么等要么换个低峰期重启实例让清理更彻底。空闲空间合并这项任务在老版本用字典管理表空间的时候有实际意义因为那时空闲区的记录方式容易产生碎片需要 SMON 定期合并。现代版本基本都用本地管理表空间位图管理天然不会有这个问题所以这项任务实际上处于挂着但不怎么干活的状态。知道这一点可以避免一个常见的误判看到文档里写SMON 合并空闲区就去怀疑表空间碎片是 SMON 没干活导致的。碎片问题的根因通常在段的空间分配策略和删除模式上跟 SMON 关系不大。3.4 离线回滚与被标记损坏的块还有两项不太常被提到、但关键时刻会冒出来的任务。第一项是离线回滚。当一个数据文件在还有未提交事务的情况下被下线或者某个表空间被删除时里面可能有需要回滚的事务。这些事务不能就地回滚得等文件重新上线或者实例重启时由 SMON 处理。如果你遇到过把文件下线再上线结果访问相关表一直报错或者一直等待的情况八成就是撞上了这个机制。处理办法通常是让实例做一次完整的打开流程给 SMON 机会把离线回滚做完。第二项是处理被标记为损坏的块。当某些块被标记为逻辑损坏SMON 会在实例启动或定期巡检时尝试恢复它们恢复手段依赖于重做日志和备份的可用性。这个能力在实际使用中并不常用更多是作为最后一道防线存在。我的建议是不要依赖它块损坏的根因排查存储层、内存层、异常关机的写入时序才是正事。但知道有这么个机制存在在遇到某些块在某些时点自己好了的诡异现象时至少不会怀疑到玄学上。4. PMON 的核心工作进程清理、注册与服务发现4.1 进程巡检与资源回收PMON 最本职的工作是看着所有服务进程。它周期性地巡检一旦发现某个进程已经不存在了就负责把它留下的烂摊子收拾掉释放它持有的锁和闩、清理它在共享池里的会话状态、回收它的 PGA 内存、把它占用的资源归还给系统。这个过程在视图里表现为会话从活动状态变成已终止状态然后从会话列表里消失。这里有个容易被误解的地方会话消失和事务回滚完成不是一回事。PMON 清理会话状态很快几毫秒到几十毫秒级别但事务回滚可能要很久。所以你会看到一种现象——会话列表里已经找不到那个会话了但相关行依然被锁着。这不是 bug是清理和回滚被拆开处理的结果。理解了这一点排查锁是谁持有的这类问题时就不会被会话已经没了误导。还有一个实操层面的细节PMON 的巡检是有节拍的不是实时的。所以进程异常终止之后会有一小段时间通常几十秒内资源还没有被完全回收。如果在这段时间内立刻去查锁信息可能看到一个已经不存在的会话还在持锁。等一个巡检周期再看往往就正常了。急着下结论说锁泄漏然后去杀会话是新手常犯的错误。4.2 死事务的移交机制这是 SMON 和 PMON 协同最精彩的地方。前面说过PMON 负责清理会话资源但大事务的回滚它不自己扛。它做的动作是把这个事务在撤销段里的状态标记为需要恢复把相关信息登记到内部队列然后唤醒 SMON。SMON 醒来后从队列里取任务决定并行度拉起从属进程开始回滚。这个移交设计的好处是明显的PMON 保持轻量能持续巡检SMON 可以集中调度重活还能顺带处理实例恢复期间遗留的未完成回滚。两者职责清晰不会互相拖累。坏处是引入了一个中间状态——事务处于已标记待恢复、回滚尚未开始的状态在这个状态下锁依然存在撤销段不能被复用撤销表空间可能在短时间内快速膨胀。所以实际运维中如果发现撤销表空间使用率突然飙升同时有大量会话在等行锁那基本可以确定是某个大事务崩了正在排队等回滚。这时候该做的不是去扩撤销表空间虽然临时扩一下能救急而是确认那个事务到底有多大、回滚能不能加速。加速的手段就是并行回滚参数下一章会给具体配置。4.3 注册监听与动态服务PMON 还有一个非常实用的职责向监听器注册当前实例提供的服务。客户端连接进来的时候先连监听监听再根据服务名把连接转给实例。如果没有动态注册就得在监听配置文件里手写实例信息实例重启换了端口或者换了配置就得手动改非常容易出错。有了 PMON 的动态注册实例启动后自己把服务名、实例名、当前负载情况报给监听监听那边就能看到READY状态。这个机制出问题时的表现非常典型数据库明明启动正常本地用管理员身份能连上但应用连不上报监听程序当前无法识别连接描述符中请求的服务。这时候第一步是去监听那边查服务列表看目标服务是不是处于 READY 状态。如果不是再看实例侧的注册配置、监听地址配置、网络是否互通。绝大多数情况下问题出在配置项写错、端口不对、或者监听启动顺序反了先起了实例后起了监听中间又没到注册周期。一个实操心得注册是周期性的实例起来之后监听不会立刻知道。所以刚重启完连不上有时候只是等得不够久。如果业务上不能接受这个等待窗口可以手动触发一次注册动作让 PMON 立刻上报比干等有效得多。这个动作在应急切换和演练场景里很有价值值得记在本子上。4.4 共享服务器模式下的调度器看护如果部署形态用的是共享服务器模式一个调度器进程服务多个客户端连接PMON 还额外负责看护这些调度器进程。某个调度器异常退出PMON 会把它清理掉并按需拉起新的保证连接分发不中断。这个职责在专用服务器模式下不生效所以很多只用专用服务器的人根本不知道 PMON 还有这一面。知道这一点的实际意义在于排查方向。共享服务器模式下如果出现连接数忽高忽低某些连接莫名断开除了查网络和客户端还要去看调度器进程的数量和状态是否稳定。如果调度器频繁重启根因可能是资源不足进程数上限、内存而不是网络问题。把 PMON 的这一层职责纳入排查范围能少走不少弯路。专用服务器模式下就不用操这个心把注意力放在会话数和进程数上限上就行。5. 实操模拟崩溃、观察恢复全过程5.1 环境与观察工具准备建议在测试环境做这套演练别在生产上玩。准备一个能随意重启的实例打开告警日志的实时跟踪准备好几个观察视图。下面这些查询是整套演练的核心工具先记下来。-- 查看实例恢复的整体估算与目标 select target_mttr, estimated_mttr, ckpt_block_writes, optimal_logfile_size from v$instance_recovery; -- 查看死事务恢复进度已回滚块数 / 总块数 select xid, usn, slt, seq, undoblocksdone, undoblockstotal, round(undoblocksdone / greatest(undoblockstotal, 1) * 100, 2) as pct, rcverservers, state from v$fast_start_transactions; -- 查看参与回滚的从属进程统计 select * from v$fast_start_servers; -- 查看实例恢复/介质恢复的分阶段进度较新版本可用 select * from v$recovery_progress; -- 查看后台进程当前的等待事件 select s.sid, s.serial#, s.program, s.event, s.wait_time, s.seconds_in_wait from v$session s where s.program like %SMON% or s.program like %PMON%;最后那个查询特别有用。当你想知道SMON 现在到底在干嘛看它的等待事件比看文档有效得多。它挂在定时器类的等待上说明在打盹巡检它在做 IO 相关的等待说明在干活。PMON 同理。5.2 制造一次崩溃并观察告警日志演练的第一步是制造一个干净的崩溃现场。先跑一个不提交的大事务让它在缓冲区里留下大量脏块和未提交修改然后强制关闭实例。-- 会话 A开启一个大事务插入数据但不提交 insert into demo_big_table select * from demo_big_table_source; -- 不要 commit保持事务打开 -- 会话 B管理员强制关闭实例 shutdown abort; -- 重新启动 startup;启动过程中盯住告警日志你会看到类似这样的段落具体措辞随版本略有差异Beginning crash recovery of 1 threads Started redo scan Completed redo scan read 8642 KB redo, 1287 data blocks need recovery Started redo application at ... Recovery of Online Redo Log: Thread 1 Group 1 Seq 128 Reading mem 0 Completed redo application of 1287 blocks Completed crash recovery at ... SMON: enabling cache recovery SMON: enabling tx recovery Database Characterset is AL32UTF8这里有三条信息值得记住。第一read xxx KB redo, xxx data blocks need recovery给出了恢复规模这个数字越大前滚越久。第二Completed crash recovery出现的位置标志着前滚完成接下来才允许打开数据库。第三SMON: enabling cache recovery和SMON: enabling tx recovery这两行是 SMON 明确宣告自己进入恢复流程的标志看到它们就等于确认了主责进程。打开之后立刻去查v$fast_start_transactions如果那个大事务还没滚完你会看到一行记录undoblocksdone在缓慢增长state显示正在恢复。这时候同时去查锁相关视图会发现相关行一直有锁。这个现场就是最好的教材数据库已经可用但业务在特定表上会卡。5.3 关键视图与等待事件的联动观察单看一个视图容易片面几个视图对着看才能形成完整判断。我常用的组合是这样先用v$instance_recovery看全局估算判断这次恢复大概是什么量级再用v$fast_start_transactions看具体事务的进度判断是刚起步还是快完了然后用会话视图看 SMON 和 PMON 在等什么判断它们是在干活还是在打盹最后看普通业务会话在等什么判断恢复对业务的实际影响。举个具体的判断逻辑。如果业务会话大面积在等行锁类的等待事件同时v$fast_start_transactions里有一行的进度百分比在缓慢上升那结论很明确在等回滚等着就行或者去调并行回滚。如果业务会话在等日志相关的等待同时v$instance_recovery里的估算值远高于目标值那说明检查点推进不够快需要去调检查点相关参数。如果 SMON 挂在定时器等待上而数据库整体很慢那问题根本不在恢复别往这个方向查了。我个人的经验是把这几个视图做成一个固定的巡检脚本每次实例重启后跑一遍三十秒内就能对恢复进行到哪一步、还要多久、对业务影响多大形成判断。这比翻日志快得多也更适合在不方便看日志的环境里用。5.4 MTTR 参数的计算与选择恢复时间能不能控住很大程度取决于检查点推进的节奏而这由恢复目标参数引导。核心思路是让系统保留最近一段时间产生的重做量对应的脏块不落盘这样崩溃后只需要重放这一小段。目标时间设得越短脏块落盘越勤平时写压力越大设得越长恢复越慢。计算方式不复杂。假设你的库峰值重做生成速率是 8 MB/s你希望崩溃后前滚阶段控制在 60 秒以内那么需要保留的重做量大约是 8 × 60 480 MB。这意味着联机日志的单个日志组大小至少要能装下这个量级考虑日志切换的原子性通常再留一倍余量取 1 GB 一组比较稳。同时把恢复目标设成 60 秒系统就会按这个节奏推进检查点。-- 设置实例恢复目标时间单位秒 alter system set fast_start_mttr_target 60 scope both; -- 查看系统的建议值辅助判断日志组大小是否合理 select * from v$mttr_target_advice order by mttr_target;v$mttr_target_advice这张表能给出不同目标值下的预估 IO 开销非常适合用来做取舍。比如目标设 30 秒时预估写次数是 60 秒的两倍多那就要掂量一下存储扛不扛得住。我在 IO 余量不大的库上一般不会把目标压到 30 秒以下宁可接受 60 到 90 秒的恢复窗口也不愿意平时就被检查点写拖慢。关于日志组大小还有一个经验公式可以参考把峰值重做速率乘以 20 分钟作为单个日志组的下限。这样做的目的不只是为了恢复也是为了避免日志切换过于频繁切换本身会触发检查点形成正反馈。如果日志组大小远小于这个值那么无论恢复目标怎么设实际恢复时间都会被日志切换节奏牵着走调参效果会很差。5.5 并行回滚的配置与取舍事务恢复阶段的加速手段主要是并行回滚。配置项通常有三个档位关闭并行单进程慢慢滚、低并行、高并行。低并行对应的从属进程数大致是 CPU 核数的两倍高并行是四倍。选哪个取决于这个库起来之后要干什么。-- 低并行适合刚起来就要对外提供查询服务的库 alter system set fast_start_parallel_rollback low; -- 高并行适合恢复后立刻要跑批、做校验的场景 alter system set fast_start_parallel_rollback high;这里有个反直觉的点并行回滚不一定更快。如果回滚涉及的撤销记录在磁盘上分布很散多个从属进程会争抢同一批 IO 资源实际吞吐还不如单进程顺序读。我遇到过一个小库开了高并行之后回滚反而更慢因为存储的随机读能力很弱。所以这条参数的正确用法是先默认观察一次实际恢复的表现如果确实是回滚瓶颈再调调完再测一次对比。别一上来就按越大越好的思路配。另外要提醒的是并行度上限还受其他资源约束影响包括进程数上限、内存、撤销表空间的可用空间。如果撤销表空间只剩一点点余量并行回滚会因为空间不足报错中断反而更麻烦。调并行之前先确认撤销表空间够用这是一个非常低级的但确实常见的失误。6. 常见问题与排查技巧实录6.1 实例恢复特别慢怎么定位是前滚慢还是回滚慢定位方法就是看时间分界点。告警日志里完成崩溃恢复到数据库已打开之间是前滚的耗时这段时间业务完全连不上数据库已打开之后如果业务依然很慢、大量行锁等待那是回滚的耗时。前者去查三个东西需要恢复的块数日志里直接有、检查点推进频率恢复目标参数是否设得过长、存储的顺序读吞吐。后者去查未提交事务的规模、回滚进度是否在推进、撤销表空间余量。有一类情况容易被误判日志显示需要的恢复块数不多但恢复依然很慢。这往往是存储的随机读能力出了问题或者日志文件和数据文件放在了同一批高负载设备上。这时候调参数没用得从存储布局上想办法比如把联机日志单独放到低延迟设备上。这个结论我在多个环境里验证过日志设备的 IO 延迟对前滚速度的影响远超大多数人的预期。6.2 临时表空间使用率居高不下找不到对应会话这是 SMON 临时段清理的典型场景。排查路径是先确认临时段视图里有没有孤儿段对应不到活动会话的段如果有那就是等 SMON 清理。然后再看 SMON 当前在干什么如果它被其他重活拖住了比如正在做实例恢复或者大事务回滚清理节拍就会延后。需要区分两种情况。如果是偶发性的、清理之后空间能回来那是正常的不用干预。如果持续增长而且清理不动可能是临时段分配策略有问题比如排序区配置不合理导致大量小临时段或者是应用存在长期不释放临时段的连接池问题。前者调内存参数后者改应用。盲目加数据文件只能缓解症状治不了根。我见过一个系统靠不停加临时文件撑了半年最后发现是连接池里有一批连接长期持有排序区不释放。6.3 数据库能连上但应用连不上监听里看不到服务按三步走。第一步查监听侧的服务列表看目标服务是否存在、状态是否正常。第二步查实例侧的注册配置确认服务名、监听地址、端口是否与监听侧一致。第三步查网络连通性确认实例所在机器与监听所在机器或端口之间没有阻断。最常见的两个原因一是实例启动之后还没到注册周期等一会儿或者手动触发注册就好二是配置文件里的监听地址写成了主机名而主机名解析在这个环境里不稳定导致注册时有时无。第二种情况特别隐蔽因为表现是间歇性的——有时候能连有时候不能。我的建议是这类配置统一用明确的地址写法别用可能解析出多个结果的主机名能省掉大量莫名其妙的故障。6.4 死事务长时间不回滚锁一直不释放先确认回滚是不是真的卡住了。看死事务视图里的进度数字隔一分钟看两次如果完全没动那是卡住了如果有缓慢增长那只是慢。卡住的常见原因有三个撤销表空间满了无法继续写入、并行回滚的从属进程因为资源不足起不来、撤销段所在的数据文件处于不可用状态。对应的处理方式临时给撤销表空间加空间、把并行回滚降级成单进程先让它跑起来、把不可用的文件恢复上线。切忌直接杀掉 SMON 或者强制重启那只会让现场更复杂——重启之后这些事务还是要回滚而且可能因为反复中断导致撤销段状态更难处理。我的原则是能让它跑完就跑完实在跑不完再考虑用离线方式处理那个段但那属于最后手段。6.5 常见问题速查表下面这张表是我自己排障时用的速查清单按现象查原因比从原理推起快得多。现象最可能的原因第一手动作重启后长时间停在已装载前滚量大或日志设备 IO 慢看日志里的需恢复块数检查日志设备延迟重启后能连上但业务大面积卡大事务回滚未完成查死事务进度与锁等待评估是否调并行回滚临时表空间只涨不降孤儿临时段待清理查临时段视图确认 SMON 是否被拖住应用报无法识别服务PMON 注册未完成或配置错查监听服务状态手动触发注册会话已消失但锁还在会话清理与事务回滚分离等一个巡检周期同时查回滚进度回滚进度长时间不动撤销空间不足或从属进程起不来扩撤销空间降并行度重试恢复目标设了但恢复还是慢日志组太小切换节奏拖累检查点按峰值重做速率重算日志组大小7. 几个我实际踩过的坑和一点使用习惯说几个文档里不会写、但真会让人半夜睡不着的细节。第一个坑是撤销表空间的自动扩展被关掉了。某个库为了控制磁盘增长把撤销文件设成固定大小结果一次大事务崩溃之后回滚因为写不进撤销记录直接报错中断锁一直不释放业务停了快两个小时。后来我们把撤销表空间改成可自动扩展并设了上限同时给回滚阶段留了足够的余量这个问题再没出现过。教训很直白别在撤销表空间上抠磁盘。第二个坑是日志组大小和恢复目标不匹配。有人把恢复目标设成 30 秒但日志组只有 100 MB峰值重做速率 10 MB/s等于十秒就切一次日志。结果平时检查点被日志切换追着跑IO 一直很忙恢复时间反而没降下来。这种参数设了但底层撑不住的情况特别常见调参之前先算一遍数比事后猜有效得多。第三个是习惯问题。我现在接手任何一个库第一件事是把它重启后的观察脚本准备好包括前面那几个视图的查询、告警日志的关键字过滤、以及一份这次恢复耗时多少、影响多少业务的记录。攒上十几次重启的数据之后你就能大概判断出这个库的恢复能力边界在哪也就能在出问题之前把参数调到位。这件事花不了多少时间但能让你在真出事的时候心里有底而不是一边翻文档一边看日志。最后分享一个小技巧如果只是想验证恢复流程是否正常不需要真的制造崩溃可以用强制关闭的方式在测试库上走一遍完整流程重点观察告警日志的阶段划分和死事务视图的进度变化。走三五次之后你对这个库崩溃之后大概会怎样会形成非常具体的直觉这种直觉在关键时刻比任何文档都管用。
返回列表