业务卡顿时,CPU 低并不代表数据库没问题。本文还原一次从 AWR、ASH 到 RMAN、磁盘与 RAID 的完整实战排查过程,并附上可直接复用的查询脚本。
前言
前段时间遇到一个 Oracle 数据库卡顿问题。
业务反馈系统突然变得很慢,打开页面要等,提交也要等,但是没有明确报错。登录主机看了一下,数据库实例正常,CPU 也不高,Idle 甚至接近 99%。
从主机监控看,这台服务器完全不像正在出故障,但业务确实已经卡住了。
这种问题通常比直接报 ORA 错误更麻烦。没有错误信息,只能从故障时间点开始,一层一层往下查。本文记录一下当时的分析和处理过程。
问题分析
首先,根据业务反馈的时间段生成了一份 AWR 报告。
报告窗口大约 59 分钟:
Elapsed Time : 59 min
DB Time : 309 min
Average Active Sessions 大约是 5.2。服务器有 64 个逻辑 CPU、32 个 Core,再看 CPU 使用情况:

AWR Host CPU:故障时间段 CPU Idle 为 99.1%,主机并没有出现 CPU 忙的问题。
CPU 确实很空闲,但是 59 分钟的报告窗口里,DB Time 却有 309 分钟,说明数据库会话大部分时间不是在消耗 CPU,而是在等待。
继续看 AWR 的 Top Foreground Events:

AWR Top 10 Foreground Events:log file sync 占 54.9% DB Time,平均等待 431ms。
第一名是 log file sync,占了 54.93% 的 DB Time,平均等待 431ms。
前台会话提交事务时,需要等待 LGWR 将 redo 写入联机日志。现在一次 COMMIT 平均要等 431ms,业务页面点一下、顿一下也就说得通了。
接着查看对应的后台等待 log file parallel write:
log file parallel write
Waits 25,954
Time Waited 1,359 sec
Avg Wait 52 ms
LGWR 平均一次写要等 52ms,这个延迟已经明显不正常。AWR 中还有两个写等待也很高:
control file parallel write ≈ 197 ms
db file async I/O submit ≈ 364 ms
看到这里,我首先怀疑的是存储。因为慢的不只是业务 SQL 读取,redo 和 controlfile 写入也慢。
不过 log file sync 高也可能和业务提交频率有关,现在还不能直接认定是硬件问题,还要看看故障时间段内有没有其他任务在大量做 I/O。
RMAN 正好也在跑
继续看 ASH,Top Background Events 中有一个等待非常显眼:

ASH Top Background Events:RMAN backup & recovery I/O 的活动占比为 20.03%,平均活跃会话为 1.98。
Top Sessions 中还看到了两个 RMAN Channel:
ASH Top Sessions(账号与 SID 已脱敏)
Session Program Event Active Samples
------------ ------------------------- ---------------------------- --------------
<RMAN-CH-1> rman@<db-host> RMAN backup & recovery I/O 3544 / 3600
<RMAN-CH-2> rman@<db-host> RMAN backup & recovery I/O 3578 / 3600
两个 Channel 的活跃采样比例分别接近 98% 和 99%。也就是说,业务卡顿的这一个小时,它们基本一直在执行备份。
先从数据库确认 RMAN 会话:
set lines 220 pages 100
col program format a40
col module format a30
col action format a30
col event format a35
select sid,
serial#,
username,
status,
program,
module,
action,
event
from v$session
where lower(program) like '%rman%'
or lower(module) like '%rman%'
or lower(action) like '%backup%'
order by sid;
两个会话都是 ACTIVE:
ACTION EVENT
backup incr datafile RMAN backup & recovery I/O
backup incr datafile RMAN backup & recovery I/O
操作系统上也能找到对应的 RMAN 进程和计划任务:
ps -ef | grep -i '[r]man'
crontab -l
计划任务每周执行一次 Level 0,其余日期执行 Level 1 cumulative。故障时运行的是两通道 Level 1,同时备份数据库和归档日志。
核心脚本大致如下,路径已经脱敏:
run {
allocate channel c1 type disk;
allocate channel c2 type disk;
backup incremental level 1 cumulative database
format '<backup_dir>/db_%U.bkp';
backup archivelog all
format '<backup_dir>/arch_%U.bkp';
release channel c1;
release channel c2;
}
RMAN 的执行时间和业务卡顿时间完全重合,确实很可疑。但是到这一步,只能证明 RMAN 在持续做 I/O,还不能证明它就是这次故障的根因。
继续到操作系统看磁盘:
iostat -x 1 20
目标设备多次接近 100% util,队列持续堆积,随机读延迟出现过:
188 ms
365 ms
694 ms
1096 ms
与此同时,CPU Idle 仍然超过 90%。
从这些数据看,RMAN 大概率把磁盘 I/O 压满了,LGWR 和业务 SQL 都在后面排队。但是这个判断还需要验证一下。
停掉 RMAN 之后,问题只解决了一半

停止 RMAN 后 I/O 压力下降,但异常等待没有完全消失。
当时业务还在受影响,和客户确认之后,决定先停止本次 RMAN 备份,看看等待是否会恢复。
停止之前先确认了本次备份类型、上一份可用备份、中断之后对 RPO 的影响以及后续补跑时间。正常情况下应该优先从 RMAN 客户端或者调度器取消任务,只有失去控制时,才从数据库终止会话。
alter system kill session '<sid>,<serial#>' immediate;
两个 Channel 退出之后,分别从数据库和操作系统进行确认:
select sid,
serial#,
status,
program,
module,
action,
event
from v$session
where lower(program) like '%rman%'
or lower(module) like '%rman%';
ps -ef | grep -i '[r]man'
接着查看实时等待。数据库版本是 11g,这里还踩了一个小坑:这个版本的 V$EVENTMETRIC 不能直接使用 EVENT_NAME 查询,需要关联 V$EVENT_NAME。
另外,TIME_WAITED 的单位是百分之一秒,换算平均等待毫秒时,需要乘以 10 再除以等待次数。
set lines 220 pages 100
col event_name format a35
select en.name event_name,
em.wait_count,
em.time_waited,
round(em.time_waited * 10 /
nullif(em.wait_count, 0), 2) avg_wait_ms,
em.num_sess_waiting
from v$eventmetric em
join v$event_name en
on em.event# = en.event#
where en.name in (
'log file sync',
'log file parallel write',
'db file sequential read',
'db file parallel read',
'read by other session',
'direct path read',
'control file parallel write'
)
order by em.time_waited desc;
RMAN 停止之后,实时窗口中的等待变成了:
V$EVENTMETRIC(RMAN 停止后实时窗口)
EVENT_NAME WAIT_COUNT AVG_WAIT_MS NUM_SESS_WAITING
--------------------------- ----------- ------------ ----------------
log file sync <n> 9.42 <n>
log file parallel write <n> 2.87 <n>
db file sequential read <n> 42.88 <n>
其中两个等待变化非常明显:
log file sync
431 ms → 9.42 ms
log file parallel write
52 ms → 2.87 ms
这说明 RMAN 确实对提交链路造成了很大影响。停止两个 Channel 后,LGWR 写延迟和前台 COMMIT 等待都恢复了。
但是 db file sequential read 仍然有 42.88ms。
RMAN 已经停了,数据文件的单块读为什么还是这么慢?看来问题还没有结束。
几十个 IOPS,磁盘却跑到了 100%
先重新查看数据库活动会话:
set lines 220 pages 100
col username format a15
col event format a35
col program format a40
select sid,
serial#,
username,
status,
sql_id,
event,
wait_class,
seconds_in_wait,
program
from v$session
where status = 'ACTIVE'
and type = 'USER'
order by seconds_in_wait desc;
RMAN 会话已经消失,但是业务 SQL 仍然在等待:
db file sequential read
read by other session
db file parallel read
操作系统继续使用 sar 观察磁盘:
sar -d 1 20
平均结果大致如下:
tps ≈ 60
avgqu-sz ≈ 2.4
await ≈ 40 ms
%util ≈ 100%
这个结果有点反常。设备只有几十个 IOPS,吞吐量也不高,%util 却接近 100%,平均响应时间仍然有 40ms。
再使用 pidstat 看一下具体是谁在读写:
pidstat -d 1 20
最大的几个 Oracle 进程每秒也只有几百 KB,并没有发现另一个隐藏的大吞吐任务。
到这里,前面的判断就需要改一下了。
RMAN 不是简单地把一块正常磁盘跑满。更可能是这块存储本身已经很慢,RMAN 的持续读写又把问题放大了。
这也是本次排查比较关键的一个转折。如果看到停掉 RMAN 后业务恢复,就直接把故障原因写成“备份抢占磁盘”,那么底层问题仍然会留在那里。下一次备份或者业务高峰到来时,问题大概率还会出现。
数据文件、Redo 和备份都在同一组盘

数据文件、Redo 和备份共用一组盘,任何后台 I/O 都可能放大业务等待。
接下来检查 Oracle 文件和备份目录分别落在哪里。
先查 redo:
set lines 220 pages 100
col member format a120
select l.group#,
l.thread#,
l.bytes / 1024 / 1024 size_mb,
l.status,
f.type,
f.member
from v$log l
join v$logfile f
on f.group# = l.group#
order by l.group#, f.member;
再查 datafile 和 controlfile:
set lines 220 pages 200
col file_name format a120
select tablespace_name,
file_id,
file_name
from dba_data_files
order by tablespace_name, file_id;
select name
from v$controlfile;
操作系统侧将这些路径映射到实际块设备:
lsblk -o NAME,KNAME,MODEL,SIZE,ROTA,TYPE,FSTYPE,MOUNTPOINT
df -hT
findmnt -T <redo_file>
findmnt -T <datafile>
findmnt -T <rman_backup_dir>
服务器对外呈现的是一个十几 TB 的 RAID Virtual Disk,块设备为 sda,主要目录都挂载在 /u01。再查看介质标识:
cat /sys/block/sda/queue/rotational
返回值为 1,后端使用的是旋转介质。
脱敏后的文件布局如下:
Datafile /u01/...
Online Redo /u01/...
Controlfile /u01/...
RMAN Backup /u01/backup/...
目录看起来是分开的,最终却全部落到了同一个块设备:
/u01
↓
sda
↓
同一套 HDD RAID
数据库从这组盘读取 datafile,LGWR 往同一组盘写 redo,RMAN 又从这组盘读取数据库,再将备份写回这组盘。两个 Channel 同时运行时,业务随机读、redo 同步小写和备份 I/O 全部挤在了一起。
现在已经可以解释 RMAN 为什么会明显放大延迟,但还不能解释 RMAN 停止之后,设备为什么仍然以很低的 IOPS 跑出 40ms 的 await。
只能继续往硬件层查。
dmesg | grep -Ei 'megaraid|degraded|predictive|failed|offline|rebuild'
grep -Ei 'megaraid|degraded|predictive|failed|offline|rebuild' \
/var/log/messages*
日志中多次出现下面的信息:
megaraid_sas ... CRIT - VD <id> is now DEGRADED
到这里,前面那些看起来有点矛盾的现象就能解释得通了。
不过这条日志只能证明 RAID Virtual Disk 出现过降级,不能仅凭一条历史日志就断定查询当时阵列仍然处于 Degraded,也不能确定是哪一块物理盘有问题。
后续还需要登录服务器带外管理或者使用 RAID 管理工具,继续检查 Virtual Disk、Physical Disk、Predictive Failure、Media Error、Rebuild、Hot Spare 和 Controller Cache 状态。
在这些状态没有确认之前,没有重启服务器,也没有尝试 Offline、Force Online、Clear Foreign 或重新初始化阵列。阵列已经丢失冗余时,操作错误很可能把一次性能故障变成数据不可用。
OGG 不是这次卡顿的主要原因
这套数据库还运行着 GoldenGate,所以排查过程中也检查了 Integrated Extract 和 LogMiner 会话。
set lines 220 pages 100
col program format a40
col module format a35
col event format a40
select sid,
serial#,
username,
status,
program,
module,
event,
wait_class,
seconds_in_wait
from v$session
where lower(program) like '%extract%'
or lower(program) like '%replicat%'
or lower(module) like '%goldengate%'
or lower(program) like '%logminer%'
order by status, seconds_in_wait desc;
当时能看到 LogMiner reader、builder、preparer 和 client 等进程,但是大多数处于空闲等待,没有发现与故障时间段相匹配的大量活动 I/O。
OS 侧也可以继续观察:
pidstat -d 1 20 | egrep 'extract|replicat|oracle|PID'
如果 OGG trail 和 checkpoint 也位于 /u01,它肯定会产生长期 I/O,不过从现场数据看,它不像这次突发卡顿的主要触发者,所以没有为了排除问题而直接停止复制链路。
写在最后
回顾一下整个故障过程。
故障期间,主要等待如下:
log file sync ≈ 431ms
log file parallel write ≈ 52ms
db file sequential read ≈ 65ms
RMAN 两个 Channel ≈ 98% / 99% 活跃
sda ≈ 100% util
r_await 多次达到数百毫秒
停止 RMAN 之后:
log file sync ≈ 9.42ms
log file parallel write ≈ 2.87ms
db file sequential read ≈ 42.88ms
sda 仍接近 100% util
从前后对比可以确认,两个 RMAN Channel 是本次业务卡顿的放大因素。它停止后,LGWR 写延迟和前台 COMMIT 等待很快恢复了。
但 RMAN 不是全部原因。数据文件单块读仍然接近 43ms,磁盘在只有几十个 IOPS 的情况下依旧接近 100% util,再结合文件布局和 RAID 降级日志,底层存储状态才是后续需要优先确认的问题。
当时也有人提出要不要重启数据库,我没有同意。重启修不好 RAID,也改变不了 datafile、redo 和 RMAN 共用一组盘的现状,还会清空大约 41GB 的 Buffer Cache。数据库重新启动之后,大量数据需要重新从磁盘读取,在单块读仍然接近 43ms 的情况下,冷缓存阶段很可能比重启之前更慢。
当然,这次问题到这里还没有完全结束。RAID 当前状态、具体故障盘以及后续存储整改方案,还需要结合控制器管理界面进一步确认。
本文记录一下这次 Oracle 卡顿的实际分析过程。很多数据库性能问题不一定会报 ORA 错误,也不一定会把 CPU 跑满。看到一个可疑任务停止后指标有所恢复,也不要急着结束排查。如果还有指标没有恢复,就继续沿着文件系统、块设备和硬件往下找。
好了,本次分享就到这了~