直接TRUNCATE sys.mlog$_xxx会破坏数据字典并导致ORA-12003等错误,因HWM下降但元信息残留、snaptime$$未更新;安全删除需交叉验证DBA_REGISTERED_SNAPSHOTS和DBA_MVIEW_LOGS,并导出依赖快照。
mlog$_ 表空间暴增不是“满了就能 truncate”的问题——直接 truncate table sys.mlog$_xxx 会破坏数据字典,后续建同名物化视图大概率报 ora-12003 或刷新失败;而定期手动清理又容易误删活跃日志。真正要解决的,是日志堆积背后的消费断链和刷新失控。
为什么 TRUNCATE MLOG$_ 表会出事
Oracle 不允许也不支持直接操作 sys.mlog$_xxx 表:
- TRUNCATE 后高水位线(HWM)虽降,但段头块仍残留日志结构元信息,后续插入可能触发异常扩展,甚至引发 ORA-00604
- DROP TABLE 直接损坏数据字典,再执行 CREATE MATERIALIZED VIEW LOG 时可能报 ORA-12083
- 日志表是否被消费,由 snaptime$$ 字段标记,而非物理删除行;truncate 不改这个标记,Oracle 仍认为“日志还在”,下次刷新会继续写入
怎么安全确认一个 MLOG$_ 表可删除
不能只查 DBA_MVIEWS,它不记录日志绑定关系;也不能只看有没有 MV 存在。必须交叉验证两个视图:
- 查询 DBA_REGISTERED_SNAPSHOTS:若 LOG_OWNER 和 LOG_NAME(如 MLOG$_EMP)组合无返回,说明没有物化视图注册使用该日志
- 查询 DBA_MVIEW_LOGS:确认该日志未被其他 MV 引用,尤其注意多个 MV 可共享一个日志,得逐个核对 LOG_TABLE 和 LOG_OWNER
- 执行前务必导出当前依赖快照:SELECT LOG_OWNER, LOG_TABLE, MASTER, ROWIDS, PRIMARY_KEY FROM DBA_MVIEW_LOGS WHERE LOG_TABLE = 'MLOG$_EMP'
purge_mview_from_log 能清什么、不能清什么
这个内置过程只清理「已被所有活跃 MV 消费」的旧记录,不释放空间,也不影响未消费部分:
- mvid 必须来自 DBA_BASE_TABLE_MVIEWS(不是 DBA_MVIEWS),查 BASE_TABLE_OWNER + BASE_TABLE_NAME 对应的 MVIEW_ID
- 若某个 MV 的 LAST_REFRESH_DATE 远早于日志中 MIN(SNAPTIME$$),PURGE_MVIEW_FROM_LOG 会因 SCN 断层失败
- 示例:BEGIN DBMS_MVIEW.PURGE_MVIEW_FROM_LOG(123); END; —— 123 是从 DBA_BASE_TABLE_MVIEWS 查到的有效 ID
- 多个 MV 共享日志时,清理边界由最晚的 LAST_REFRESH_DATE 决定,不能只按单个 MV 清
SYSTEM 表空间爆满?别迁 MLOG$_,去查 IDL_UB1$
MLOG$_ 表本身通常不在 SYSTEM 表空间(默认随源表走),真正撑爆 SYSTEM 的是 IDL_UB1$、IDL_UB2$ 这类 PL/SQL 编译缓存表:
- 每次 FAST 刷新都会触发物化视图定义重编译,大量写入 IDL_UB1$
- 高频刷新 + 复杂查询定义(含包、分析函数等)会让 IDL_UB1$ 在几小时内涨到几十 GB
- 紧急收缩步骤:先停掉 DBA_SCHEDULER_JOBS 中状态为 BROKEN 或 FAILED 的刷新作业;再执行 EXEC DBMS_UTILITY.COMPILE_SCHEMA('SYS');必要时临时 UNREGISTER_MVIEW 断开日志链路
实际清理动作永远围绕两点:谁还在用这个日志、谁最后一次消费了它。跳过验证就动手,比空间满更危险的是元数据错乱。


















