查 mlog$_ 表实际占用空间应使用 user_segments 而非 user_tables,因后者仅存元数据、不反映磁盘占用;segment_name 严格大写匹配,lob 列会导致多 segment,需 sum(bytes) 计算总空间。

查 MLOG$_ 表实际占用空间用 user_segments 而不是 user_tables
user_tables 只存元数据(比如状态、列数),不反映磁盘占用;真正分配了多少空间,得看 user_segments。物化视图日志表名如 MLOG$_EMP,在 user_segments 中的 segment_name 字段是大写的,必须严格匹配。
常见错误是直接查 user_tables 然后发现 bytes 列为空——那不是没数据,是你查错了视图。
- 普通用户默认可查
user_segments,但若被收回SELECT_CATALOG_ROLE权限,会报ORA-00942 - 一张表带 LOB 列时,
MLOG$_表本身可能对应多个 segment(主表段 + LOB 段 + LOB 索引段),SUM(bytes)才是总空间 - 示例:查当前用户下
MLOG$_EMP占用多少 MB:SELECT SUM(bytes) / 1024 / 1024 AS "size_mb" FROM user_segments WHERE segment_name = 'MLOG$_EMP';
确认高水位线(HWM)是否真拖慢刷新性能
仅看 size_mb 没用。关键要看已分配块和真实使用块的差距——差值大,说明 HWM 高但数据稀疏,全表扫描仍要扫大量空块。
DBMS_SPACE.SPACE_USAGE 是唯一能准确读出这个差距的函数,但它不修改任何东西,只诊断。
- 调用前确保表启用了行移动:
ALTER TABLE MLOG$_EMP ENABLE ROW MOVEMENT; - 重点关注输出里的
formatted_blocks - used_blocks:如果远大于 0,说明 HWM 下有大量空块,SHRINK 才有意义 - 如果
unformatted_blocks > 0,说明 HWM 后面真有未格式化块,RESIZE数据文件才可能成功 - 别用
dba_tables.blocks估算 HWM 位置,它不准;dba_extents的最大block_id + blocks才是物理边界
为什么 DBA_MVIEW_LOGS 的 LOG_TABLE 字段不能直接当大小依据
DBA_MVIEW_LOGS 里 LOG_TABLE 是日志表名(如 MLOG$_EMP),但它不存空间信息,也不反映是否被消费。更危险的是:多个物化视图可以共享同一个日志表,只要它们建在同一个基表上且参数兼容。
你看到 LOG_TABLE = 'MLOG$_EMP' 有记录,不代表这个日志还在活跃使用——可能所有 MV 都已删,但日志 DDL 没执行,表就一直空转占空间。
- 必须交叉验证:
DBA_REGISTERED_SNAPSHOTS中LOG_OWNER和LOG_NAME组合无返回,才说明没 MV 注册使用它 -
DBA_MVIEW_LOGS里还要检查LAST_REFRESH_DATE,多个 MV 共享时,清理窗口由最晚的那个决定 - 误判为“可用”而直接
DROP MATERIALIZED VIEW LOG ON owner.table_name,可能中断其他还在用它的 MV 刷新
监控脚本里最容易漏掉的三件事
日常巡检如果只跑空间查询,大概率错过真正瓶颈。MLOG$_ 表的问题从来不在“多大”,而在“怎么用”。
- 没查
snaptime$$分布:执行SELECT MIN(snaptime$$), MAX(snaptime$$) FROM MLOG$_EMP,如果MIN是 2022 年,说明日志堆积严重,但 Oracle 还认为它有效 - 忽略索引缺失:
CREATE INDEX idx_mlog_snap_seq ON MLOG$_EMP (snaptime$$, sequence$$)是 FAST 刷新的关键路径,没它,每次刷新都全表扫 - 统计信息过期:
DBMS_STATS.GATHER_TABLE_STATS必须在日志表上定期运行,否则优化器永远按旧数据量估算,执行计划失真
真正卡住人的,是查了空间、看了 HWM、甚至清了旧记录,却忘了建索引、没更新统计信息、也没确认 snaptime$$ 是否被所有 MV 正确推进。











