MLOG$_表暴增本质是消费阻塞而非写入过快;超10万条且持续上涨即判定失控,需查SNAPTIME$$确认滞后,并通过PURGE_LOG或NOLOGGING+TRUNCATE紧急清理,但必须立即执行成功FAST刷新以避免变更丢失。
MLOG$_ 表暴增不是日志“写得太快”,而是“没人来收”。只要刷新滞后,它就只进不出——这不是容量问题,是消费阻塞问题。
怎么判断日志表是否已失控?
别等表空间报警。直接查两条语句:
-
SELECT COUNT(*) FROM MLOG$_YOUR_TABLE_NAME;—— 超过 10 万且持续上涨,基本确认消费卡死 SELECT SNAPTIME$$, DMLTYPE$$, OLD_NEW$$ FROM MLOG$_YOUR_TABLE_NAME WHERE ROWNUM —— 看 <code>SNAPTIME$$是否还停留在公元 4000 年(4000-01-01 00:00:00),是则说明从未成功 FAST 刷新过
DBMS_MVIEW.PURGE_LOG 能删哪些记录?
DBMS_MVIEW.PURGE_LOG 不按系统时间删,而是按日志里每条记录的 SNAPTIME$$ 字段删。这个时间戳由刷新作业写入,代表“该记录已被哪个物化视图消费过”。所以:
- 必须确保所有依赖该日志的物化视图都已完成至少一次 FAST 刷新,否则
SNAPTIME$$全是 4000 年,PURGE_LOG(num_days => 1)一条也删不掉 - 如果多个物化视图共用同一张日志,
num_days是取它们各自最新SNAPTIME$$的最小值;只要有一个 MV 滞后,所有日志都保留在那儿 - 执行前建议先加锁:
LOCK TABLE MLOG$_YOUR_TABLE_NAME IN EXCLUSIVE MODE;,避免 purge 过程中触发器写入新记录导致状态错乱
紧急收缩日志表时为什么必须禁用 LOGGING?
高并发 DML 下,MLOG$_ 表本身会产生大量 REDO,进一步拖慢刷新速度。而 TRUNCATE TABLE 默认走完整事务日志路径,会加剧压力。所以:
- 先执行
ALTER TABLE MLOG$_YOUR_TABLE_NAME NOLOGGING;,跳过 REDO 写入 - 再
TRUNCATE TABLE MLOG$_YOUR_TABLE_NAME;,瞬间清空 - 但必须立刻执行一次成功的
DBMS_MVIEW.REFRESH('MV_NAME', 'F');,否则后续增量变更将永久丢失——因为日志从零开始,但物化视图的LAST_REFRESH_DATE没变,Oracle 无法对齐变更起点
为什么定期清理脚本常失效?
很多 DBA 写定时任务跑 PURGE_LOG,却没意识到它依赖刷新状态。常见失效点:
- 物化视图刷新作业失败后状态变成
BROKEN,dba_scheduler_jobs里查不到报错,但PURGE_LOG始终无效果 - 日志表建在源表同表空间,而该表空间开启了
FORCE LOGGING,导致NOLOGGING失效,TRUNCATE依然慢且产生 REDO - 业务允许延迟,但误用了
ON COMMIT刷新模式——每次事务提交都触发刷新,反而因锁争用导致刷新排队,日志越积越多
SNAPTIME$$ 字段才是日志生命周期的开关,盯住它比盯表大小重要得多。











