☰
Oracle AWR报告生成与解读:三分钟定位数据库性能瓶颈
2026/9/26 6:01:04 网站建设 项目流程

前阵子凌晨一点被电话叫醒,生产库CPU直接拉满,登上去看系统状态一切正常,监听也在跑,会话数没有爆发式增长,但业务就是卡死。靠直觉猜了十分钟毫无头绪,最后是一份AWR报告让问题原形毕露——一条漏了索引的SQL在半小时内吃掉了上亿次逻辑读。从那以后我就养成了习惯:Oracle数据库但凡出现“说不清道不明”的性能问题,第一件事就是拉AWR。

AWR全称Automatic Workload Repository,Oracle自动负载信息库。它会周期性地采集数据库的运行指标、等待事件、SQL执行统计、IO表现,存到SYSAUX表空间,形成一个又一个快照。你要做的就是从快照里提取一份报告,让“数据库这半小时到底在忙什么”变成一张可以逐行解读的清单。这篇文章就把我从生成到解读的完整套路梳理一遍,目标是让一个没怎么碰过AWR的人,也能在拿到报告后三分钟内圈出最可疑的方向。

1. 动手之前先搞懂:AWR在底层记了什么

很多人拿到AWR就直奔SQL部分,看到慢SQL就截图交差,这其实浪费了这份报告大半的价值。AWR的价值不在于“哪条SQL慢”,而在于它给了你一张完整的证据链:数据库的负载从哪来,等待花在哪里,资源消耗在哪个模块,IO和内存压力如何,最后才落到具体的SQL上。

1.1 快照:AWR的时间切片

AWR的核心机制是快照。后台进程MMON会按固定间隔(默认每60分钟)抓一次数据库的整体状态,包括会话数量、CPU使用、物理IO、锁等待、各个等待事件的累计时间,然后把状态数据写入SYSAUX表空间。一次快照代表一个时间点的“体检数据”,两次快照之间的差值,就是你看到的报告内容。

通常默认保留8天,之后老快照会被自动清理。所以要做历史趋势分析或者跨周对比,不能只靠默认配置,需要先把快照保留周期调长。

-- 查询当前快照配置 SELECT snap_id, to_char(begin_interval_time, 'yyyy-mm-dd hh24:mi:ss') AS begin_time, to_char(end_interval_time, 'yyyy-mm-dd hh24:mi:ss') AS end_time FROM dba_hist_snapshot ORDER BY snap_id DESC;

这个查询会列出当前库里已有的所有快照,起止快照ID是生成报告的关键输入参数。需要留意的是相邻快照的间隔,生产库如果间隔超过90分钟,中间这段负载信息就可能被“摊平”,不够精确。

1.2 AWR到底采了哪些数据

除了快照机制本身,AWR采集的数据维度也值得拉个清单,因为后面解读报告时每一部分都能对应到某个具体维度:

  • 等待事件:数据库会话都在等什么,是等磁盘IO、等锁、等日志写入,还是等CPU
  • SQL统计:每一条SQL的解析次数、执行次数、逻辑读、物理读、执行耗时
  • 会话活动:活跃会话数量、各状态会话的占比
  • IO统计:物理读写总量、Redo日志生成量、数据文件读写延迟
  • 内存命中率:Buffer Cache命中率、Shared Pool命中率、PGA使用情况
  • 资源使用:CPU使用率、内存使用、进程和会话数

这些数据组合起来,就能回答一个很朴素的问题:“数据库时间都花到哪里了”。记住这个思路,后面看报告时永远先找“时间去向”,而不是先翻SQL。

1.3 什么时候需要用AWR

我自己的判断标准很简单。第一种情况,业务反馈“系统变慢了”,但你在系统层面看不到明显的CPU或内存异常,这时候AWR能告诉你数据库内部到底发生了什么。第二种情况,CPU持续飙高,但你无法确定是应用发疯还是SQL有问题,AWR里的Top Event和SQL统计能快速给出方向。第三种情况,做版本升级、参数变更、索引调整之前,先拉一份基线报告,变更之后再拉一份对比,用数字说话。

反过来,有些问题AWR并不擅长。比如网络抖动导致的连接超时、应用层逻辑死循环、间歇性的锁冲突只持续十几秒,这些场景快照粒度太粗,可能被平均掉。那种时候更适合用ASH(Active Session History)做秒级回放。AWR看的是“整体趋势”,ASH看的是“瞬间现场”。

2. 生成AWR报告前的检查项:权限、快照、参数

既然要生成报告,首先得保证你手里的环境有权限,而且快照是真的够用。这一节列的是我每次操作前必过的三步检查,缺一步都可能白跑一趟。

2.1 权限检查:没有DBA也能生成

生成AWR报告需要ADVISOR权限,或者直接拥有DBA角色。实际工作中大多数DBA都是直接拿DBA角色在操作,但如果你是在开发环境或者审查别人的库,建议用最小权限原则。

-- 查看当前用户拥有的角色 SELECT * FROM user_role_privs; -- 直接把权限授予给对方(需要SYSDBA身份执行) GRANT ADVISOR TO your_user;

不过要注意,AWR报告实质是从dba_hist_*系列视图读取数据,即使你有了ADVISOR权限,查询底层的dba_hist_active_sess_history之类的视图可能还是受限。最省心的做法是:如果在生产环境长期需要生成报告,直接让账号具备DBA权限,否则每次权限排查的成本比生成报告本身还高。

2.2 快照数量和间隔:快照不足怎么处理

生成报告最少需要两个快照,一个是起点,一个是终点。实操中经常会遇到的问题是新装的库只有一条快照记录,或者刚重启过实例导致快照断档,这时候就需要手工补一个快照。

-- 手工生成一次快照 EXEC dbms_workload_repository.create_snapshot();

我等两三分钟,再执行一次:

EXEC dbms_workload_repository.create_snapshot();

两次快照之间有足够的时间间隔(建议至少5到10分钟),否则数据量太少,报告里大量指标会显示为0或者失真。如果等了十几分钟后发现还是没有新快照,就去查MMON进程的状态:

SELECT * FROM v$diag_info WHERE name = 'Diag Alert';

重点看告警日志里有没有MMON相关的报错信息,比如SYSAUX表空间不足导致快照写入失败。别问我是怎么知道的——真的遇到过因为SYSAUX满了导致AWR快照悄悄停了三天的情况。

2.3 快照保留和间隔调整

有时你需要把快照调密一些,比如重点业务系统在高峰期出现问题,默认的60分钟间隔粒度太粗,就改成30分钟甚至20分钟。

BEGIN dbms_workload_repository.modify_snapshot_settings( interval => 30, -- 快照间隔,单位分钟 retention => 20160 -- 保留时间,单位分钟,20160就是14天 ); END;

说句实在话,生产库我一般推荐30分钟间隔 + 14天保留。保留太久SYSAUX会膨胀,间隔太短又会产生海量快照数据。调整完之后正常等待即可,无需额外操作。

2.4 一个提前避坑项:确认SYSAUX空间

每次生成报告前,顺手查一下SYSAUX的剩余空间,别到了报告生成到一半才报错。

SELECT tablespace_name, ROUND(SUM(bytes) / 1024 / 1024 / 1024, 2) AS size_gb, ROUND(SUM(CASE WHEN maxbytes > 0 THEN maxbytes - bytes ELSE 0 END) / 1024 / 1024 / 1024, 2) AS free_gb FROM dba_data_files WHERE tablespace_name = 'SYSAUX' GROUP BY tablespace_name;

如果SYSAUX接近95%,先扩容或者清理历史快照,再做报告。扩数据文件用标准的ALTER TABLESPACE SYSAUX ADD DATAFILE ...即可。

3. 标准生成路径:awrrpt脚本与命令行的完整操作

检查项都过了,下面进入正题。生成AWR报告最通用、最不依赖图形界面的方式就是awrrpt.sql脚本,它在数据库的$ORACLE_HOME/rdbms/admin目录下。这个脚本是Oracle官方提供的,从10g到19c甚至21c都能用。

3.1 常规步骤:awrrpt.sql的完整交互流程

用任意一个能连上数据库的账号登录SQL*Plus:

sqlplus / as sysdba

然后执行:

SQL> @?/rdbms/admin/awrrpt.sql

脚本会问你几个问题,我按实际交互过程逐一说明。

第一个问题是报告类型,输入html或text:

Enter value for report_type: html

我通常选html,因为可以在浏览器里点开,排版清晰,方便给开发团队或者其他同事看。如果只是自己在终端里快速grep关键字,选text也行。

第二个问题是天数,也就是要往前翻几天的快照:

Enter value for num_days: 1

输入1表示看最近一天内的快照,如果你是临时排查几分钟前的问题,直接输入1完全够用。

脚本接着会列出所有满足条件的快照列表,格式大致是:

Instance DB Name Snap Id Snap Started Snap Level orcl ORCL 100 05 Apr 2025 10:00 1 101 05 Apr 2025 11:00 1

这时输入起始快照ID和结束快照ID:

Enter value for begin_snap: 100 Enter value for end_snap: 101

确认后脚本会在当前目录生成一个awrrpt_1_100_101.html的报表文件。如果目录下没有写权限,建议先cd到有权限的目录再执行脚本。

3.2 指定实例和RAC环境:awrrpti.sql的用法

在RAC环境下,awrrpt.sql默认生成整个数据库级别的报告,但如果你想针对某一个实例单独分析,就要用awrrpti.sql:

SQL> @?/rdbms/admin/awrrpti.sql

交互过程中会多一个问题:让你输入实例号。

Enter value for instance_num: 1

每个实例各生成一份报告,对比它们的Top Event,可以快速定位某个节点是不是出现了资源倾斜。RAC环境做性能分析时说句实话,全局报告信息量太大,单实例报告反而更聚焦。

3.3 定期自动化的变通做法

如果老板要求每天早上自动发一份昨天高峰期的AWR报告到邮箱,手动敲命令显然不行。我在生产环境里用的是shell脚本+crontab的组合。脚本里把参数用here document的方式传给awrrpt.sql:

#!/bin/bash export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1 export PATH=$ORACLE_HOME/bin:$PATH export ORACLE_SID=orcl sqlplus -s / as sysdba <<EOF set echo off set feedback off @?/rdbms/admin/awrrpt.sql EOF

脚本里省去了交互输入,直接让它生成。当然实际自动化脚本还需要在交互提示处预填值,用expect或sqlplus的参数化方式处理。核心思路是先把命令通过手工跑顺,再套自动化框架。

3.4 报告文件命名与归档习惯

别小看文件命名这件小事。没归档习惯的时候,一个月后想找上周的报告,文件名全是awrrpt_1_101_105.html,根本分不清哪个是哪个。我现在的命名规范是:

AWR_<DBNAME>_<实例号>_<起始时间>_<结束时间>.html

比如AWR_ORCL_1_20250405_10_00_20250405_11_00.html。配合按月份建目录,半年后回溯问题也有据可查。每次出问题拉完报告,建议顺手把当时的告警时间、变更记录、结论一并写进一个简单的文本里,和报告放一起。这份额外的“上下文”比报告本身还值钱。

4. 报告解读路线图:三分钟锁定嫌疑方向

报告生成好只是开始,真正见功力的是解读。我一直强调,AWR是拿来缩小排查范围的,不是拿来当唯一证据的。它的正确用法是:你先从AWR里圈出几条可疑线索,再沿着线索去看具体的SQL、看锁、看执行计划,最后确定根因。

4.1 第一眼:总负载指标——DB Time和Elapsed Time

打开HTML报告,先不要急着往下翻,看顶部Report Summary里的DB Time和Elapsed Time这两个数字。它们的比值是你对整个数据库负载的第一判断。

  • Elapsed Time:快照覆盖的物理时间,比如从10:00到11:00,就是60分钟
  • DB Time:所有会话在CPU上执行的时间+所有会话等待的时间总和

如果DB Time / Elapsed Time接近1,说明数据库平均有1个会话在忙;如果这个比值是5、是10,说明平均同时有5到10个会话在争抢资源,负载已经很重了。反之比值小于0.5,那数据库本身并不忙,瓶颈可能在外面,比如网络、应用服务器。

举个例子,我看到一份报告里DB Time是3600秒,Elapsed Time是600秒,比值是6。这意味着高峰期平均6个会话在并发处理,配合Top Event一看,全是db file sequential read,马上就知道是SQL执行计划有问题,在大量做单块读。

4.2 第二眼:Top 10 Foreground Events——等待事件是入口

Top 10 Foreground Events by Total Wait Time这一段,直接给出了数据库会话把时间花在哪里。判断标准很简单:选第一个“非空闲等待事件”往下钻。

空闲等待事件(如SQL*Net message from client)是会话闲着的正常状态,不是问题。真正要关注的是下面这些:

等待事件常见含义优先排查方向
db file sequential read单块读,通常是索引扫描或者按ROWID回表检查SQL执行计划、索引选择
db file scattered read多块读,常见全表扫描或FTS检查是否缺索引,是否有不必要的全扫
enq: TX - row lock contention行锁竞争,有事务在锁同一行排查阻塞会话、应用事务逻辑
log file sync会话等待Redo日志写入磁盘检查磁盘IO、提交频率
log file parallel write日志文件并行写入慢检查日志文件所在存储
library cache: mutex X / latch free硬解析或Shared Pool锁竞争检查SQL解析次数、Bind变量使用情况
CPU + Wait for CPU会话在排队等CPU检查服务器CPU核数和整体负载

看见没有,等待事件本身不说明根因,但它能告诉你该往哪个方向查。比如log file sync等待高,你能确定的只是重做日志写入慢,下一步要么查磁盘IO延迟,要么查应用是不是高频提交。

4.3 第三眼:SQL ordered by Elapsed Time——锁定元凶

等待事件指向了方向,再往下翻就是定罪证据。SQL Statistics部分有多个子章节,最常用的是:

  • SQL ordered by Elapsed Time:最耗时的SQL
  • SQL ordered by CPU Time:消耗CPU最多的SQL
  • SQL ordered by Gets:逻辑读最多的SQL
  • SQL ordered by Physical Reads:物理读最多的SQL

正常套路是把这两类重合的SQL挑出来。比如等待事件是db file sequential read,那就看SQL ordered by Gets列表的头部,十有八九是同样的几条SQL。

拿到SQL ID后,用SELECT sql_text FROM dba_hist_sqltext WHERE sql_id = '...'把完整SQL文本拉出来,再配合DBMS_XPLAN.DISPLAY_AWR看历史执行计划。

SELECT * FROM TABLE(dbms_xplan.display_awr( sql_id => 'your_sql_id', format => 'ALL' ));

这一步基本就能看出执行计划是否合理,是不是在走全表扫描,是不是索引列被函数包裹导致索引失效。

4.4 一个可复制的3分钟定位流程

我把这套流程压缩成固定动作,每次拿到AWR都照着做:

  1. 第0到30秒:看DB Time / Elapsed Time比值,判断负载高低
  2. 第30到60秒:看Top 10 Foreground Events,记录第一个非空闲等待事件
  3. 第60到120秒:翻到SQL ordered by Elapsed Time和SQL ordered by Gets,比对Top 3 SQL的SQL ID
  4. 第120到150秒:用SQL ID拉完整SQL文本和执行计划,确认是解析问题、缺索引、还是数据量膨胀
  5. 第150到180秒:回看等待事件和SQL,形成结论,写处理方案

这套流程用在十几个不同项目的故障排查里,基本没失过手。真正难的不是流程,而是你能不能在5分钟内读懂执行计划,那是另一门功夫。

5. 等待事件的下钻手法:三类典型场景拆解

等待事件虽然看起来种类多,但生产环境最常见的就是那几类。我挑三个高概率场景,把分析思路完整走一遍,下次你在报告里看到同样的等待事件,能直接套用。

5.1 db file sequential read:索引和回表的天下

这类等待的本质是Oracle在做“单块读”,一次IO只读一个数据块,通常发生在索引扫描或者通过ROWID回表时。如果它的等待时间占比很高,首先要怀疑的就是SQL执行计划走了低效索引。

有一次排查一个批处理任务,AWR报告里db file sequential read占了总等待时间的46%,SQL ordered by Gets排名第一的是一条关联了五张表的查询。拉出执行计划一看,驱动表走了某个选择性很差的索引,优化器估算行数只有50行,实际却有20万行。这就是典型的统计信息过期。

处理方式很直接:先对该表重新收集统计信息,再看执行计划是否恢复。如果还不行,就用dbms_stats.gather_table_stats加method_opt => 'for all columns size auto'做直方图补充。

5.2 enq: TX - row lock contention:真正的锁纠纷

enq: TX - row lock contention出现时,不用想也知道是有会话在锁同一行或者同一批数据。AWR报告里能看到等待次数和平均等待时间,但看不到是谁锁了谁,这一步必须靠实时视图补刀。

-- 找阻塞者会话 SELECT blocking_session, sid, serial#, username, event, wait_class, seconds_in_wait FROM v$session WHERE blocking_session IS NOT NULL;

查到阻塞者后,再看它的SQL:

SELECT sql_text FROM v$sql WHERE sql_id = ( SELECT sql_id FROM v$session WHERE sid = 阻塞者SID );

AWR在这个场景里的真正价值,是指明了“等待事件存在”,然后你要靠v$session实时确认是谁在阻塞。典型原因无非三种:应用开了事务不提交、批量更新走了全表锁、两条业务路径更新同一行数据。解决方式各有侧重,但有一点共通——优化应用事务的粒度和顺序,比在数据库层面调参有用得多。

5.3 log file sync:磁盘IO和应用提交频率的较量

log file sync是指会话提交事务后,等Redo日志写入磁盘完成才返回。等待时间高有两个方向:一是日志文件存放的磁盘IO太慢,二是应用提交频率太高。

先查日志文件存放在哪:

SELECT group#, member FROM v$logfile;

如果日志文件放在机械硬盘或者和业务数据混在同一块盘上,第一优先级是迁移日志文件到独立的高性能存储。如果磁盘本身没问题,那就是应用每秒提交几百上千次小事务,每次提交都触发一次日志写入,累加起来就成了瓶颈。

这种问题用纯数据库手段很难根治,最好的解法是应用侧合并提交:把几千条插入放在一个事务里批量提交。AWR报告里User Commits这个指标可以直观地看到提交频率,如果每秒提交数达到几百甚至上千,基本就是应用逻辑的问题。

5.4 CPU瓶颈:CPU + Wait for CPU的排队逻辑

当服务器CPU核数不够用的时候,Top Event里会出现CPU + Wait for CPU。它的判断方式比较特殊,不是物理等待,而是会话已经在了run queue里等着被调度。和它匹配的指标是DB CPU和CPU Cores。

此时先看DB CPU占总DB Time的比例,再回到SQL ordered by CPU Time看哪条SQL吃掉最多CPU。通常这种场景要么是某条SQL在做大规模计算、排序、全表扫描,要么是数据库跑的东西超出了机器本身的能力上限。前者优化SQL,后者可能要扩容。但扩容之前先把SQL优化到位,很多CPU瓶颈其实是低效执行计划造成的假象。

6. 快照策略、基线对比和我在生产环境踩过的坑

报告会解读了,还要会做长期养护。AWR是持续运转的机制,日常配置不到位,等故障发生时再想起来看,可能已经什么都没了。

6.1 快照保留周期:别让历史数据悄无声息消失

默认8天保留期对一般系统够用,但要支撑“上周同一时间也慢”这种周期性问题的排查,最好还是延长保留。建议用前面提到的modify_snapshot_settings改到14天到30天,视SYSAUX空间而定。

不用太担心SYSAUX爆掉,每个AWR快照的数据量其实就几十MB左右,一天48个快照,也就是2到3GB的量级。除非快照间隔改到5分钟再加上超长保留期,否则SYSAUX的膨胀速度是可控的。

6.2 基线(Baseline):给性能对比找参照物

基线是AWR里被很多人忽略的功能。它的本质是把某段时期的快照打上一个标记,之后任何时间都可以拿当前负载和基线做对比,定性能退化是否真实存在。

BEGIN dbms_workload_repository.create_baseline( start_snap_id => 100, end_snap_id => 110, baseline_name => 'PEAK_BASELINE' ); END;

比如每周日晚上的批量作业高峰,把这时段的快照存成基线,到下个周日再生成AWR,用awrddrpt.sql直接做两段时期的对比报告:

SQL> @?/rdbms/admin/awrddrpt.sql

这个脚本会要求输入两组快照ID,其实就是两个对比窗口。生成的差异报告能直接看出哪个等待事件增长了、哪条SQL的耗时翻了倍,对做性能回归分析非常有用。

6.3 我在生产环境踩过的几个典型坑

写到最后,把这几年实际踩过的坑集中说一遍,每一个都付出过真金白银的代价。

第一个坑是SYSAUX满导致快照静默停止。某次排查历史性能数据,发现最近三天完全没有快照,查告警日志才发现SYSAUX使用率95%,MMON写入失败后自动跳过快照采集。之后我每次巡检都会查SYSAUX空间,超过80%就提前扩容。

第二个坑是新建实例后没有足够快照。新搭建的数据库当天就出问题,想生成AWR,结果只有1个快照记录,根本没法生成。正确的做法是新库上线后第一时间手工造两个快照,中间隔个十几分钟,为的就是让报告机制先跑起来。

第三个坑是时区问题导致快照时间看起来错乱。AWR里记录的begin_interval_time用的是数据库时区,如果你用客户端本地时间去对比,经常会发现快照时间“对不上”。跨时区排查时,直接对begin_interval_time做AT TIME ZONE转换,避免误判。

第四个坑是看到等待事件就直接断言根因。前面强调过,等待事件只是线索不是结论。有次我盯着enq: TX - row lock contention查了半天阻塞会话,最后发现是应用侧一个微服务在循环调用同一个更新接口,事务根本没提交。如果只看数据库,永远找不到根因。跨团队协作时,AWR报告要能讲成一个完整的故事:谁在什么时间做了什么,引发了什么样的等待,最后影响了谁。

6.4 扩展思路:把AWR纳入日常巡检

AWR不只是故障排查工具,也可以变成日常巡检的一部分。我习惯每周一早晨自动生成上周的AWR报告并归档,顺手扫一眼几个关键指标的变化趋势。指标突变往往比绝对数值更值得警惕,比如Top Event从前一周的db file scattered read变成log file sync,即便业务还没感觉到慢,背后一定发生了什么。

这个习惯坚持半年后,你会对本系统的基础负载特征非常敏感。一有异常,立刻能判断出“这次和上次不一样在哪”。

说到底,AWR像数据库的“黑匣子”,它不负责告诉你问题怎么解决,但它负责把问题发生的全过程记下来。解读报告的能力强弱,直接决定你是在快速排查还是大海捞针。把上面这套生成和解读的流程跑熟,下一次性能告警来临时,你至少能在一杯咖啡的时间内指出正确的方向。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询