mlog$_表刷新后空间不释放是因为oracle仅标记“已消费”而不物理删除数据;truncate/delete/drop均存在高风险,应通过dba_registered_snapshots和dba_mview_logs确认日志无引用后,用drop materialized view log或dbms_mview.purge_log清理,并及时收集统计信息、重建索引。

MLOG$_ 表刷新后空间不释放,不是 Oracle “忘了删数据”,而是它根本没打算物理删除——只改 snaptime$$ 字段标记“已消费”,行还在那儿。
为什么 TRUNCATE 或 DELETE 都不能直接用
你看到日志表占了 80GB,第一反应可能是 TRUNCATE TABLE mlog$_orders。别动。这会触发三重风险:
-
TRUNCATE后高水位线(HWM)下降,但段头块残留元信息,后续插入可能引发ORA-00604级扩展异常 DELETE FROM mlog$_orders WHERE snaptime$$ 会全表扫描、锁表、撑爆 UNDO,还容易被长事务阻塞-
DROP TABLE sys.mlog$_orders直接破坏数据字典,再建同名日志大概率报ORA-12083
怎么确认这个日志真没人用了
不能只查 DBA_MVIEWS,它不记录日志绑定关系。必须交叉验证两个视图:
- 查
DBA_REGISTERED_SNAPSHOTS:若LOG_OWNER和LOG_NAME(如MLOG$_ORDERS)组合无返回,说明没有 MV 正在注册使用它 - 查
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$_ORDERS'
安全清理的两种路径:删整个日志 or 清旧记录
选哪条路,取决于你还想不想保留日志供其他 MV 使用:
- 如果确认完全孤立:直接走 DDL ——
DROP MATERIALIZED VIEW LOG ON schema.orders。这是唯一合规的“物理删除”方式 - 如果还想留着日志,只清已被所有 MV 消费的旧数据:调用
DBMS_MVIEW.PURGE_LOG('ORDERS', 7)(保留最近 7 天),或传入具体mvid调用PURGE_MVIEW_FROM_LOG;注意mvid必须来自DBA_BASE_TABLE_MVIEWS,不是DBA_MVIEWS - 多 MV 共享一个日志时,清理边界由所有 MV 中最晚的
LAST_REFRESH_DATE决定,不能只看单个
清理完不补这三步,空间照样不下来
删完不是结束。Oracle 不允许对 sys.mlog$_xxx 做 SHRINK SPACE,所以物理空间不会自动回收。你必须立刻做三件事:
- 收集统计信息:
DBMS_STATS.GATHER_TABLE_STATS('SCHEMA_NAME', 'MLOG$_ORDERS'),否则优化器仍走全表扫描 - 重建关键索引:
CREATE INDEX idx_mlog_snap_seq ON mlog$_orders (snaptime$$, sequence$$),这是快速刷新扫描性能的命脉 - 若空间仍未释放,只能导出有效数据 →
DROP MATERIALIZED VIEW LOG→ 重建日志
snaptime$$ 是否被所有 MV 正确推进。











