简介:本资源是Oracle数据库DBA及性能优化工程师必备的AWR报告深度解析指南,聚焦数据库性能诊断与调优实战场景。黄伟波系统梳理AWR(Automatic Workload Repository)核心机制,涵盖统计信息采集原理、STATISTICS_LEVEL参数调控策略、AWR快照管理、多维度报告生成(含基线对比与时段对比),并逐项解读报告关键模块——数据库负载概况、SQL执行效率、等待事件分析、缓存命中率、硬解析次数及活动会话历史等。资源为单个PDF文件,共1个,大小7.13MB,内容结构清晰,含完整目录与实操指引,适合作为日常性能分析手册或进阶学习参考资料。目前已有226人学习下载,内容兼顾理论基础与落地分析逻辑,既适合初入DBA岗位的技术人员建立AWR分析框架,也对资深工程师排查复杂性能瓶颈具有直接参考价值。
1. AWR报告不是“看一眼就懂”的性能快照:它是Oracle数据库在特定时间窗口内所有关键指标的压缩黑匣子,专治“系统变慢但查不出哪慢”的玄学故障
你刚收到一份awr_20240515_123456789.pdf,打开发现满屏数字、百分比、TOP SQL列表和一堆缩写(DB Time、Buffer Gets、Physical Reads、latch free、gc current block busy……),第一反应是“这玩意儿到底想告诉我什么?”——别急,这不是考Oracle认证的模拟题,而是一份带时间戳的数据库健康体检报告。AWR(Automatic Workload Repository)不是日志,也不是实时监控面板;它是Oracle每小时自动采样一次、默认保留8天的结构化性能快照集合,背后由DBA_HIST_*系列数据字典视图支撑。这份PDF只是其中某两个快照(比如Snapshot ID 1234 和 1235)之间差异的可视化摘要。它真正解决的,是DBA和运维工程师最头疼的三类场景:业务高峰期响应延迟突增却找不到SQL瓶颈、数据库CPU持续95%但top -p看不出具体进程、应用报“连接超时”而监听日志里没有错误。适合人群非常明确:能登录数据库执行SQL、会看Linux top/ps、熟悉基本Oracle架构(SGA/PGA、shared pool、buffer cache)的中级以上从业者。新手硬啃PDF只会陷入“每个词都认识,连起来看不懂”的困境;老手跳过AWR直接调优,则大概率在错误的方向上优化三天——因为AWR告诉你“哪里出了问题”,而不是“怎么修”。本篇不讲AWR原理源码,只聚焦一件事:如何从这份PDF出发,15分钟内定位到真实瓶颈,并验证你的判断是否成立。
2. 用Oracle自带工具生成并导出AWR报告:避开HTML乱码、PDF缺失图表、快照ID错配三大翻车点
AWR报告本身是Oracle内部采集的数据,PDF只是导出格式之一。很多人卡在第一步:明明执行了@?/rdbms/admin/awrrpt.sql,生成的却是乱码HTML或空白PDF。这不是环境问题,而是参数链路断裂——从快照选择、格式指定到字符集处理,每一步都有隐性依赖。
2.1 确认数据库版本与AWR基础状态:先看“有没有”,再谈“怎么导”
AWR在Oracle 10g起默认启用,但需确认当前实例是否真在运行。不要依赖SELECT * FROM V$VERSION;就认为万事大吉,必须验证AWR本身是否激活:
-- 检查AWR是否启用(返回TRUE才有效) SELECT VALUE FROM V$PARAMETER WHERE NAME = 'statistics_level'; -- 查看最近3天的快照是否存在(关键!无快照则报告为空) SELECT SNAP_ID, BEGIN_INTERVAL_TIME, END_INTERVAL_TIME FROM DBA_HIST_SNAPSHOT WHERE BEGIN_INTERVAL_TIME > SYSDATE - 3 ORDER BY SNAP_ID DESC; -- 检查AWR保留策略(默认8天,若被修改过需注意时间窗口) SELECT RETENTION, INTERVAL, TOPNSQL FROM DBA_HIST_WR_CONTROL;提示:
STATISTICS_LEVEL必须为TYPICAL或ALL,BASIC模式下AWR不采集核心性能数据。若返回BASIC,需执行ALTER SYSTEM SET STATISTICS_LEVEL=TYPICAL SCOPE=BOTH;并重启实例(生产环境慎用)。快照表DBA_HIST_SNAPSHOT为空?说明MMON进程异常或AWR被手动禁用,此时任何报告都是空壳。
2.2 用SQL*Plus生成标准HTML报告:绕过图形界面依赖,确保内容完整
图形化工具(如EM Express、OEM)生成PDF常因浏览器渲染丢失图表或中文乱码。最稳路径是纯命令行+SQL*Plus,且必须指定字符集:
# 登录时强制指定NLS_LANG(关键!否则中文变问号) export NLS_LANG="AMERICAN_AMERICA.AL32UTF8" sqlplus / as sysdba @?/rdbms/admin/awrrpt.sql执行后按提示输入:
Type of Report: 选1(HTML格式,PDF易丢图,HTML可直接浏览器打开)Number of days: 输入分析时间范围(如7表示查最近7天内的快照)Begin Snapshot Id: 手动输入起始快照ID(从2.1查询结果中选)End Snapshot Id: 输入结束快照ID(必须大于起始ID)Report Name: 输入文件名(如awr_report_20240515.html)
生成的HTML文件会包含全部图表(Top SQL、Instance Efficiency、Wait Events)、可点击跳转的章节导航,且中文正常显示。不要用@?/rdbms/admin/awrrpti.sql(交互式脚本),它默认走DBMS_WORKLOAD_REPOSITORY.AWR_REPORT_HTML,对字符集更敏感。
2.3 将HTML转为可靠PDF:用wkhtmltopdf而非浏览器另存为
浏览器“打印为PDF”会裁剪表格、压缩字体、丢失SVG图表。正确做法是用wkhtmltopdf命令行工具(开源,支持Linux/Windows):
# Ubuntu安装(CentOS用yum) sudo apt-get install wkhtmltopdf # 转换命令(关键参数:--enable-local-file-access避免CSS加载失败) wkhtmltopdf --enable-local-file-access \ --page-size A4 \ --margin-top 20 \ --margin-bottom 20 \ awr_report_20240515.html \ awr_report_20240515.pdf参数说明:
--enable-local-file-access允许读取本地CSS/JS资源;--page-size A4保证表格不被截断;--margin-*留出页眉页脚空间。转换后用pdfinfo awr_report_20240515.pdf检查页数是否与HTML一致(通常HTML约20页,PDF应相同)。
3. 报告核心页逐页拆解:从“DB Time”开始,像读心电图一样识别性能脉搏
AWR PDF共分10+章节,但90%的有效信息集中在前5页。不要从头读到尾,要按因果链逆向扫描:先看结果(DB Time飙升),再找原因(Wait Events),最后定位对象(Top SQL/Segments)。
3.1 第1页:Cache Sizes与Load Profile——先确认“系统是否在干正事”
这是报告的“生命体征页”。重点看两组数字:
| 指标 | 正常范围 | 异常信号 | 诊断逻辑 |
|---|---|---|---|
| DB Time (s) | 应接近Elapsed Time (s)× CPU核数 | DB Time >> Elapsed Time × CPU核数 | CPU严重争用,大量会话排队 |
| Physical Reads (blocks) | < 10% of Logical Reads | > 20% | Buffer Cache命中率低,磁盘IO压力大 |
| User Calls | 与业务QPS匹配 | 突增/突降 | 应用层流量异常(非数据库问题) |
| Redo size (bytes) | 与事务量正相关 | 突增 | 大量DML或归档压力 |
实操技巧:用Ctrl+F搜索
DB Time,直接跳到该行。若DB Time为12,345.67秒,而Elapsed Time为3,600秒(1小时),服务器为8核,则理论最大DB Time为28,800秒(3600×8)。当前值12,345占理论值43%,属中等负载;若达25,000,则说明几乎所有CPU都在处理数据库请求,需立即查Wait Events。
3.2 第2页:Top 5 Timed Foreground Events——找到“卡点”的黄金5分钟
这是报告的心脏页。列出耗时最长的5个等待事件,按“时间占比”降序排列。永远不要跳过这一页——它直接告诉你数据库“卡在哪”。
常见等待事件解读:
DB CPU:CPU计算时间,非等待。占比高=SQL执行效率低或硬件不足。log file sync:提交时等待redo写入磁盘。>5ms/次=存储写延迟高。enq: TX - row lock contention:行锁冲突,典型于高并发更新同一行。gc buffer busy acquire:RAC环境中跨节点缓存块争用。direct path read temp:排序/哈希操作溢出到临时表空间。
血泪经验:曾遇一例
log file sync平均等待12ms,排查发现存储阵列电池失效,write cache被禁用。更换电池后降至0.8ms。等待事件的平均等待时间(Avg wait ms)比等待次数更重要——100次等待每次1ms,不如1次等待100ms致命。
3.3 第3页:SQL Statistics——锁定“罪魁祸首SQL”的三把尺子
此页列出Top SQL,但不能只看“Executions”或“Buffer Gets”。要用三个维度交叉验证:
- Elapsed Time per Exec (s):单次执行耗时。>1秒的SQL必查执行计划。
- CPU Time per Exec (s):排除IO干扰,纯计算耗时。若远高于Elapsed Time,说明SQL在CPU上空转(如笛卡尔积)。
- Rows Processed per Exec:单次处理行数。若为0或极小(如0.001),说明SQL频繁执行但几乎不返回数据(典型于应用层循环查单条记录)。
避坑动作:用
SELECT sql_id, sql_text FROM v$sql WHERE sql_id = 'abc123def456';查出SQL原文,务必检查绑定变量。AWR中显示的SQL可能含:,实际执行时变量值不同会导致执行计划剧变。例如WHERE status = :1,当:1='ACTIVE'走索引,:1='INACTIVE'可能全表扫描。
4. 避坑:AWR报告分析中最常见的5个认知陷阱与技术雷区
AWR报告本身不会撒谎,但人容易误读。以下5个坑,我至少踩过3次,每次修复都花掉半天以上。
4.1 现象:报告中“DB Time”数值巨大,但服务器top显示CPU仅30%
原因:DB Time是所有会话等待+执行的总时间,不是CPU占用率。8核服务器1小时内DB Time可达28,800秒,即使CPU只用30%,DB Time也可能达8,000秒(因会话排队等待)。
解决:看DB Time / Elapsed Time比值。若为2.5,说明平均每个时刻有2.5个会话在活动(含等待),属合理并发;若达10,说明严重排队,需查Top Foreground Events。
4.2 现象:Top SQL的“Buffer Gets”高达千万,但执行计划显示走了索引
原因:“Buffer Gets”统计的是逻辑读次数,索引范围扫描+回表可能产生海量逻辑读。例如INDEX RANGE SCAN返回1万行,再TABLE ACCESS BY INDEX ROWID回表1万次,Buffer Gets即为2万。
解决:结合Rows Processed看。若Buffer Gets / Rows Processed > 100,说明单行访问成本过高,需检查索引是否覆盖、是否需要复合索引。
4.3 现象:报告中“Physical Reads”很低,但应用端IO等待明显
原因:AWR的Physical Reads只统计数据库层读取,不包括操作系统缓存(Page Cache)或存储层缓存。若应用直写文件或使用Direct I/O,这部分IO不计入AWR。
解决:用iostat -x 1看%util和await,对比DBA_HIST_SYSMETRIC_HISTORY中Physical Read IO Requests Per Sec,若后者远低于iostat值,说明IO发生在数据库外。
4.4 现象:两个快照间“Shared Pool Free Memory”从1GB降到100MB,怀疑内存泄漏
原因:Shared Pool Free Memory波动是正常现象。Oracle会动态在Shared Pool内分配/释放内存,Free Memory低≠内存不足,要看library cache hit ratio是否<99%或ORA-04031错误是否出现。
解决:查V$SHARED_POOL_ADVICE,看当前Size下预估的Parse Time Savings。若增加Shared Pool Size对节省解析时间影响微乎其微,则无需扩容。
4.5 现象:报告中“Logons per sec”突增10倍,但业务量未变化
原因:应用未复用连接,每次请求新建连接(Connection Per Request)。AWR统计的是登录次数,非活跃会话数。
解决:查V$SESSION_CONNECT_INFO中AUTHENTICATION_TYPE为DATABASE的会话,结合应用日志确认连接池配置。典型案例如Java应用未配置HikariCP的maximumPoolSize。
5. 用AWR数据反向验证调优效果:不是“改完就完”,而是“数据闭环”
调优不是改完一个参数就结束,而是建立“问题→假设→验证→结论”的数据闭环。AWR是唯一能提供跨时段对比的权威数据源。
5.1 创建基线快照:为调优设定可量化的起点
在实施任何变更前(如加索引、调SORT_AREA_SIZE、改PGA_AGGREGATE_TARGET),必须保存当前性能基线:
-- 手动生成一个快照(确保采集到变更前状态) EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT; -- 查看刚生成的快照ID SELECT MAX(SNAP_ID) FROM DBA_HIST_SNAPSHOT; -- 记录此时关键指标(用SQL直接查,不依赖报告) SELECT (SELECT VALUE FROM DBA_HIST_SYSMETRIC_SUMMARY WHERE METRIC_NAME = 'Database Time Per Sec' AND SNAP_ID = &SNAP_ID) AS db_time_per_sec, (SELECT VALUE FROM DBA_HIST_SYSMETRIC_SUMMARY WHERE METRIC_NAME = 'Average Synchronous Single-Block Read Latency' AND SNAP_ID = &SNAP_ID) AS avg_read_latency_ms FROM DUAL;为什么不用报告?因为报告是区间汇总,而基线需要精确到秒级的瞬时值。
DBA_HIST_SYSMETRIC_SUMMARY提供每分钟粒度的指标,比报告更灵敏。
5.2 调优后对比:用SQL直接查AWR历史视图,拒绝主观判断
改完后等待1小时,再生成新快照,用SQL对比:
-- 对比两次快照的DB Time和等待事件占比(核心!) SELECT s1.SNAP_ID AS snap_before, s2.SNAP_ID AS snap_after, ROUND((s2.DB_TIME - s1.DB_TIME) / (s2.ELAPSED_TIME - s1.ELAPSED_TIME), 2) AS db_time_per_sec_after, ROUND(s1.DB_TIME / s1.ELAPSED_TIME, 2) AS db_time_per_sec_before, -- 关键:等待事件占比变化 (SELECT ROUND(100 * SUM(w2.WAIT_TIME_MILLI) / NULLIF(SUM(w2.TIME_WAITED_MILLI), 0), 2) FROM DBA_HIST_SYSTEM_EVENT w2 WHERE w2.SNAP_ID = s2.SNAP_ID AND w2.EVENT_NAME = 'log file sync') AS log_sync_pct_after, (SELECT ROUND(100 * SUM(w1.WAIT_TIME_MILLI) / NULLIF(SUM(w1.TIME_WAITED_MILLI), 0), 2) FROM DBA_HIST_SYSTEM_EVENT w1 WHERE w1.SNAP_ID = s1.SNAP_ID AND w1.EVENT_NAME = 'log file sync') AS log_sync_pct_before FROM ( SELECT SNAP_ID, DB_TIME, ELAPSED_TIME FROM DBA_HIST_SYSMETRIC_SUMMARY WHERE SNAP_ID = &SNAP_BEFORE ) s1, ( SELECT SNAP_ID, DB_TIME, ELAPSED_TIME FROM DBA_HIST_SYSMETRIC_SUMMARY WHERE SNAP_ID = &SNAP_AFTER ) s2;输出示例:若
log_sync_pct_before=12.3%,log_sync_pct_after=3.7%,且db_time_per_sec_before=8.2,db_time_per_sec_after=5.1,则证明调优有效。拒绝用“感觉快了”这种描述,只认数字下降≥30%才算成功。
5.3 建立自动化回归检查:把AWR验证变成CI/CD一环
在运维平台中嵌入AWR校验脚本,每次发布后自动执行:
# check_awr_regression.py(伪代码) import cx_Oracle def get_metric(snap_id, metric_name): conn = cx_Oracle.connect("user/pass@host:port/service") cursor = conn.cursor() cursor.execute(f""" SELECT VALUE FROM DBA_HIST_SYSMETRIC_SUMMARY WHERE SNAP_ID = :snap_id AND METRIC_NAME = :metric_name """, snap_id=snap_id, metric_name=metric_name) return cursor.fetchone()[0] # 发布前快照ID baseline_snap = 12345 # 发布后快照ID(等待1小时自动生成) current_snap = 12346 db_time_before = get_metric(baseline_snap, "Database Time Per Sec") db_time_after = get_metric(current_snap, "Database Time Per Sec") if db_time_after > db_time_before * 1.1: # 上升超10%即告警 send_alert("DB Time regression detected!")我的习惯:在Jenkins Pipeline中加入此脚本,失败则阻断发布。它逼着团队在改SQL前先写好验证用例,而不是事后补救。AWR不是故障后的救火队,而是调优前的验孕棒——阴性才能开工。
希望帮到你。
本文还有配套的精品资源,点击获取