SEQUENCE$$列本身不加锁,但FAST刷新按其顺序扫描日志时会对基表被变更行加TX行锁;卡顿主因是MLOG$_xxx缺乏snaptime$$与sequence$$复合索引、统计信息陈旧及atomic_refresh=TRUE导致锁持有时间过长。

SEQUENCE$$列本身不加锁,但FAST刷新读取它时会触发基表行锁
物化视图日志(MLOG$_xxx)里的SEQUENCE$$列只是个递增数值,用于保证DML变更顺序。它自己不会锁基表,也不会被业务DML修改——真正引发争用的是DBMS_MVIEW.REFRESH执行FAST刷新时,按SEQUENCE$$顺序扫描日志并回放对应DML的过程。这个回放操作会对基表上被变更的每一行加TX行锁。
为什么按SEQUENCE$$顺序读日志容易卡住
当基表写入频繁、日志表缺乏合适索引时,刷新线程扫描MLOG$_xxx的效率会急剧下降:
-
SEQUENCE$$列默认无索引,全表扫描+排序极易触发大量Sort Segment临时段争用 - 多个刷新会话并发读同一日志表,会争抢
MLOG$_xxx上的TX锁(不是基表,是日志表自身) - 若日志表统计信息陈旧,CBO可能选错执行计划,让刷新卡在
db file sequential read上,间接拉长持锁时间 - 更隐蔽的问题:
SEQUENCE$$值连续不代表事务提交时间连续,刷新可能“提前”处理未提交事务的记录,导致与业务会话互等
atomic_refresh=FALSE能绕过SEQUENCE$$带来的锁延迟吗
不能绕过,但能大幅压缩锁窗口:
- 设
atomic_refresh => FALSE后,刷新不再把所有DML包在一个事务里,而是按日志批次分批提交——每批只锁当前处理的几行,SEQUENCE$$仍要读,但锁持有时间从分钟级降到秒级 - 注意:该参数对
COMPLETE刷新无效;若物化视图定义含聚合或连接,FAST刷新可能静默退化,SEQUENCE$$就失去意义 - 验证是否真走FAST:
SELECT fast_refreshable FROM user_mviews WHERE mview_name = 'MV_NAME',返回必须是FAST而非DIRLOADDML
最常被忽略的优化点:日志表索引和统计信息
没人会想到给MLOG$_xxx建索引,但它恰恰是SEQUENCE$$相关争用的根因之一:
- 必须创建复合索引:
CREATE INDEX idx_mlog_snap_seq ON MLOG$_SALES (snaptime$$, sequence$$)——这是Oracle官方文档明确推荐的,尤其在并行刷新场景下 -
ANALYZE TABLE MLOG$_SALES COMPUTE STATISTICS或DBMS_STATS.GATHER_TABLE_STATS要定期执行,否则CBO无法正确估算日志扫描代价 - 别依赖
WITH SEQUENCE自动建索引:它只建SEQUENCE$$单列索引,而实际刷新谓词通常是snaptime$$ :y,单列索引完全无效
真正卡住你的往往不是SEQUENCE$$本身,而是没索引的日志表+过期统计信息+默认的atomic_refresh=TRUE三者叠加。改一个参数不如三者齐动。


















