应查询v$sql_workarea与v$sql关联视图,通过sql_id关联获取sql文本,其中tempseg_size反映累计磁盘写入量,比v$sort_usage更准确识别高消耗sql;number_passes>1是性能劣化关键指标。
怎么查出是哪个sql在疯狂吃临时表空间
直接看 v$sort_usage 只能知道谁在用、用了多少块,但没法对应到具体 sql 文本——尤其当会话已断开、sql 已不在共享池时,v$sql 里可能早就没了。真正能定位“罪魁祸首”的组合是 v$sql_workarea + v$sql,它记录的是每个执行计划中排序/哈希操作的实际内存与磁盘使用情况。
关键点在于:v$sql_workarea 的 sql_id 是稳定可关联的,且只要该 SQL 还在库缓存中,就能拿到完整语句。执行以下查询:
SELECT w.sql_id, w.operation_type, w.estimated_optimal_size, w.actual_mem_used,
w.max_mem_used, w.number_passes, w.tempseg_size,
s.sql_text
FROM v$sql_workarea w
JOIN v$sql s ON w.sql_id = s.sql_id
WHERE w.tempseg_size > 104857600 -- 大于100MB的临时段使用
ORDER BY w.tempseg_size DESC;
注意:tempseg_size 是累计写入临时表空间的字节数(不是当前占用),所以它反映的是“这次执行总共刷了多少数据到磁盘”,比 v$sort_usage.bytes_used 更能说明问题严重性。
为什么V$SQL_WORKAREA显示的tempseg_size远大于v$sort_usage
这是常见误解来源。v$sort_usage 显示的是“当前会话正在使用的临时段大小”,属于瞬时快照;而 v$sql_workarea 的 tempseg_size 是“该 SQL 执行过程中所有工作区(workarea)累计写入临时表空间的总字节数”。一次大排序可能分多轮写入、多次读取,每轮都计入 tempseg_size,但 v$sort_usage 只显示最后一轮还在占着的那部分。
容易踩的坑:
-
v$sql_workarea不包含已执行完且被清除的工作区信息——如果 SQL 执行太快、或 PGA 足够大没溢出,这里就查不到记录 -
number_passes > 1是危险信号,说明排序/哈希被迫做了多次磁盘往返,性能必然差,且临时空间消耗呈倍数增长 - 同一个
sql_id可能对应多个operation_type(如 HASH-JOIN 和 SORT-ORDER BY 同时存在),要逐条看tempseg_size
如何用V$SQL_WORKAREA判断是否该优化SQL而不是加空间
加临时文件只是兜底手段,真正该拦截的是那些低效 SQL。从 v$sql_workarea 数据可以快速做判断:
- 若
actual_mem_used接近estimated_optimal_size,但number_passes = 1,说明只是数据量大,内存刚好不够——这时调高pga_aggregate_target或加临时空间更合理 - 若
number_passes > 1,且max_mem_used远小于estimated_optimal_size,说明 PGA 分配严重不足或统计信息过期,导致优化器低估了内存需求——必须更新统计信息或加 hint 强制走合适执行计划 - 若
operation_type = 'GROUP BY'且tempseg_size极高,但业务上其实只需要前 N 行(比如分页报表),却写了ORDER BY ... FETCH FIRST 20 ROWS ONLY之前就做了全量 GROUP ——这种应改写为物化中间结果或加过滤条件
监控脚本里最容易漏掉的权限和刷新时机
想把 v$sql_workarea 加进日常巡检脚本?先确认两点:
- 账号必须有
SELECT权限在v_$sql_workarea(注意带下划线和 $ 符号),普通SELECT ANY DICTIONARY不够,得显式授权:GRANT SELECT ON v_$sql_workarea TO your_monitor_user; -
v$sql_workarea是基于共享池中执行计划的实时视图,不是每秒刷新。如果 SQL 执行完就立刻查,很可能查不到——建议搭配dba_hist_sql_workarea(AWR 快照)用于事后分析,或用DBMS_SQLTUNE.REPORT_SQL_MONITOR抓正在运行的长 SQL - Oracle 11g 及以后版本才支持
tempseg_size字段,老版本只能靠v$sort_usage+v$session关联,精度差很多
真正难的从来不是查出是谁占的,而是判断这个“谁”是不是本不该这么占——v$sql_workarea 提供的是带上下文的用量证据,不是简单计数器。盯着它看,比盲目扩容更有价值。











