数据库慢的时候,先别急着进库查等待事件。主机层面的 CPU、负载和 I/O 往往能在一分钟内排除掉一半的可能:是整机被压满,还是只有某个 Oracle 进程在跑高,抑或是磁盘已经在排队。本文按“CPU 与负载 → 磁盘 I/O → 落到具体进程和 SQL”的顺序,整理一组在 CentOS 7 + Oracle 19c 单机上验证过的命令,示例输出来自一台 2 CPU 的测试机。
这些工具都来自 sysstat 和 procps 包,sar 的历史数据依赖 sysstat 服务持续采集,落在 /var/log/sa/saXX(XX 为日期)。如果服务没开,只能看实时数据,查不了历史。
一、先看 CPU 是否真的忙
1. 用 sar -u 看整体利用率
sar -u [interval] [count]
-u:报告 CPU 利用率,最常用的选项。[interval]:采样间隔(秒)。[count]:采样次数。省略时,sar 显示当天从零点开始的历史记录。
实时采样,每秒一次共五次:
[root@ora19c-single soft]# sar -u 1 5
Linux 3.10.0-1160.el7.x86_64 (ora19c-single) 2026年04月03日 _x86_64_ (2 CPU)
01时19分24秒 CPU %user %nice %system %iowait %steal %idle
01时19分25秒 all 0.00 0.00 0.00 0.00 0.00 100.00
01时19分26秒 all 0.00 0.00 0.00 0.00 0.00 100.00
01时19分27秒 all 0.00 0.00 0.00 0.00 0.00 100.00
01时19分28秒 all 0.00 0.00 0.00 0.00 0.00 100.00
01时19分29秒 all 0.50 0.00 0.50 0.00 0.00 98.99
平均时间: all 0.10 0.00 0.10 0.00 0.00 99.80
各列的含义:
%user:用户态程序占用的 CPU。Oracle 服务进程的大部分计算都记在这里。%nice:调整过优先级的用户进程占用,通常很低。%system:内核态占用。偏高常见于大量系统调用、网络包处理或内存回收。%iowait:CPU 等待磁盘 I/O 的时间比例。持续偏高说明瓶颈在存储,不在 CPU。%steal:虚拟化环境下被 Hypervisor 分给其他虚拟机的时间。非虚拟化环境为 0,偏高说明宿主机超分。%idle:空闲比例。持续低于 10% 才算 CPU 真的紧张。
看当天历史数据直接执行 sar -u;看指定日期:
sar -u -f /var/log/sa/sa03
[root@ora19c-single soft]# sar -u -f /var/log/sa/sa03
Linux 3.10.0-1160.el7.x86_64 (ora19c-single) 2026年04月03日 _x86_64_ (2 CPU)
00时50分01秒 CPU %user %nice %system %iowait %steal %idle
01时00分02秒 all 23.01 0.00 12.40 0.81 0.00 63.78
01时10分01秒 all 9.23 0.00 7.54 6.31 0.00 76.92
01时20分01秒 all 0.05 0.00 0.05 0.04 0.00 99.86
平均时间: all 10.34 0.00 6.42 2.32 0.00 80.92
历史数据是按小时汇总的,用来判断“业务高峰时 CPU 到过多少”,比实时采样更有说服力。
2. 用 uptime 看负载趋势
[root@ora19c-single soft]# uptime
01:45:28 up 5:07, 2 users, load average: 0.07, 0.09, 0.18
三个数字分别是 1、5、15 分钟的平均负载。判断标准不是绝对值,而是和 CPU 核数比较:
- 负载 < 核数:系统空闲。
- 负载 ≈ 核数:资源利用合理。
- 负载 > 核数:开始排队,需要看是 CPU 密集还是 I/O 阻塞。
上面这台是 2 核,负载 0.07/0.09/0.18,属于极低。注意 Linux 的 load average 把不可中断睡眠(D 状态,通常是等 I/O)也算进去,所以负载高但 %idle 很高时,先怀疑存储。
输出里的 up 5:07 是运行时长,2 users 是当前登录会话数——排查“是不是有人在做大批量操作”时,这两个信息顺手就看完了。
3. 用 mpstat 看每个核的分布
mpstat 1 10
[root@ora19c-single soft]# mpstat 1 10
Linux 3.10.0-1160.el7.x86_64 (ora19c-single) 2026年04月03日 _x86_64_ (2 CPU)
01时52分40秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
01时52分41秒 all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
01时52分42秒 all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
01时52分43秒 all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
01时52分44秒 all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
01时52分45秒 all 0.00 0.00 0.50 0.00 0.00 0.00 0.00 0.00 0.00 99.50
01时52分46秒 all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
01时52分47秒 all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
01时52分48秒 all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
01时52分49秒 all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
01时52分50秒 all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
平均时间: all 0.00 0.00 0.05 0.00 0.00 0.00 0.00 0.00 0.00 99.95
和 sar -u 的区别是 mpstat 能按核展开(mpstat -P ALL 1)。当 all 的均值不高、但某个核长期 100%,通常是单线程热点或中断绑定不均,属于另一类问题。
%usr 用户态
%sys 内核态
%iowait 等 I/O
%irq 硬中断
%soft 软中断(网络、调度)
%steal 被虚拟化层占用
%idle 空闲
二、再看磁盘 I/O 有没有排队
1. sar -d
sar -d 2 3
[root@ora19c-single soft]# sar -d 2 3
Linux 3.10.0-1160.el7.x86_64 (ora19c-single) 2026年04月03日 _x86_64_ (2 CPU)
01时32分15秒 DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
01时32分17秒 dev8-0 2.00 0.00 26.00 13.00 0.00 0.75 0.25 0.05
01时32分17秒 dev8-16 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
01时32分17秒 dev253-0 2.50 0.00 26.00 10.40 0.00 0.80 0.20 0.05
01时32分17秒 dev253-1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
01时32分17秒 dev253-2 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
01时32分17秒 DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
01时32分19秒 dev8-0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
01时32分19秒 dev8-16 1.99 0.00 50.75 25.50 0.00 0.00 0.00 0.00
01时32分19秒 dev253-0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
01时32分19秒 dev253-1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
01时32分19秒 dev253-2 1.99 0.00 50.75 25.50 0.00 0.00 0.00 0.00
平均时间: DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
平均时间: dev8-0 0.67 0.00 8.65 13.00 0.00 0.75 0.25 0.02
平均时间: dev8-16 1.00 0.00 27.62 27.67 0.00 0.17 0.17 0.02
平均时间: dev253-0 0.83 0.00 8.65 10.40 0.00 0.80 0.20 0.02
平均时间: dev253-1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
平均时间: dev253-2 1.00 0.00 27.62 27.67 0.00 0.17 0.17 0.02
这台机器上 %util 最大 0.05%、平均 0.02%,磁盘基本空闲;await 在 0.00–0.80 毫秒之间,avgqu-sz 全为 0,说明没有任何排队。反过来,如果 %util 长期接近 100%、avgqu-sz 持续大于 1、await 明显高于平时,就该怀疑存储侧了。
注意 dev253-x 是 device-mapper 设备(LVM 或 multipath),dev8-x 是底层 SCSI 设备。两层都要看:上层 %util 低但底层高,说明多路径或 LVM 在合并 I/O。
2. sar -b 看缓冲区和块设备吞吐
[root@ora19c-single soft]# sar -b
Linux 3.10.0-1160.el7.x86_64 (ora19c-single) 2026年04月03日 _x86_64_ (2 CPU)
00时50分01秒 tps rtps wtps bread/s bwrtn/s
01时00分02秒 706.05 366.65 339.41 62208.51 75864.69
01时10分01秒 502.58 277.44 225.14 33499.45 61826.41
01时20分01秒 3.83 2.35 1.48 320.62 34.04
01时30分01秒 1.48 0.09 1.39 1.35 35.89
平均时间: 303.49 161.63 141.86 24010.36 34439.11
tps 是每秒物理 I/O 次数,rtps/wtps 是读写分开的计数,bread/s/bwrtn/s 是每秒读写的块数。用它看“这台机器历史上写过多少”,比单次 iostat 更能反映基线。
3. iostat 的常用组合
# 所有磁盘的扩展统计,2 秒间隔,5 次
iostat -x 2 5
# 持续刷新
iostat -x 2
# 只看指定设备
iostat -x sda 2
# 只要 TPS 和吞吐量(KB)
iostat -d -k 2
# CPU 和磁盘一起看(MB)
iostat -c -d -x -m 2
# 自启动以来的累计统计
iostat -x
关键列:
r/s、w/s:每秒读写次数,即 IOPS。rkB/s、wkB/s:每秒读写数据量,即吞吐。%util:磁盘忙碌比例,最直观的瓶颈指标。await:平均 I/O 响应时间,包含排队时间。svctm:平均服务时间,不含排队(新版本内核已不再准确,仅作参考)。avgqu-sz:平均队列长度,持续大于 1 说明请求在排队。
%util 对 SSD 和全闪存阵列参考价值有限——一块 NVMe 可以 %util 很高但仍然很快,这时更要看 await 和业务侧响应时间。
三、从系统进程落到具体 SQL
CPU 高的时候,先确认是谁在吃。下面这条命令按 CPU 排序列出进程:
ps -e -o pcpu,pid,user,args | sort -k 3 -r | tail
[root@ora19c-single soft]# ps -e -o pcpu,pid,user,args | sort -k 3 -r | tail
0.0 27378 oracle ora_dbw0_sincdb
0.0 27368 oracle ora_dbrm_sincdb
0.0 27408 oracle ora_d000_sincdb
0.0 27345 oracle ora_clmn_sincdb
0.0 27753 oracle /oracle/app/19.3.0/db_1/bin/tnslsnr LISTENER -inherit
0.0 27384 oracle ora_ckpt_sincdb
0.1 27444 oracle ora_cjq0_sincdb
0.0 27432 oracle ora_aqpc_sincdb
0.0 694 dbus /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
0.0 697 chrony /usr/sbin/chronyd
这条命令按第 3 列(USER)倒序排,输出的是文件末尾,实际使用时更实用的写法是直接按 CPU 排序:
ps -e -o pcpu,pid,user,args --sort=-pcpu | head -20
拿到 Oracle 进程的 OS PID 后,回库中查它对应的会话:
col username format a15
col osuser format a10
col program format a20
set verify off
select a.username, a.osuser, a.program, spid, sid, a.serial#
from v$session a, v$process b
where a.paddr = b.addr
and spid = '&spid';
再顺着 SID 和 SERIAL# 看它正在执行什么、开了哪些游标:
set verify off
column username format a15
column sql_text format a60
undefine sid
undefine serial#
accept sid prompt 'sid: '
accept serial prompt 'serial#: '
select 'SQL Currently Executing:' from dual;
select b.username, a.sql_text
from v$sql a, v$session b
where b.sql_address = a.address
and b.sql_hash_value = a.hash_value
and b.sid = &sid
and b.serial# = '&serial';
select 'Open Cursors:' from dual;
select b.username, a.sql_text
from v$open_cursor a, v$session b
where b.sql_address = a.address
and b.sql_hash_value = a.hash_value
and b.sid = &sid
and b.serial# = '&serial';
v$sql 只保留最近执行过的语句,如果会话已经跑完,这条查询可能返回空——那就换 v$active_session_history 按 sample_time 回看。
四、排查顺序
uptime看负载是否超过核数,判断是 CPU 型还是 I/O 型。sar -u 1 5或mpstat 1 5看%user/%iowait/%idle。%iowait高就转sar -d/iostat -x,看%util、await、avgqu-sz。ps --sort=-pcpu找出吃 CPU 的进程,确认是不是 Oracle 服务进程。- 用 SPID 关联
v$session→v$sql,拿到具体 SQL 和会话。 - 需要历史对比时,
sar -u -f /var/log/sa/saXX和sar -d -f比实时采样更有价值。
监控数据只能定位方向,最终结论要落到一条 SQL、一个等待事件或一块盘上。
原文首发于微信公众号「止水研习社」。
文章评论