物化视图刷新慢不能只查DBA_MVIEWS.LAST_REFRESH_DATE,因其仅记录完成时间;真正瓶颈需通过DBA_HIST_ACTIVE_SESS_HISTORY结合session_state='WAITING'、program含'M00'或'oracle@%M%'定位递归SQL的等待事件,如enq: TX锁争用、MLOG$表全表扫描或direct path write IO问题。
物化视图刷新慢,为什么只查DBA_MVIEWS.LAST_REFRESH_DATE没用
因为该字段只记录刷新完成时间,不反映过程卡点。真正拖慢刷新的,往往是某条递归sql在等锁、扫日志表或写临时段——这些都藏在ash里。刷新可能耗时5分钟,但其中4分50秒都在等enq: tx - row lock contention,而你只看到“总耗时5分钟”这个结果。
关键判断:只要刷新耗时明显超过基表DML本身(比如插入1万行只要0.2秒,但FAST刷新要8秒),就必须进ASH看实际在等什么。
- 刷新是后台会话触发的,
top_level_sql_id通常为空或为系统生成ID,得靠sql_id+program组合定位,常见值是ORACLE.EXE或oracle@host (M000) - 别只查
v$active_session_history,它默认只保留约1小时;优先查dba_hist_active_sess_history,配合sample_time BETWEEN 刷新开始时间 AND 刷新结束时间 - 必须加
session_state = 'WAITING',否则大量ON CPU行会淹没真实等待
怎么从ASH里揪出刷新期间最耗时的SQL
物化视图刷新本质是一组递归SQL执行:先查MLOG$_xxx,再对物化视图做INSERT/UPDATE/DELETE。这些SQL不会出现在v$sqlarea的用户SQL列表里,但一定留在ASH中。
执行这条查询(替换'2026-05-10 18:30:00'为你的刷新时间段):
SELECT sql_id, event, COUNT(*) cnt, ROUND(AVG(time_waited)/1000, 2) avg_ms FROM dba_hist_active_sess_history WHERE sample_time BETWEEN TIMESTAMP '2026-05-10 18:30:00' AND TIMESTAMP '2026-05-10 18:35:00' AND session_state = 'WAITING' AND program LIKE '%M00%' OR program LIKE '%oracle@%M%' GROUP BY sql_id, event ORDER BY cnt DESC;
重点盯三类sql_id:
- 访问
MLOG$_表的,如SELECT * FROM MLOG$_SALES WHERE SNAPTIME$$ <= :1——若event是db file sequential read且cnt极高,说明日志表没索引或膨胀了 - 写物化视图本身的,如
INSERT /*+ APPEND */ INTO MV_SALES_MONTHLY——若event是direct path write,可能是并行度太高或临时表空间IO瓶颈 - 锁相关的,如
enq: TM - contention,说明刷新会话和基表DML在争同一张表的DML锁,得查p1解出对象ID,再联dba_objects
为什么10046 trace和ASH要一起用
10046 trace能告诉你“执行了哪些SQL”,ASH能告诉你“哪条SQL在等什么”。单用任一者都缺一半真相。比如trace显示一条UPDATE MLOG$_SALES SET ...跑了3秒,但ASH显示这3秒全在等log file sync——问题就不是SQL本身,而是基表提交太频繁,导致刷新被日志写入拖住。
实操建议:
- 刷新前在刷新会话里执行:
ALTER SESSION SET EVENTS '10046 trace name context forever, level 12'; - 刷新完成后立刻关掉:
ALTER SESSION SET EVENTS '10046 trace name context off'; - 用
tkprof解析trace文件,找Elapsed Times最高的递归SQL;再拿它的sql_id去ASH里查等待事件分布 - 特别注意
WAIT #行里的p1text,比如file#对应db file scattered read,就能定位到具体数据文件是否热点
容易忽略的ASH采样盲区
ASH每秒采样一次,但物化视图刷新中的某些动作可能快于1秒,比如小批量日志清理或索引维护,就容易漏掉。如果发现ASH里等待事件断断续续、时间对不上,别急着下结论。
这时要交叉验证:
- 查
dba_hist_sqlstat里对应sql_id的elapsed_time_delta和buffer_gets_delta,确认是否真有高逻辑读 - 用
SELECT * FROM v$transaction看刷新期间是否有长事务未提交,导致日志无法消费 - 检查
MLOG$_xxx表的NUM_ROWS是否持续增长却不下降——ASH看不到日志堆积,但SELECT COUNT(*) FROM MLOG$_SALES一眼可见
最麻烦的情况是:ASH显示大量cursor: pin S wait on X,但sql_id全是系统生成的。这往往意味着物化视图刷新触发了硬解析风暴,根源在绑定变量未重用或共享池压力大,得切到v$latch_children和v$librarycache继续挖。



















