dbms_mview.refresh执行fast刷新时,因回放mlog$_xxx中的变更而在基表对应行加tx行锁;该锁源于刷新过程中的等效dml操作,而非日志表本身锁定基表,且on commit刷新同样触发此机制。
dbms_mview.refresh 执行 fast 刷新时,会读取 mlog$_xxx 中的变更记录,并对基表执行对应 dml(insert/update/delete),这个过程会对被修改的具体行加 tx 行锁 —— 不是日志表本身锁基表,而是刷新动作在回放变更时产生的锁。
FAST 刷新期间基表行锁是怎么来的
物化视图日志(MLOG$_xxx)本身是普通堆表,建好后不主动锁基表。真正触发锁的是 DBMS_MVIEW.REFRESH 调用 FAST 模式时的行为:
- Oracle 从日志表中读出未消费的变更(如某行被 UPDATE),然后在基表上执行等效 DML
- 这个 DML 必须保证事务一致性,所以会对目标行加 TX 锁(即行级排它锁)
- 如果业务会话正持有同一行的 TX 锁(比如 UPDATE 未提交),刷新就会卡住,报
enq: TX - row lock contention -
atomic_refresh => TRUE(默认)会让整个刷新包在一个事务里,锁持续到事务结束;设为FALSE则按批次 commit,锁只持几行、几秒
为什么 ON COMMIT 刷新也会卡业务 DML
ON COMMIT 刷新本质仍是 FAST 刷新的一种触发方式,只是时机由事务提交驱动。它不改变锁机制,只改变触发节奏:
- 用户事务一提交,Oracle 就立即启动 FAST 刷新流程
- 若此时基表某行刚被业务 UPDATE 提交,而刷新线程恰好要基于日志对该行做 DELETE 或 UPDATE,就会抢同一行的 TX 锁
- 更隐蔽的问题:多个 ON COMMIT MV 共享同一个基表日志,一个刷新慢了,会拖住其他 MV 的提交路径
- 日志表
MLOG$_xxx缺少(snaptime$$, sequence$$)复合索引时,刷新读取日志变慢,间接延长持锁窗口
atomic_refresh=FALSE 真的能解耦锁吗
能缩短锁时间,但不能消除锁冲突的本质:
- 设为
FALSE后,FAST 刷新退化为 TRUNCATE + INSERT /*+ APPEND */,不再逐行回放日志 → 基表上不再加 TX 行锁,只加 TM 表级锁(短暂,且仅在 TRUNCATE 时) - 但该模式仅对真正支持 FAST 的物化视图生效;查
user_mviews.fast_refreshable必须返回FAST,否则静默退化为 COMPLETE,反而更慢、锁更久 - TRUNCATE + APPEND 不写 redo,但要求基表无外键引用、无启用的触发器,否则报错
- 即使用了
atomic_refresh => FALSE,如果日志表本身被多个刷新会话争抢(比如高并发下同时刷多个 MV),仍可能卡在enq: TX等待日志表行锁
最容易被忽略的锁来源:日志表自身争抢
你以为锁都在基表?其实 MLOG$_xxx 这张表自己就是热点:
- 所有对基表的 DML 都要往日志表 INSERT 记录,高频写入下,
MLOG$_xxx成为瓶颈 - 多个
DBMS_MVIEW.REFRESH并发执行时,会同时扫描、清理同一条日志记录,触发日志表上的 TX 行锁争用 - 日志表若建在共享表空间、没开 AUTOALLOCATE、或存在冗余索引,INSERT 性能下降 → DML 延迟 → 日志堆积 → 刷新更慢 → 锁更久
- 查当前谁在锁日志表:
SELECT object_name FROM v$locked_object JOIN dba_objects USING(object_id) WHERE object_name LIKE 'MLOG$%'
锁不是配置问题,是刷新行为与业务 DML 在同一行上“撞车”的结果。调参(如改 atomic_refresh)只能调节锁的粒度和时长,没法绕过 Oracle 的事务一致性模型。真正要稳,得让刷新和业务错开节奏,或者把日志表 IO 和锁热点彻底隔离。











