尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Oracle AWR报告生成与解读:三分钟定位数据库性能瓶颈

Oracle AWR报告生成与解读:三分钟定位数据库性能瓶颈 前阵子凌晨一点被电话叫醒生产库CPU直接拉满登上去看系统状态一切正常监听也在跑会话数没有爆发式增长但业务就是卡死。靠直觉猜了十分钟毫无头绪最后是一份AWR报告让问题原形毕露——一条漏了索引的SQL在半小时内吃掉了上亿次逻辑读。从那以后我就养成了习惯Oracle数据库但凡出现“说不清道不明”的性能问题第一件事就是拉AWR。AWR全称Automatic Workload RepositoryOracle自动负载信息库。它会周期性地采集数据库的运行指标、等待事件、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、等锁、等日志写入还是等CPUSQL统计每一条SQL的解析次数、执行次数、逻辑读、物理读、执行耗时会话活动活跃会话数量、各状态会话的占比IO统计物理读写总量、Redo日志生成量、数据文件读写延迟内存命中率Buffer Cache命中率、Shared Pool命中率、PGA使用情况资源使用CPU使用率、内存使用、进程和会话数这些数据组合起来就能回答一个很朴素的问题“数据库时间都花到哪里了”。记住这个思路后面看报告时永远先找“时间去向”而不是先翻SQL。1.3 什么时候需要用AWR我自己的判断标准很简单。第一种情况业务反馈“系统变慢了”但你在系统层面看不到明显的CPU或内存异常这时候AWR能告诉你数据库内部到底发生了什么。第二种情况CPU持续飙高但你无法确定是应用发疯还是SQL有问题AWR里的Top Event和SQL统计能快速给出方向。第三种情况做版本升级、参数变更、索引调整之前先拉一份基线报告变更之后再拉一份对比用数字说话。反过来有些问题AWR并不擅长。比如网络抖动导致的连接超时、应用层逻辑死循环、间歇性的锁冲突只持续十几秒这些场景快照粒度太粗可能被平均掉。那种时候更适合用ASHActive 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*Plussqlplus / as sysdba然后执行SQL ?/rdbms/admin/awrrpt.sql脚本会问你几个问题我按实际交互过程逐一说明。第一个问题是报告类型输入html或textEnter 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和结束快照IDEnter 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.sqlSQL ?/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_SIDorcl 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最耗时的SQLSQL ordered by CPU Time消耗CPU最多的SQLSQL ordered by Gets逻辑读最多的SQLSQL 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都照着做第0到30秒看DB Time / Elapsed Time比值判断负载高低第30到60秒看Top 10 Foreground Events记录第一个非空闲等待事件第60到120秒翻到SQL ordered by Elapsed Time和SQL ordered by Gets比对Top 3 SQL的SQL ID第120到150秒用SQL ID拉完整SQL文本和执行计划确认是解析问题、缺索引、还是数据量膨胀第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;查到阻塞者后再看它的SQLSELECT 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像数据库的“黑匣子”它不负责告诉你问题怎么解决但它负责把问题发生的全过程记下来。解读报告的能力强弱直接决定你是在快速排查还是大海捞针。把上面这套生成和解读的流程跑熟下一次性能告警来临时你至少能在一杯咖啡的时间内指出正确的方向。
返回列表