Oracle数据库和MySQL在慢查询记录机制上存在明显差别。Oracle并不维护一个单独的慢查询日志文件,而是把SQL的累计执行信息、等待事件、执行计划等分散在动态性能视图和AWR仓库中。部分DBA一开始会试图寻找类似慢查询日志的开关,实际上更有效的方式是从v$sql和v$session这类视图入手,先确定哪些SQL消耗了最多时间,再有针对性地抓取这些SQL的trace文件。这样既能避免全库跟踪带来的性能损耗,也能快速锁定瓶颈。

一、从动态性能视图定位候选慢查询
Oracle虽然没有独立的慢查询日志,但其动态性能视图提供了比普通日志更丰富的SQL执行元数据。最常用的视图包括v$sql、v$sqlarea、v$session以及历史视图dba_hist_sqlstat。其中v$sql保存共享池中SQL的累计执行统计,v$session展示当前会话状态和等待事件,dba_hist_sqlstat则记录AWR快照中的历史SQL指标,适合分析过去某个时间段内的慢查询。
抓取慢查询的第一步是从这些视图中筛选出消耗资源最高的候选SQL。下面这条语句按累计执行时间倒序获取前20条执行过的SQL,同时展示CPU时间、逻辑读、磁盘读和执行次数,便于综合判断慢的原因。
SELECT *
FROM (
SELECT sql_id,
child_number,
SUBSTR(sql_text, 1, 120) AS sql_text_fragment,
elapsed_time,
cpu_time,
buffer_gets,
disk_reads,
executions
FROM v$sql
WHERE executions > 0
ORDER BY elapsed_time DESC
)
WHERE ROWNUM <= 20;
这里elapsed_time和cpu_time的单位都是微秒,除以1000000后可换算为秒。buffer_gets表示逻辑读次数,数值越高通常意味着访问的数据块越多;disk_reads表示物理读次数,过高说明缓存命中不理想。不能只看累计时间,还要结合executions计算平均耗时,公式为elapsed_time / executions。例如一条SQL累计耗时很高,但执行次数达到百万级,平均每次只有几毫秒,就不一定是最紧急的优化对象。
如果需要查看当前正在执行的长SQL,可以通过v$session关联v$sql,过滤status='ACTIVE'且last_call_et超过阈值的会话。这样能够及时发现正在拖慢业务的长事务或长时间运行的批处理语句。
二、使用SQL Trace和10046事件抓取完整执行过程
动态性能视图只能给出汇总数据,要深入分析一条慢查询的完整执行过程,必须开启SQL Trace。SQL Trace会记录SQL的解析、执行、抓取阶段的详细统计,包括CPU时间、等待事件、绑定变量值以及行源操作等。10046事件是Oracle提供的扩展跟踪机制,通过设置不同级别控制跟踪内容的详细程度。常用级别包括level 1标准跟踪、level 4增加绑定变量、level 8增加等待事件、level 12同时包含绑定变量和等待事件。
最推荐的方式是使用DBMS_MONITOR包进行会话级跟踪。下面的PL/SQL块会针对指定会话开启level 12级别的跟踪,既捕获等待事件也捕获绑定变量,避免跟踪整个数据库造成大量文件输出。
BEGIN
DBMS_MONITOR.SESSION_TRACE_ENABLE(
session_id => 1234,
serial_num => 5678,
waits => TRUE,
binds => TRUE
);
END;
/
也可以直接在目标会话中执行10046事件命令。对于当前会话,可以使用ALTER SESSION开启和关闭跟踪,中间的SQL执行过程都会被记录到对应的trace文件中。
ALTER SESSION SET EVENTS '10046 trace name context forever, level 12'; -- 执行需要分析的慢SQL ALTER SESSION SET EVENTS '10046 trace name context off';
开启跟踪后,Oracle会在用户转储目录或诊断目录中生成扩展名为trc的文件。可以通过SHOW PARAMETER user_dump_dest或SHOW PARAMETER diagnostic_dest找到目录位置。对于RAC环境,要注意会话可能运行在不同实例上,需要到对应实例的目录中查找。
三、用tkprof格式化Trace并阅读等待事件
原始trace文件的内容虽然完整,但可读性较差,尤其是包含大量十六进制数据和内部行源信息。通常使用Oracle自带的tkprof工具将trace文件转换为易读的文本报告。它能够按照调用阶段汇总统计信息,并可以选择性地生成执行计划。
tkprof orcl_ora_12345.trc output.txt sys=no sort='(prsela,exeela,fchela)' print=10
上述命令中sys=no表示不包含SYS用户执行的递归SQL,sort参数按解析、执行和抓取的总耗时排序,print=10只输出前10条SQL。需要注意tkprof本身会连接数据库生成执行计划,如果数据库压力较大,可以省略执行计划选项,先分析统计部分。
call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.01 0.02 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 20 0.45 2.30 1835 18442 0 20
从上面的输出可以看出,这条SQL的主要时间消耗在Fetch阶段。cpu时间只有0.45秒,但elapsed达到2.30秒,说明有相当一部分时间花在等待上。再结合等待事件统计,如果出现大量db file sequential read,通常与索引访问和随机I/O有关;如果出现db file scattered read,往往意味着存在全表扫描;如果出现enq: TX - row lock contention,则需要关注锁竞争和事务设计。
分析trace文件时,不要只盯着一条SQL的累计耗时,还要关注每个执行阶段的耗时和等待事件分布。通过比较CPU时间与总耗时的差异,可以快速判断慢查询是消耗在CPU计算、磁盘I/O还是锁等待上,这是后续优化方向的重要依据。
四、结合执行计划与统计信息完成根因分析
从trace文件或动态视图拿到sql_id后,可以进一步获取该SQL的实际执行计划。使用DBMS_XPLAN.DISPLAY_CURSOR可以查看共享池中最近一次执行的计划,并且可以附加ALLSTATS LAST显示实际执行统计。
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('5p3k2q...', NULL, 'ALLSTATS LAST'));
阅读执行计划时,重点关注是否存在全表扫描、索引是否被正确使用、表连接顺序是否合理以及连接方式是嵌套循环还是哈希连接。例如大表之间的连接如果走了嵌套循环且内层没有合适索引,会产生极高的逻辑读;反过来,小结果集查询如果走了哈希连接,也可能带来不必要的内存和CPU消耗。执行计划中Rows预估值与实际值差距过大,往往意味着统计信息过期或直方图缺失。
常见优化方向包括:为过滤条件创建合适的索引,必要时使用复合索引;收集或刷新表与索引的统计信息;通过SQL改写减少不必要的排序和去重;使用绑定变量降低硬解析开销;在复杂场景中通过SQL Profile或Baseline固定执行计划。完成优化后应再次抓取trace并对比elapsed_time、buffer_gets和等待事件的变化,确认优化是否真正生效。
总体来看,Oracle慢查询的抓取与分析并不是依赖一个简单的日志文件,而是一个从汇总视图筛选、到会话级Trace捕捉、再到tkprof与执行计划下钻的闭环过程。通过这套流程,DBA可以准确判断SQL慢在何处,并有针对性地完成索引调整、统计信息维护或SQL改写,从而持续稳定数据库性能。