物化视图刷新本身不直接产生日志,真正撑爆归档和redo的是源表dml、日志表写入、刷新时的insert/update/delete三重叠加,关键在于日志表未控制写入开销。
物化视图刷新本身不直接产生日志,真正撑爆归档和 redo 的是源表 dml + 日志表写入 + 刷新时的 insert/update/delete 三重叠加。关键不在“怎么刷”,而在“谁在记、怎么记、谁在读”。
为什么 MLOG$_ 表暴增会连带拉高归档日志量
物化视图日志表(如 MLOG$_BIG_TABLE)默认继承源表的 LOGGING 属性。高频 INSERT 进日志表本身就会生成大量 REDO —— 这和源表 DML 产生的 REDO 是两份独立开销。更糟的是,FAST 刷新时物化视图基表的变更(比如 UPDATE 某些字段)又会再打一次 REDO。
- 源表每秒 200 次
UPDATE→ 触发 200 条日志表INSERT→ 产生 REDO A -
MLOG$_BIG_TABLE表自身是LOGGING→ 每条INSERT再产 REDO B - FAST 刷新执行
MERGE或UPDATE→ 物化视图表变更再产 REDO C
三者叠加,归档日志翻几倍很正常。不是刷新错了,是日志表没控住写入开销。
紧急止血:停写 + 降开销 + 强制消费
别急着 TRUNCATE MLOG$_XXX,先确认刷新链路是否已中断。查 DBA_SCHEDULER_JOBS 中状态为 BROKEN 或长时间未运行的刷新任务;再查 DBA_MVIEW_LOGS 的 LAST_REFRESH_DATE 是否明显滞后。
- 立刻禁用无效刷新作业:
DBMS_SCHEDULER.DISABLE('MV_REFRESH_JOB_NAME') - 临时关闭日志表 REDO:
ALTER TABLE MLOG$_BIG_TABLE NOLOGGING(仅限紧急,之后必须补一次成功 FAST 刷新) - 用官方接口清积压:
EXEC DBMS_MVIEW.PURGE_LOG('BIG_TABLE')(比TRUNCATE安全,会同步清理内部快照时间戳) - 重建日志前,确认物化视图定义支持 FAST:不能含
SELECT *、聚合无GROUP BY、不能跨数据库链接
CREATE MATERIALIZED VIEW LOG 的参数选择直接影响日志体积
日志表字段越多、记录越细,体积越大、写入越慢、REDO 越多。不是所有列都需要进日志。
- 只记录真正被物化视图 SQL 引用的列:
WITH SEQUENCE, ROWID (id, status),而非全字段 - 避免
INCLUDING NEW VALUES—— 它会让每条 UPDATE 记两条(旧值+新值),日志翻倍;除非物化视图 SQL 明确用了OLD_NEW$或需要 UPDATING 判断 - 如果物化视图只做 INSERT/DELETE 同步(无 UPDATE 逻辑),可省略
SEQUENCE,改用ROWID+PRIMARY KEY,减少序列维护开销
示例安全写法:CREATE MATERIALIZED VIEW LOG ON big_table WITH ROWID (id, created_at) INCLUDING NEW VALUES; —— 注意这里保留 INCLUDING NEW VALUES 是因物化视图 SQL 里用了 NEW 关键字;若没用,就删掉它。
刷新触发时机比刷新方式更容易被忽略
很多人以为设了 REFRESH FAST ON COMMIT 就万事大吉,但 Oracle 实际只在事务提交后 *异步* 触发日志消费。如果源表事务极短、极密(如批处理每毫秒提交一次),日志消费可能永远追不上写入速度,形成“写入洪水”。
-
ON COMMIT适合低频、业务事务粒度大的场景;高频 DML 必须切回ON DEMAND+ 定时 JOB - 定时 JOB 不要设太密(比如 1 秒一次),建议从 30 秒起步,观察
SNAPTIME$$和日志表COUNT(*)差值是否收敛 - 刷新 JOB 内务必加异常捕获:失败时不退出,而是记录日志并 sleep 后重试,避免“一挂全停”导致日志雪崩
最常被跳过的细节:日志表空间和源表不在同一表空间时,NOLOGGING 操作可能因权限或 ASM 配置失败;执行前先验证 ALTER TABLE ... NOLOGGING 是否真生效(查 USER_TABLES.LOGGING)。











