MLOG$_表写入是同步、强制、不可绕过的内核级操作,每条DML必触发日志表插入且须等REDO落盘和索引更新完成才提交,导致高频场景性能雪崩。

MLOG$_ 表写入是同步、强制、不可绕过的内核级操作,每条基表 INSERT/UPDATE/DELETE 都会额外触发一次日志表插入,且必须等其 REDO 落盘、索引更新完成才能提交——这不是“偶尔慢”,而是每条 DML 稳稳多出几毫秒开销,高频场景下性能雪崩不可避免。
为什么每次DML都变慢:日志写入不是触发器,是事务强绑定
Oracle 不是靠触发器模拟日志记录,而是在 DML 执行路径中直接重写执行计划:只要基表存在 MLOG$_xxx,内核就会在同一个事务里追加一条日志行。这意味着:
- 无法异步、不能批处理,
COMMIT前必须等日志表的INSERT完成(含索引块更新、REDO写入、IO 确认) - 实测:10 万行
INSERT无日志耗时 0.21 秒;加日志后升至 3.73 秒;再叠加快速刷新,直接涨到 8.98 秒 -
enq: TX - row lock contention和db file sequential read等待在 OLTP 下高频出现,本质是日志表索引争用和 IO 延迟放大
日志表所在表空间配置错误会显著放大延迟
日志表 MLOG$_xxx 是普通堆表,性能完全取决于它所在的表空间。混放 USERS 或 SYSTEM 是最常见误操作:
- 手动段管理(
MANUAL)导致频繁递归 SQL 分配区段,高并发下锁表严重 - 未启用
AUTOALLOCATE,EXTENT分配争用加剧 - 与业务数据共享 LUN/SSD,IO 被打满,
log file sync时间飙升 - 查定位:
SELECT table_name, tablespace_name FROM user_tables WHERE table_name LIKE 'MLOG$%';确认属性:SELECT extent_management, allocation_type FROM dba_tablespaces WHERE tablespace_name = 'XXX'
正确做法:建专用 LOCAL + AUTOALLOCATE 表空间,挂 SSD 存储,只放日志表。
冗余索引让单条日志插入从 0.2ms 涨到 1.5ms+
Oracle 默认只建两个必要索引:I_MLOG$_xxx(主键 ROWID)和 I_SNAP$_xxx(加速 SNAPTIME$$ 查询)。但很多人额外加 ON (seq$$) 或 ON (snaptime$$, dmltype$$):
- 每条日志插入都要同步更新所有索引块,OLTP 场景极易引发
latch: cache buffers chains - 查冗余索引:
SELECT index_name, column_name FROM user_ind_columns WHERE table_name LIKE 'MLOG$%' ORDER BY index_name - 只保留 Oracle 自建的两个,其余一律
DROP INDEX
ROW MOVEMENT 和 COMPRESS 对日志表完全有害无益
这两个特性在日志表上不仅不省空间、不提性能,反而拖慢 INSERT:
-
ROW MOVEMENT在行迁移或分区维护时引入额外元数据操作,增加事务复杂度 -
COMPRESS FOR OLTP强制压缩解压流程,对以追加写为主的日志表毫无收益,反增 CPU 和 latch 开销 - 实测显示:开启
COMPRESS后单条日志插入延迟上升 40%+,且更容易触发ORA-01555
真正关键的是控制日志写入密度——比如只记录被物化视图实际引用的列、禁用 INCLUDING NEW VALUES(除非真需要旧值),而不是在日志表上堆砌优化手段。日志表越“轻”,基表 DML 才越快。



















