查V$SESSION_LONGOPS可获实时刷新进度,因Oracle在此记录扫描类操作的sofar/totalwork;DBA_MVIEW_REFRESH_TIMES仅存历史结束时间,无法反映当前状态。
查 DBA_MVIEW_REFRESH_TIMES 能看到上次刷新时间,但看不到实时进度
这个视图只记录历史刷新的 last_refresh_date 和 refresh_method,不包含当前运行中的任务状态。如果你刚执行 dbms_mview.refresh,它不会立刻更新,得等刷新结束才写入——所以无法用于“正在刷到哪了”的判断。
真正能反映实时状态的是 V$SESSION_LONGOPS,只要刷新过程涉及大量数据扫描(比如 FAST 刷时解析 MLOG$、COMPLETE 刷时全表扫描基表),Oracle 就会在这里注册一条记录。
- 查当前活跃刷新:
SELECT opname, target, sofar, totalwork, units, elapsed_seconds FROM V$SESSION_LONGOPS WHERE opname LIKE '%refresh%' AND sofar -
sofar是已处理行数或块数,totalwork是预估总数;两者接近说明快完了 - 注意:
FAST刷新若日志极小(如只改 1 行),可能根本不上V$SESSION_LONGOPS,因为没触发 longops 门槛(默认 > 6 秒且有可计量 work)
用 V$SQL_MONITOR + SQL_ID 追踪单次刷新的执行细节
当你用 DBMS_MVIEW.REFRESH 手动触发,或者调度作业调用它时,底层实际跑的是动态 SQL(如 INSERT /*+ APPEND */ INTO ... SELECT ... FROM MLOG$_xxx)。这些语句只要开启监控(MONITOR hint 或 sql_monitor 参数为 TRUE),就能被 V$SQL_MONITOR 捕获。
- 先查刷新会话的 SQL_ID:
SELECT sql_id, prev_sql_id FROM V$SESSION WHERE program LIKE '%DBMS_MVIEW%' - 再查监控详情:
SELECT status, sql_text, elapsed_time, cpu_time, buffer_gets, reads FROM V$SQL_MONITOR WHERE sql_id = 'xxx' - 关键字段:
status是EXECUTING还是DONE;elapsed_time是真实耗时(微秒级),比DBA_MVIEW_REFRESH_TIMES的分钟级精度高得多 - 陷阱:默认只有并行度 ≥ 8 或执行时间 ≥ 5 秒的语句才进
V$SQL_MONITOR,如果快速刷新只花 2 秒,它可能为空
为什么 DBA_JOBS_RUNNING 对物化视图刷新几乎无效
DBA_JOBS_RUNNING 只显示老式 DBMS_JOB 提交的作业,而 Oracle 11g 中绝大多数定时刷新都走 DBMS_SCHEDULER(尤其是用 CREATE MATERIALIZED VIEW ... NEXT ... 语法创建的)。你在这张表里基本找不到任何记录。
- 正确查调度中任务:
SELECT job_name, state, session_id, running_instance FROM DBA_SCHEDULER_RUNNING_JOBS WHERE job_name LIKE '%MV%' - 但注意:它只告诉你“某个 job 在跑”,不告诉你“这个 job 正在刷哪个 MV”或“刷到哪一步”——job 名称通常是系统生成的(如
MAINTENANCE_WINDOW_JOB_123),和 MV 名无关 - 更实用的办法是结合
V$SESSION:查PROGRAM含DBMS_MVIEW且STATUS = 'ACTIVE'的会话,再关联V$SQL看其SQL_TEXT是否含MV_NAME或MLOG$
自建日志表捕获每次刷新的 start/end 时间与错误码
Oracle 不提供开箱即用的“每次刷新打点日志”,必须自己埋点。最轻量的方式是在调用 DBMS_MVIEW.REFRESH 前后插入时间戳到一张自定义表。
- 建日志表:
CREATE TABLE mv_refresh_log (mv_name VARCHAR2(30), start_time DATE, end_time DATE, duration_sec NUMBER(10,2), err_code VARCHAR2(10)) - 封装刷新逻辑(示例):
BEGIN INSERT INTO mv_refresh_log (mv_name, start_time) VALUES ('SALES_MV', SYSDATE); COMMIT; DBMS_MVIEW.REFRESH('SALES_MV', 'F'); UPDATE mv_refresh_log SET end_time = SYSDATE, duration_sec = (SYSDATE - start_time)*86400 WHERE mv_name = 'SALES_MV' AND end_time IS NULL; COMMIT; EXCEPTION WHEN OTHERS THEN UPDATE mv_refresh_log SET end_time = SYSDATE, err_code = SQLCODE WHERE mv_name = 'SALES_MV' AND end_time IS NULL; COMMIT; RAISE; END; - 这个方法唯一缺点:无法感知
ON COMMIT刷新——因为它由事务隐式触发,你没机会插桩
真实环境里,V$SESSION_LONGOPS 和自定义日志表配合用最可靠。别指望单个视图给出全部答案,Oracle 11g 的物化视图刷新监控本身就是拼图式的——你得把几个碎片对上时间戳才能还原出完整过程。


















