MLOG$_表空间暴增是因日志仅标记消费而不自动删数据,需通过DROP MATERIALIZED VIEW LOG或PURGE_MVIEW_FROM_LOG清理;直接TRUNCATE/DROP会破坏数据字典,引发ORA-00604等错误。

MLOG$_ 表空间暴增不是“突然发生”的,而是物化视图日志机制与实际使用脱节的必然结果:它只标记消费、不自动删数据,只要刷新没跑、或刷新失败、或 MV 停用但日志没删,日志就持续累积。
MLOG$_ 表为什么不会自动释放空间
Oracle 的物化视图日志(MLOG$_xxx)本质是变更捕获队列,设计上不承诺物理清理:
- 每次 FAST 刷新后,Oracle 只更新
snaptime$$字段标记“该时间点前的变更已被消费”,不 DELETE 行 - 即使所有关联物化视图都已成功刷新,旧记录仍原样留在表中
- 如果某个 MV 长期未刷新(比如 ETL 任务中断、手动停用),
snaptime$$就卡在旧时间,后续所有 DML 都持续写入新日志行 - 日志表本身没有分区、无自动归档策略,也不响应
SHRINK SPACE
常见现象:基表才 50MB,MLOG$_orders 却涨到 180GB —— 这不是 bug,是机制使然。
怎么确认日志是否已被“遗忘”而仍在写入
不能只查有没有物化视图存在,得验证日志是否还在被消费:
查
DBA_REGISTERED_SNAPSHOTS:SELECT * FROM DBA_REGISTERED_SNAPSHOTS WHERE LOG_OWNER = 'SCOTT' AND LOG_NAME = 'MLOG$_EMP';
若返回空,说明没有任何 MV 当前注册使用该日志查
DBA_MVIEW_LOGS:SELECT LOG_OWNER, MASTER, ROWIDS, PRIMARY_KEY FROM DBA_MVIEW_LOGS WHERE LOG_TABLE = 'MLOG$_EMP';
确认该日志是否还绑定到任何基表,以及是否启用ROWIDS或PRIMARY_KEY(影响日志结构)查日志表本身:
SELECT MIN(snaptime$$), MAX(snaptime$$), COUNT(<em>) FROM SCOTT.MLOG$_EMP;</em>
若MIN(snaptime$$)是 2024 年、COUNT()超过千万,基本可断定长期无人消费
注意:DBA_MVIEWS 不反映日志绑定关系,查了也没用。
为什么 TRUNCATE / DROP TABLE 会引发更严重问题
直接操作 sys.mlog$_xxx 表是 Oracle 明确禁止的高危动作:
-
TRUNCATE TABLE sys.mlog$_emp:- 立刻释放空间,但段头块残留元信息
- 下次执行
CREATE MATERIALIZED VIEW LOG时大概率报ORA-12083 - 更隐蔽的是:后续 INSERT 可能触发异常扩展,甚至报
ORA-00604
-
DROP TABLE sys.mlog$_emp:- 直接破坏数据字典一致性
- 后续任何涉及该基表的 MV DDL 都可能失败
-
DELETE FROM sys.mlog$_emp:- 产生巨量 UNDO 和 REDO
- 锁表时间长,极易阻塞业务 DML
- 不降低 HWM,空间不真正释放
唯一合规路径只有两个:DROP MATERIALIZED VIEW LOG ON owner.table_name,或用 DBMS_MVIEW.PURGE_MVIEW_FROM_LOG 清理已消费部分。
多个物化视图共享一个日志时,清理边界怎么算
共享日志(common log)很常见,但清理不能按单个 MV 的节奏来:
- 所有共用该日志的 MV 中,以
LAST_REFRESH_DATE最晚的那个为准 -
PURGE_MVIEW_FROM_LOG只能清理 SCN ≤ 所有 MV 中最小LAST_REFRESH_DATE对应 SCN 的记录 - 如果 A MV 最后刷新是 2026-09-10,B MV 是 2026-08-01,那只能清理到 2026-08-01 之前的数据,B 的滞后会拖住整个清理窗口
查依据:SELECT mview_name, last_refresh_date FROM DBA_MVIEWS WHERE master = 'EMP';
再关联 DBA_BASE_TABLE_MVIEWS 获取对应 mvid,才能安全调用 purge 过程。
真正的麻烦点不在“怎么删”,而在于:你得先确认所有依赖它的 MV 是否真的不再需要、是否都已停用、是否有隐藏的定时任务在后台悄悄刷新——这些往往藏在运维脚本或调度系统里,而不是数据库里。


















