数据库基础体系 · 第 112/139 篇。文章以各产品官方稳定版本的公开语义为准;示例会明确引擎、事务与部署边界。
Oracle 监控与调优:AWR、ASH、等待事件、统计和容量
Oracle 性能问题通常不是“某条 SQL 慢”这么简单。一次请求从客户端到数据库,可能经历连接排队、CPU 争用、逻辑读、物理读、锁等待、日志提交、并行执行、网络传输和存储响应。要判断问题,必须同时回答四个问题:
- 数据库当时在做什么?
- 时间花在哪里?
- 优化器为什么选择这个执行计划?
- 当前资源是否已经接近容量边界?
Oracle 提供了几组互补的观测机制:
- AWR(Automatic Workload Repository):以快照为边界保存一段时间内的数据库工作负载摘要。
- ASH(Active Session History):以活动会话为对象,记录会话在某个时间点正在运行或等待什么。
- 等待事件(Wait Events):描述会话为什么没有继续执行。
- 统计信息(Optimizer Statistics):描述表、列、索引等对象的数据分布,供优化器估算执行代价。
- 容量分析:把吞吐量、响应时间、CPU、I/O、并发和数据库时间联系起来,判断系统距离瓶颈还有多少余量。
这些机制的观察对象不同。AWR 和 ASH 不能替代执行计划,等待事件也不能直接证明某个对象有问题,统计信息更不是运行时性能计数器。
一、先区分几种“时间”:响应时间、数据库时间和资源时间
1. 响应时间不等于数据库时间
一次请求的响应时间可以抽象为:
其中:
- :用户感知的端到端响应时间;
- :请求在连接池、线程池或其他队列中的等待时间;
- :应用代码执行时间;
- :网络传输时间;
- :数据库内部处理时间。
Oracle 主要观察数据库内部部分。即使 AWR 显示数据库负载正常,应用仍可能因为连接池耗尽或网络延迟而超时。
Oracle 中常见的 DB time 是所有前台会话处于活动状态时消耗的时间总和。活动状态通常包括:
- 正在使用 CPU;
- 正在等待非空闲等待事件。
如果同一时刻有 10 个会话各自活动 1 秒,DB time 大约增加 10 秒,而墙上时钟只过去 1 秒。因此:
AAS 是性能分析中的核心量。它回答的是:在一个时间区间内,平均有多少个前台会话正在消耗数据库资源或等待数据库资源。
2. 一个完整算例
假设某小时的 AWR 报告给出:
- DB time:7200 秒;
- DB CPU:3600 秒;
- 非空闲等待时间约为 3600 秒;
- 该区间长度:3600 秒。
则:
平均有 2 个前台会话处于活动状态。
进一步可以分解:
于是:
- CPU 占 DB time:;
- 非空闲等待占 DB time:。
这并不表示数据库只用了 50% 的主机 CPU。DB CPU 是所有会话累计使用的 CPU 时间,而主机 CPU 使用率还受到后台进程、其他进程、CPU 核数和虚拟化调度的影响。
如果数据库运行在 8 个逻辑 CPU 上,那么 3600 秒 DB CPU 分布在 3600 秒墙上时间中,平均相当于约 1 个 CPU 核:
但这仍然不能直接推出“CPU 没有问题”。如果业务高峰只有 5 分钟,峰值 CPU 可能远高于小时平均值;如果运行在受限的容器 CPU 配额中,8 个宿主机 CPU 也不代表数据库可以使用 8 个 CPU。
3. CPU 不是等待事件
Oracle 会话状态常见为:
ON CPU:正在 CPU 上执行;WAITING:正在等待某个事件;- 其他状态:例如会话未处于数据库活动执行阶段。
CPU 消耗不会以一个叫作 CPU 的等待事件出现。分析时必须同时看:
- DB CPU;
- 前台等待时间;
- 活跃会话数量;
- 主机 CPU、运行队列和 CPU 配额;
- SQL 的执行次数和单次资源消耗。
“没有明显等待事件”不代表没有性能问题,可能是 CPU 饱和、SQL 做了过多逻辑读,或者应用端没有把数据库时间记录完整。
二、AWR:以快照比较工作负载变化
1. AWR 记录什么
AWR 是 Oracle 自动工作负载资料库。它周期性记录数据库状态和工作负载摘要,典型内容包括:
- 系统级时间模型统计;
- 系统等待事件;
- 前台和后台统计;
- SQL 的执行次数、逻辑读、物理读、CPU 时间、数据库时间等;
- 实例活动和部分资源统计;
- 对象级访问或变更统计;
- 相关的优化器和系统信息。
AWR 的基本数据流是:
运行中的内存统计
│
│ 生成快照
▼
AWR 持久化历史数据
│
│ 比较两个快照
▼
区间报告:增量、排名、趋势
AWR 不是实时监控工具。它记录的是快照之间的累计变化,因此一个小时的报告可能掩盖五分钟内发生的尖峰。
AWR 的诊断数据通常保存在数据库内部的相关表空间中。快照具有:
- 快照编号;
- 开始时间;
- 结束时间;
- 实例标识;
- 数据库标识。
在 RAC 环境中,需要注意数据库级和实例级视角,查询通常需要使用 GV$ 视图或在正确的实例上生成报告。
2. 快照间隔和保留时间不是性能结论
Oracle 环境中的快照间隔和保留时间可以配置。某些安装的常见默认值是每小时生成一次并保留若干天,但具体值取决于版本、安装方式和管理员配置,不能把默认值当作规范保证。
可以通过以下查询查看配置:
SELECT snap_interval,
retention,
topnsql
FROM dba_hist_wr_control;
这些字段分别描述快照间隔、保留策略和 SQL 记录范围。查询通常需要相应的数据字典权限。
在生产环境中,修改 AWR 控制参数会影响存储量和诊断能力,不应为了“让报告更详细”而无限缩短间隔或无限延长保留时间。更高的采样或保留成本需要结合问题发生频率、存储空间和合规要求评估。
3. AWR 报告的本质是“两个状态的差分”
例如,某 SQL 在快照 100 时累计:
- 执行次数:1000;
- DB time:500 秒;
- 逻辑读:1,000,000。
在快照 101 时累计:
- 执行次数:1100;
- DB time:900 秒;
- 逻辑读:1,800,000。
那么区间增量为:
- 执行次数:100;
- DB time:400 秒;
- 逻辑读:800,000。
区间平均值为:
不能直接用累计值判断当前区间性能,否则会把数据库启动以来的历史工作负载混入结论。
4. 生成 AWR 报告
Oracle 常见环境中可以使用 SQL*Plus 提供的报告脚本:
@?/rdbms/admin/awrrpt.sql
脚本通常会依次询问:
- 报告类型,例如单实例或 RAC;
- 报告格式;
- 起始快照;
- 结束快照;
- 输出文件名。
它要求执行者具有足够的访问权限,并且环境必须具备相应的 AWR 能力。
报告结果应按以下顺序阅读:
- 报告时间范围和数据库实例;
- Load Profile;
- Top Timed Events;
- Time Model Statistics;
- SQL 统计;
- Instance Activity;
- 主机 CPU 和 I/O 信息;
- 与业务峰值、发布、批处理时间对照。
不要看到 Top Timed Events 的第一项就立刻修改参数。它只说明某个事件在该时间区间累计消耗较多时间,还需要结合执行次数、并发量、调用链和资源上限。
5. AWR 的许可证边界
AWR、ASH 以及相关历史诊断能力属于 Oracle Diagnostics Pack 的范围。是否可以在某个环境中使用,取决于 Oracle 版本、版本选项、部署方式和许可证合同。不能因为视图存在就默认可以在任何环境中自由使用。
在生产环境使用 AWR 报告、历史 ASH 或相关接口前,应由负责 Oracle 许可证的团队确认授权范围。本文讨论的是功能语义,不替代许可证判断。
三、ASH:回答“某一时刻哪些会话在做什么”
1. ASH 的观察单位是活动会话
ASH,即 Active Session History,面向“活动会话”采样。一个样本通常包含类似信息:
- 样本时间;
- 会话标识;
- SQL 标识;
- 会话当前是否在 CPU 上;
- 等待事件和等待参数;
- 等待类别;
- 当前对象或数据文件等上下文;
- 阻塞会话相关信息;
- 服务名、模块、动作等会话属性。
ASH 的重要特点是:
- 它不是每次等待开始和结束都完整记录;
- 它主要记录活动会话;
- 它通常以约一秒级的采样粒度观察活动;
- 短于采样间隔的瞬时事件可能被漏采;
- 长时间持续的等待更容易被观察到。
因此,ASH 是抽样历史,不是完整审计日志。某条 SQL 没出现在 ASH 中,不一定代表它从未执行过,可能是执行太快、未处于活动状态,或者历史数据已被覆盖。
2. 当前 ASH 与历史 ASH
当前内存中的活动会话历史可通过 V$ACTIVE_SESSION_HISTORY 查询。历史持久化数据通常通过 DBA_HIST_ACTIVE_SESS_HISTORY 查询。
一个查看最近活动会话的示例:
SELECT sample_time,
session_id,
session_serial#,
session_state,
event,
wait_class,
sql_id,
module
FROM v$active_session_history
WHERE sample_time >= SYSTIMESTAMP - INTERVAL '10' MINUTE
ORDER BY sample_time DESC
FETCH FIRST 100 ROWS ONLY;
需要注意:
V$ACTIVE_SESSION_HISTORY的可见历史受内存和当前负载影响;- 历史视图需要相应权限和许可;
FETCH FIRST的使用要符合目标数据库版本;- 在 RAC 中通常应考虑
GV$ACTIVE_SESSION_HISTORY,并显示INST_ID。
按等待类别聚合:
SELECT wait_class,
COUNT(*) AS samples
FROM v$active_session_history
WHERE sample_time >= SYSTIMESTAMP - INTERVAL '15' MINUTE
GROUP BY wait_class
ORDER BY samples DESC;
这里的 samples 不是等待次数,也不是精确的等待秒数。若采样间隔近似为 1 秒,可以把样本数量作为活动时间的近似,但仍然要明确这是抽样估计。
3. 用 ASH 找到“谁、何时、因为什么”
例如,某接口在 10:00 到 10:05 变慢,可以按 SQL 和等待类别分组:
SELECT sql_id,
session_state,
wait_class,
event,
COUNT(*) AS samples
FROM v$active_session_history
WHERE sample_time >= TO_TIMESTAMP('2025-01-01 10:00:00',
'YYYY-MM-DD HH24:MI:SS')
AND sample_time < TO_TIMESTAMP('2025-01-01 10:05:00',
'YYYY-MM-DD HH24:MI:SS')
GROUP BY sql_id, session_state, wait_class, event
ORDER BY samples DESC;
分析步骤是:
- 先确定时间窗口,而不是只看“最近”;
- 找出活动样本最多的 SQL;
- 区分
ON CPU和WAITING; - 对等待样本按
wait_class和event聚合; - 回到 SQL 执行计划和业务调用次数;
- 检查是否存在阻塞会话、I/O 延迟或并发突增。
如果同一 SQL 的 ASH 样本主要是 ON CPU,应检查逻辑读、连接条件、排序、聚合和执行次数;如果主要是 User I/O,应继续区分物理读、读请求延迟和 SQL 是否不必要地访问了大量数据;如果主要是 Concurrency 或 Application,则应检查锁、闩锁、事务和共享资源。
四、等待事件:等待什么不等于谁有错
1. 等待事件的结构
一个等待事件通常可以理解为:
会话
├─ 状态:WAITING 或 ON CPU
├─ 事件:等待的资源或同步点
├─ 等待类别:User I/O、Commit、Concurrency 等
├─ 参数:例如文件号、块号、对象号、锁类型
└─ 持续时间与等待次数
在当前会话视图中,可以先观察活动会话:
SELECT sid,
serial#,
username,
status,
sql_id,
event,
wait_class,
state,
seconds_in_wait,
blocking_session,
blocking_session_status
FROM v$session
WHERE status = 'ACTIVE'
ORDER BY seconds_in_wait DESC;
常见字段的含义:
EVENT:当前等待事件;WAIT_CLASS:等待类别;STATE:当前是等待、刚结束等待,还是其他状态;SECONDS_IN_WAIT:当前等待相关的时间字段;BLOCKING_SESSION:若已识别阻塞者,给出阻塞会话;SQL_ID:当前或相关 SQL 的标识。
V$SESSION 是实时视图,适合回答“现在发生了什么”,不适合单独回答“过去一小时发生了什么”。
2. 常见等待事件的正确解释
db file sequential read
通常表示一次单块数据文件读取。它经常出现在索引访问、按 ROWID 回表或其他单块读取路径中,但事件名称不能证明“索引有问题”。
可能原因包括:
- 索引选择性差;
- 大量索引扫描后回表;
- 缓存命中不足;
- 随机访问模式;
- SQL 估算错误导致错误的访问路径。
反例是:某 SQL 使用了索引,但返回了表中大部分行。此时索引并不一定比全表扫描好,单块读等待反而可能增加。
db file scattered read
在传统命名下常与多块读取相关,常见于全表扫描或全索引扫描。但具体行为取决于版本、访问路径和执行环境。不能仅凭事件名称认定“全表扫描就是错误”,因为对大部分数据的顺序读取可能比索引回表更合理。
direct path read
表示绕过部分缓冲区缓存路径的直接读取,可能与大表扫描、临时段读取或其他执行方式有关。需要结合 SQL 计划、对象大小、并行度和 I/O 延迟判断。
log file sync
前台会话提交时等待 LGWR 等日志写入路径完成。常见原因包括:
- 提交过于频繁;
- redo 生成量大;
- 日志写入延迟;
- 存储或虚拟化层抖动;
- LGWR 受到 CPU 调度影响。
“出现 log file sync 就应该关闭日志”是错误做法。日志持久性是事务提交语义的重要组成部分,不能为了降低等待而破坏提交可靠性。
enq: TX - row lock contention
通常表示事务正在等待其他事务释放行锁或相关事务资源。诊断时要找到阻塞链:
SELECT sid,
serial#,
username,
event,
blocking_session,
sql_id,
row_wait_obj#
FROM v$session
WHERE blocking_session IS NOT NULL;
还应结合事务持续时间、应用请求、未提交事务和锁对象进一步确认。直接杀掉阻塞会话可能导致回滚,回滚时间又可能继续阻塞其他会话。
buffer busy waits
通常说明多个会话对某个缓冲区存在并发访问冲突,但根因可能是热点块、段头、索引叶块、块管理方式或工作负载模式。不能看到该事件就直接增加缓冲区缓存。
cursor: pin S wait on X 和共享游标相关等待
这类等待可能与游标失效、硬解析、共享池竞争、DDL 或对象状态变化有关。需要结合 SQL 版本数量、解析次数、应用是否使用绑定变量以及具体版本行为分析。
3. 等待类别是分类,不是根因
等待类别便于排序和归纳,例如:
User I/O:用户 SQL 直接相关的 I/O 等待;System I/O:后台进程或系统级 I/O;Commit:提交路径;Concurrency:并发控制;Application:应用级同步或锁;Configuration:配置导致的等待;Network:网络相关;Scheduler:调度或资源管理相关。
类别只能缩小范围。真正的诊断需要把:
等待事件
+ 等待参数
+ SQL
+ 执行计划
+ 对象
+ 阻塞者
+ 主机资源
+ 时间窗口
联系起来。
五、统计信息:优化器对数据分布的模型
1. 统计信息不是运行时计数器
Oracle 优化器需要估算候选执行计划的成本。它使用的统计信息通常包括:
- 表的行数和块数;
- 列的最小值、最大值、非空数量;
- 列的不同值数量;
- 直方图;
- 索引的层级、叶块、聚簇因子等;
- 分区级或全局统计信息;
- 相关的系统统计信息。
统计信息描述的是优化器看到的数据模型,不是“这条 SQL 最近运行了多少次”。
这一区分很重要:
DBA_TAB_STATISTICS等视图描述对象统计信息;- AWR、ASH、动态性能视图描述运行时活动;
- 执行计划描述优化器选择的访问路径;
- 运行时执行统计描述实际行数和资源消耗。
2. 优化器为何会选错计划
设某个谓词选择率为 ,表中行数为 ,优化器估算返回行数:
如果真实返回行数为 ,则估算误差可以用:
表示。
例如:
- 表行数 ;
- 优化器估算选择率 ;
- 估算返回行数 ;
- 实际返回行数 。
则:
优化器以为只需要处理 1 万行,实际要处理 200 万行。它可能选择索引加回表,而实际全表扫描或其他批量访问路径更合适。
这就是为什么执行计划调优不能只盯着“是否使用索引”。关键问题是:
- 基数估算是否接近真实值;
- 访问路径是否匹配返回数据量;
- 连接顺序和连接方式是否合理;
- 排序、聚合、临时空间和并行度是否合适。
3. 直方图解决什么问题
如果列值分布均匀,优化器可用不同值数量近似估算选择率。但现实中可能存在倾斜:
status = 'ACTIVE' 99%
status = 'DELETED' 1%
如果没有足够的分布信息,优化器可能对两个值使用相近的选择率估计。直方图用于表达列值分布,使不同谓词得到不同估算。
但直方图不是越多越好:
- 它增加统计信息维护复杂度;
- 数据分布变化后可能过期;
- 绑定变量和谓词值的变化可能导致不同计划;
- 重新收集统计信息可能触发计划变化。
可以查看表和列统计信息:
SELECT owner,
table_name,
num_rows,
blocks,
last_analyzed,
stale_stats
FROM dba_tab_statistics
WHERE owner = 'APP'
AND table_name = 'ORDERS';
SELECT owner,
table_name,
column_name,
num_distinct,
num_nulls,
histogram,
last_analyzed
FROM dba_tab_col_statistics
WHERE owner = 'APP'
AND table_name = 'ORDERS';
NUM_ROWS 和 BLOCKS 是统计信息中的估计值,不应直接当作实时精确值。
4. 收集统计信息
典型示例:
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
ownname => 'APP',
tabname => 'ORDERS',
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
method_opt => 'FOR ALL COLUMNS SIZE AUTO',
cascade => DBMS_STATS.AUTO_CASCADE,
no_invalidate => DBMS_STATS.AUTO_INVALIDATE
);
END;
/
这些参数的意图是:
AUTO_SAMPLE_SIZE:让 Oracle 选择采样策略;SIZE AUTO:根据需要决定直方图;AUTO_CASCADE:由 Oracle 决定是否处理相关索引统计;AUTO_INVALIDATE:由 Oracle 决定游标失效策略。
但“自动”不等于不会产生风险。收集统计信息可能导致:
- 优化器重新选择计划;
- 硬解析或游标重新验证;
- 统计信息写入占用 CPU、I/O 和临时空间;
- 大表收集时间较长;
- 业务高峰期间资源竞争。
因此应在业务低峰执行,并在执行前后保存:
- SQL ID;
- 执行计划;
- SQL 资源指标;
- 统计信息时间;
- 业务响应时间。
如果需要恢复,Oracle 提供统计信息历史能力和相应的恢复接口,但前提是历史尚未过期且权限、保留策略和对象范围都满足要求。恢复统计信息之前,应确认问题确实由统计信息变化引起,而不是把其他变化覆盖掉。
5. 动态采样、SQL Plan Management 和 Hint 的边界
当持久化统计不足时,优化器可能使用动态采样或其他自适应机制改善估算。它不是统计信息维护的替代品:
- 动态采样有额外优化阶段开销;
- 复杂数据分布仍可能估算错误;
- 运行时数据变化可能使一次采样不具代表性。
Hint 也不是永久修复手段。Hint 直接影响某次 SQL 的优化决策,但可能因:
- 表结构变化;
- 数据分布变化;
- 版本变化;
- Hint 无效或被忽略;
- SQL 文本或别名变化;
而失去预期效果。
生产调优应优先修正 SQL 语义、数据访问方式和统计信息;需要稳定计划时,再评估 SQL Plan Management、SQL Profile、基线或有限范围的 Hint。不同功能涉及不同许可和版本能力,不能混为一谈。
六、从统计信息回到实际执行计划
1. EXPLAIN PLAN 不等于实际执行结果
以下命令展示的是优化器为某次解析生成的计划:
EXPLAIN PLAN FOR
SELECT o.order_id
FROM app.orders o
WHERE o.customer_id = 1001;
SELECT *
FROM TABLE(DBMS_XPLAN.DISPLAY(format => 'BASIC +PREDICATE'));
它有两个限制:
- 它可能不是应用实际执行的游标;
- 它没有真实执行行数和真实资源消耗。
更可靠的方式是查看已经执行的游标:
SELECT *
FROM TABLE(
DBMS_XPLAN.DISPLAY_CURSOR(
sql_id => '9abc123def456',
format => 'ALLSTATS LAST +PEEKED_BINDS +PREDICATE'
)
);
要看到实际行数,执行时通常需要采集执行统计,例如:
SELECT /*+ GATHER_PLAN_STATISTICS */
o.order_id
FROM app.orders o
WHERE o.customer_id = 1001;
也可以使用适当的会话或系统级统计配置,但范围越大,额外开销越高。
2. 重点看估算行数和实际行数
执行计划中常见两组重要列:
E-Rows:优化器估算行数;A-Rows:实际执行行数。
假设某连接节点显示:
Operation E-Rows A-Rows
HASH JOIN 1000 2000000
TABLE ACCESS 1000 2000000
这说明估算误差约为 2000 倍。此时应优先检查:
- 谓词列统计信息;
- 数据倾斜和直方图;
- 多列相关性;
- 隐式类型转换;
- 绑定变量行为;
- 分区裁剪是否发生;
- SQL 条件是否与业务实际选择性一致。
如果 E-Rows 与 A-Rows 接近,但 SQL 仍慢,问题可能在:
- 单次访问量本身太大;
- I/O 延迟;
- CPU 资源不足;
- 并发争用;
- 排序或临时空间;
- 执行次数过多。
3. 估算正确也可能计划不适合
反例:
- 估算返回 100 万行;
- 实际也返回 100 万行;
- 但 SQL 只需要 10 个字段中的 2 个字段;
- 当前计划通过低选择性索引逐行回表。
这时统计信息可能是正确的,问题是访问路径的成本模型和物理设计不适合工作负载。可以评估:
- 更合理的索引前导列;
- 覆盖索引是否值得;
- 全表扫描或分区扫描;
- SQL 是否可以减少返回列和行;
- 是否存在不必要的排序、去重或函数计算。
因此“更新统计信息”不能成为所有计划问题的统一答案。
七、从 AWR、ASH 和执行计划建立因果链
一个可靠的调优结论应至少形成如下链路:
业务症状
↓
时间窗口和影响范围
↓
AWR:区间负载、DB time、CPU、等待类别
↓
ASH:具体 SQL、会话、阻塞者和时间分布
↓
执行计划:访问路径、连接顺序、E-Rows/A-Rows
↓
统计信息、对象结构和主机资源
↓
修复措施与回归验证
示例:接口从 200 ms 变为 5 秒
第一步:确认数据库是否真的变慢
查看该时间窗口的:
- 数据库时间;
- 前台等待时间;
- 事务提交时间;
- CPU;
- 用户 I/O;
- 活跃会话数量;
- SQL 执行次数。
如果应用响应时间增加,但数据库时间没有增加,应优先检查连接池、网络或应用线程池,而不是直接改 SQL。
第二步:用 ASH 定位活动来源
如果 ASH 显示某 SQL 的样本突然增多,并且主要等待 User I/O,可以进一步查询:
- 该 SQL 的执行次数是否突增;
- 单次逻辑读和物理读是否增加;
- 是否出现新的子游标;
- 是否发生计划变化;
- 对象是否刚刚装载大量数据。
第三步:比较实际计划
使用 DBMS_XPLAN.DISPLAY_CURSOR 比较正常时和异常时的:
- 计划哈希值;
E-Rows与A-Rows;- 连接顺序;
- 索引、全表扫描和分区裁剪;
- 临时空间;
- 并行执行;
- 绑定变量窥视结果。
第四步:判断根因
可能得到不同结论:
- 统计信息过期,导致基数估算错误;
- 数据倾斜变化,原有直方图不再代表现实;
- SQL 文本未变但子游标因环境或绑定变量产生不同计划;
- 业务调用次数增加十倍,单次执行没有变慢;
- 存储延迟增加,SQL 计划没有变化;
- 事务未提交,后续请求在等待行锁。
每一种结论的修复方式都不同。
八、容量分析:从“现在很慢”推导“还能承受多少”
1. 容量不是单一指标
数据库容量至少包含:
- CPU 容量;
- I/O 吞吐和 I/O 延迟容量;
- 内存容量;
- 并发会话容量;
- 日志写入容量;
- 锁和事务并发容量;
- 临时空间容量;
- 表空间和归档空间容量;
- 连接池及网络容量。
“CPU 还有 30%”不能证明系统还有 30% 的整体容量,因为 I/O、日志、锁或连接数可能已经先达到瓶颈。
2. Little 定律与活动会话
排队系统中常用 Little 定律:
其中:
- :系统中的平均请求数;
- :吞吐率;
- :平均响应时间。
在数据库分析中,可以近似理解为:
例如:
- 每秒 200 个事务;
- 每个事务平均消耗 20 ms 数据库时间。
则:
这表示平均有约 4 个活动会话。
如果吞吐量不变,而单事务数据库时间从 20 ms 增加到 100 ms:
活动会话增加五倍,连接池、锁竞争和 CPU 争用可能随之恶化。性能问题常常不是线性增长:接近资源上限后,排队时间会快速增加。
3. 用服务需求估算资源上限
设某类请求每秒到达 次,每次在某资源上平均消耗 秒,则该资源所需利用率近似为:
例如,某 SQL 每秒执行 100 次,每次平均消耗 8 ms CPU:
这相当于消耗 0.8 个 CPU 核。如果数据库可用 CPU 配额只有 1 个核,CPU 已接近饱和;如果可用 8 个核,CPU 可能不是主要瓶颈。
对 I/O 也可以使用类似思路。若每秒产生 500 次 I/O 请求,每次存储服务时间平均 4 ms:
这意味着单一串行服务能力不足,实际系统可能依赖多个并行服务队列;不能简单把这个结果当作设备利用率,但它说明“请求率乘以单次服务时间”已经超过单服务通道能力。
4. 不能用平均值掩盖峰值
容量模型应至少分别计算:
- 工作日平均;
- 业务高峰;
- 批处理窗口;
- 发布或数据装载窗口;
- 故障降级场景;
- 副本、备份和统计信息收集期间。
例如某小时平均 TPS 为 100,但五分钟峰值 TPS 为 500。用 100 TPS 规划 CPU 和 I/O,可能在高峰时直接进入排队区。
AWR 适合观察区间总量和趋势,ASH 适合定位尖峰中的活动会话,操作系统监控则用于验证主机级 CPU、内存、I/O 和网络。
九、表空间、临时空间和日志容量
1. 表空间容量
查看永久表空间使用情况时,应明确使用的是数据文件总大小、已使用空间还是可自动扩展上限。不同查询反映不同问题。
例如可以先查看数据文件:
SELECT tablespace_name,
file_name,
bytes / 1024 / 1024 AS size_mb,
autoextensible,
maxbytes / 1024 / 1024 AS max_mb
FROM dba_data_files
ORDER BY tablespace_name, file_name;
这里的 MAXBYTES 只是数据文件的自动扩展上限,不代表底层文件系统或 ASM 磁盘组真的有足够空间。
因此容量判断至少要同时验证:
- 表空间剩余可用空间;
- 数据文件是否允许自动扩展;
- 自动扩展上限;
- 文件系统或 ASM 磁盘组剩余空间;
- 增长速度;
- 大对象、分区和索引的增长来源。
“表空间还有 20%”也不一定安全。如果每天增长 50 GB,而底层只剩 10 GB,系统仍会很快失败。
2. 临时表空间
排序、哈希连接、临时结果集和某些并行操作可能使用临时表空间。检查临时文件:
SELECT tablespace_name,
file_name,
bytes / 1024 / 1024 AS size_mb,
autoextensible,
maxbytes / 1024 / 1024 AS max_mb
FROM dba_temp_files
ORDER BY tablespace_name, file_name;
临时空间增长的诊断路径包括:
- 观察临时空间当前使用量;
- 找到消耗临时空间的会话和 SQL;
- 查看执行计划中的排序、哈希和并行操作;
- 判断是数据量增长、内存不足、计划变化还是 SQL 逻辑问题;
- 扩容前确认底层空间和增长上限。
单纯扩大临时表空间只能延后失败,不能修复错误连接顺序或不必要排序。
3. 归档和日志容量
日志相关容量包括:
- redo 日志组;
- 归档日志空间;
- 备份保留;
- Data Guard 传输或应用延迟;
- 恢复窗口要求。
如果归档目的地空间耗尽,数据库可能出现严重影响甚至无法继续正常归档。容量监控必须观察增长速率和故障恢复路径,而不是只设置一个固定百分比阈值。
十、锁、事务和等待链
等待事件经常只是阻塞链的表象。一个完整的锁问题通常是:
会话 A 开启事务并修改行
│
│ 未提交
▼
会话 B 修改同一行
│
▼
enq: TX - row lock contention
│
▼
会话 C 可能继续等待 B 或其他资源
诊断时应同时观察:
- 阻塞会话;
- 被阻塞会话;
- 事务开始时间;
- 当前 SQL;
- 最后一次提交时间;
- 应用请求和连接归属;
- 回滚段和事务持续时间。
不能把所有锁等待归因于“数据库锁配置不合理”。很多锁问题来自应用事务边界:
- 开启事务后执行远程调用;
- 修改数据后等待用户操作;
- 连接归还池前未提交或回滚;
- 批量操作过大;
- 异常路径没有正确结束事务。
修复阻塞的优先级通常是先停止或修正制造长事务的业务路径,再评估是否需要终止会话。终止会话不是无成本操作,可能触发大量回滚,并使恢复时间不可预测。
十一、常见误解与失败诊断
误解一:Top Wait Event 第一名就是根因
某等待事件累计时间最多,可能只是因为执行次数特别多。应同时看:
例如:
- 事件 A:等待 100 万次,总计 1000 秒,平均 1 ms;
- 事件 B:等待 1000 次,总计 500 秒,平均 500 ms。
事件 A 的累计时间更高,但事件 B 可能更直接反映存储或锁的异常。
误解二:出现物理读就应该增加内存
物理读可能由以下原因造成:
- SQL 扫描了过多数据;
- 索引回表次数过多;
- 工作集本来就超过内存;
- 缓存被大量无效访问冲刷;
- 存储延迟异常。
增加缓存只能缓解部分问题。如果 SQL 每次都扫描一个远大于缓存的表,缓存增加不一定有效。
误解三:执行计划用了索引就一定更快
索引访问适合高选择性、访问列和数据布局匹配的场景。返回大量行时,索引逐行回表可能比顺序扫描更慢。
判断依据应是:
- 返回行数;
E-Rows与A-Rows;- 逻辑读和物理读;
- 单次执行时间;
- 执行次数;
- 并发下的总资源消耗。
误解四:重新收集统计信息一定能修复慢 SQL
如果真正根因是:
- 锁等待;
- 存储延迟;
- 提交过于频繁;
- SQL 执行次数暴增;
- 应用连接池排队;
- 主机 CPU 配额不足;
重新收集统计信息不仅无效,还可能改变原本稳定的执行计划。
误解五:ASH 没有记录就说明没有问题
ASH 是采样系统。短 SQL、瞬时锁和采样间隔之间的事件可能漏掉。对短时尖峰,应结合:
- 应用日志;
- 实时
V$视图; - SQL 监控能力;
- 操作系统监控;
- 业务时间戳;
- 数据库审计或专门追踪机制。
不能把 ASH 当作完整事件日志。
误解六:EXPLAIN PLAN 就是应用实际运行的计划
实际游标可能因以下原因不同:
- 绑定变量不同;
- 子游标不同;
- 会话环境不同;
- 统计信息变化;
- SQL Plan Management;
- 自适应游标行为;
- 对象或分区状态变化。
调优时应优先检查实际游标和实际执行统计。
十二、一个可重复的生产诊断流程
第一步:固定事实边界
记录:
- 数据库版本和补丁;
- 单实例还是 RAC;
- 数据库时间、主机时间和应用时间是否一致;
- 问题开始和结束时间;
- 受影响的接口、用户和 SQL;
- 是否发生发布、统计信息收集、批处理、备份或数据装载。
时间边界错误会导致 AWR、ASH 和应用日志互相对不上。
第二步:判断是数据库整体问题还是局部 SQL 问题
先看:
- DB time 是否上升;
- AAS 是否上升;
- DB CPU 是否上升;
- 等待类别是否改变;
- 并发请求是否改变。
若只有一个 SQL 变慢,重点转向执行计划、统计信息和对象访问。若大量 SQL 同时变慢,重点检查 CPU、I/O、日志、锁、网络、存储和资源管理。
第三步:用 AWR 看区间差异
比较正常区间与异常区间:
- 业务吞吐是否变化;
- 每秒逻辑读、物理读、redo 和事务数;
- DB time 每秒;
- DB CPU 每秒;
- 主要等待事件;
- SQL 排名变化;
- 主机资源变化。
必须看“每秒”或“每次执行”的归一化指标,不能只看总量。
第四步:用 ASH 定位并发来源
观察:
- 哪些 SQL 占用活动样本最多;
- 哪些模块或服务产生活动;
- 是 CPU 还是等待;
- 是否形成阻塞链;
- 问题是否集中在某个实例;
- 是否存在计划切换。
第五步:检查执行计划和统计信息
对候选 SQL:
- 找到实际
SQL_ID; - 查看子游标;
- 查看实际执行计划;
- 对比
E-Rows和A-Rows; - 查看谓词是否发生隐式转换;
- 检查表、列、索引统计信息;
- 核对分区裁剪和连接条件;
- 评估修复后的回归风险。
第六步:验证修复而不是只验证命令成功
修复后的验证应包括:
- 同样时间窗口的 AAS;
- 单次执行 DB time;
- CPU、逻辑读、物理读;
- 等待事件和平均等待时间;
- 执行计划是否符合预期;
- 业务响应时间;
- 并发高峰表现;
- 回滚或恢复方案是否可用。
“统计信息收集成功”“索引创建成功”只说明操作完成,不说明性能问题已经解决。
十三、监控查询示例:把指标放到同一张图上
下面的查询用于查看某段时间内 ASH 按 SQL 聚合的活动样本:
SELECT sql_id,
COUNT(*) AS active_samples,
SUM(CASE WHEN session_state = 'ON CPU' THEN 1 ELSE 0 END)
AS on_cpu_samples,
SUM(CASE WHEN session_state = 'WAITING' THEN 1 ELSE 0 END)
AS waiting_samples
FROM v$active_session_history
WHERE sample_time >= SYSTIMESTAMP - INTERVAL '30' MINUTE
GROUP BY sql_id
ORDER BY active_samples DESC;
解释:
active_samples:该 SQL 出现在活动会话采样中的次数;on_cpu_samples:近似反映 CPU 活动;waiting_samples:近似反映等待活动;- 这些值不是精确执行次数,也不是精确等待调用次数。
若要查看当前系统级等待:
SELECT event,
wait_class,
total_waits,
time_waited_micro / 1000000 AS time_waited_seconds
FROM v$system_event
WHERE wait_class <> 'Idle'
ORDER BY time_waited_micro DESC;
系统累计视图必须结合启动时间或快照差分使用。数据库运行了很久时,累计总量很大并不等于最近发生了异常。
查看数据库启动时间:
SELECT startup_time
FROM v$instance;
如果没有 AWR,也可以在两个时间点采集动态性能视图并自行计算增量,但这不等同于 AWR 的完整历史能力,且采集过程本身需要设计好权限、频率和存储。
十四、监控系统应如何保存指标
生产监控不应只保存一个“数据库 CPU 百分比”。至少需要保存以下维度:
数据库层
- DB time;
- DB CPU;
- AAS;
- 每秒执行次数;
- 每次执行逻辑读、物理读、CPU 时间;
- 提交和回滚次数;
- redo 生成速率;
- 等待类别和主要等待事件;
- 阻塞会话数量;
- 长事务;
- 临时空间使用;
- 表空间和归档空间增长。
SQL 层
SQL_ID;- 计划哈希值;
- 执行次数;
- 平均和分位响应时间;
- CPU 时间;
- DB time;
- 逻辑读和物理读;
- 返回行数;
- 子游标数量;
- 计划切换时间。
主机和存储层
- CPU 使用率和运行队列;
- 可用内存和交换;
- IOPS;
- 吞吐;
- I/O 延迟;
- 文件系统或 ASM 容量;
- 网络延迟和丢包;
- 虚拟机或容器 CPU、I/O 配额。
指标必须带有时间窗口、实例、服务、模块和环境标签。否则同一个 SQL 在不同实例、不同服务和不同业务路径下的行为会被混在一起。
十五、生产取舍:观测能力本身也有成本
AWR、ASH、执行统计和追踪会消耗:
- CPU;
- 内存;
- I/O;
- 数据字典空间;
- 日志和报告存储;
- 运维人员的分析时间。
因此应区分三种场景:
日常监控
使用低开销的系统指标、应用指标、连接和事务指标,发现趋势和 SLO 违约。
问题定位
在确定时间窗口内使用 AWR、ASH、实时视图和实际执行计划,减少全库范围的长期高成本采集。
深度实验
在可回滚、可隔离的环境中启用更详细的执行统计或 SQL 追踪,验证假设后再推广到生产。
调优不是把所有诊断开关永久打开,而是在足够观测和可接受开销之间取得平衡。
Oracle 性能分析的核心不是记住某个等待事件对应某个参数,而是建立可验证的因果关系:
AWR 提供区间视角,ASH 提供活动会话视角,等待事件提供资源等待视角,统计信息和执行计划提供优化器视角,容量模型则把这些结果转化为对峰值、增长和故障余量的判断。只有把这些视角放在同一条时间线上,调优结论才不会停留在“看到一个事件就修改一个参数”。
系列导航与关联阅读
- 系列入口:数据库完整学习路线:从关系模型、事务索引到分布式与向量检索
- 上一篇:Oracle 安全治理:用户、角色、Profile、TDE、审计和最小权限
- 下一篇:Oracle Data Pump 与迁移:导出导入、字符集、校验和停机窗口
- 延伸:Oracle 索引与优化器:统计信息、执行计划、Hint 和 SQL 调优
- 延伸:数据库可观测性:连接、事务、锁、执行计划、复制和 SLO
官方资料
本文依据数据库官方文档重新梳理;正文、示例与生产检查清单由 WR BLOG 编写。

评论
0 条讨论