ASH无法直接记录全表扫描,但可通过过滤SAMPLE_TIME、session_type和event/session_state,再关联v$sql_plan中operation='TABLE ACCESS'且options='FULL'定位正在全表扫描的SQL;需结合AWR与执行计划验证根因。
怎么从ASH里揪出正在做全表扫描的SQL
ash本身不直接记录“是否全表扫描”,但能通过operation和options字段反推——关键不是查执行计划快照,而是找那些在table access full节点上长期卡住的会话。
直接查v$active_session_history时必须加三重过滤:
-
SAMPLE_TIME > SYSDATE - INTERVAL '10' MINUTE:避免查太久远数据导致性能抖动或内存溢出 -
session_type = 'FOREGROUND':排除DBW0、LGWR等后台进程干扰 -
event LIKE 'db file%' OR session_state = 'ON CPU':全表扫描要么大量单块读(db file sequential read),要么CPU密集计算(比如带FILTER操作的全表扫)
再关联v$sql_plan确认执行路径:
SELECT DISTINCT p.sql_id, t.sql_text FROM v$active_session_history h JOIN v$sql_plan p ON h.sql_id = p.sql_id AND h.sql_plan_hash_value = p.plan_hash_value JOIN v$sqltext t ON p.sql_id = t.sql_id AND t.piece = 0 WHERE h.sample_time > SYSDATE - INTERVAL '10' MINUTE AND h.session_type = 'FOREGROUND' AND (h.event LIKE 'db file%' OR h.session_state = 'ON CPU') AND p.operation = 'TABLE ACCESS' AND p.options = 'FULL';
为什么查到的SQL_ID在v$sql里找不到文本
常见现象:ASH里高频出现某个sql_id,但v$sql里查不到sql_text。这不是数据丢了,是共享池老化机制在起作用。
原因很实际:
- 该SQL已执行完毕,且被后续硬解析挤出共享池——
v$sql只存当前缓存中的语句 - 它来自PL/SQL匿名块或动态SQL(如
EXECUTE IMMEDIATE),Oracle不保证持久化进v$sql - 绑定变量窥探导致多个子游标,ASH采样的是实际执行的那个,但主游标已被刷掉
别死磕v$sql,优先走历史视图:
- 查
DBA_HIST_SQLTEXT:只要该SQL进过AWR快照,就有机会恢复文本 - 用
sql_plan_hash_value查DBA_HIST_SQL_PLAN:至少能看出是否真走了TABLE ACCESS FULL,以及驱动表位置 - 如果连
DBA_HIST_SQLTEXT都空,说明这条SQL压根没进过AWR(比如未启用自动快照或采样间隔太长)
怎么区分是真慢还是假热点
ASH里COUNT(*)高,不代表SQL一定有问题——可能是短查询高频调用,也可能是长查询单次霸占资源。得看样本分布形态。
典型误判场景:
- 某条SQL在10分钟内被采到600次,但全是
ON CPU状态 → 很可能单次跑10秒,只执行了60次,但每次都在CPU上“钉死” - 同一条SQL的
event在db file sequential read和ON CPU之间来回跳 → 说明它在边扫边算(比如带WHERE条件过滤+函数计算),不是纯I/O瓶颈 -
current_obj#指向大表,但sql_plan_hash_value显示走的是INDEX FAST FULL SCAN→ 实际没全表扫,只是索引块全读,逻辑读高但物理读未必高
真正要盯的是:sql_id + sql_plan_hash_value + session_state三者组合。同一sql_id若对应多个sql_plan_hash_value,且其中某个值下ON CPU占比超70%,基本可断定是执行计划劣化导致的CPU型全表扫。
容易被忽略的RAC和采样偏差问题
RAC环境下直接查v$active_session_history等于只看了一个节点的半截信息。更麻烦的是,_ash_sampling_interval隐含参数可能被调低,高负载时采样稀疏,TABLE ACCESS FULL这种本就短暂的操作极易漏掉。
实操要点:
- 必须用
gv$active_session_history,且加inst_id字段分组统计,否则会把不同实例的同一条SQL重复计数 - 别信
COUNT(*) ≈ 毫秒数——默认1秒1采,但实际受负载影响,可能2秒才采一次;尤其在大量TABLE ACCESS FULL并发时,采样点可能全落在扫描开始/结束瞬间,中间过程全丢 - 如果
ASH里几乎看不到TABLE ACCESS FULL相关样本,但AWR报告中SQL ordered by Gets里某条SQL逻辑读异常高,优先查它的执行计划(DBA_HIST_SQL_PLAN),而不是等ASH“抓现行”
最稳妥的做法永远是:ASH定位活跃窗口 → AWR确认执行计划和资源消耗趋势 → v$sql_plan或DBA_HIST_SQL_PLAN验证是否真走全表扫。单靠ASH,只能告诉你“谁正在扫”,不能告诉你“为什么扫”。











