Oracle物化视图FAST REFRESH默认锁整分区表,因物化视图日志缺失分区键信息,无法定位变更分区;需同时满足日志含分区键列且MV定义显式引用该列,才能实现分区粒度加锁。 物化视图刷新时为什么锁分区表整表? 很多DBA都遇到过这样的困惑:明明只想刷新分区表的一个分区,为什么执行FAST RE
很多DBA都遇到过这样的困惑:明明只想刷新分区表的一个分区,为什么执行FAST REFRESH时,整个基表都被锁住了?这事儿,得从Oracle的“保守策略”说起。
简单来说,Oracle在执行分区表物化视图的快速刷新时,默认并不会“聪明地”只锁定变更涉及的分区。相反,它会为整个基表申请一个TM锁(也就是DML队列锁)。哪怕你只刷新一个分区的数据,这把锁也会罩住整张表。其根本原因在于,传统的物化视图日志(mlog$)里,只记录了哪些行被改了(ROWID),却没有记录这些行到底属于哪个分区。面对这种情况,Oracle无法判断变更具体落在哪个分区,为了确保数据一致性,只能采取最保守的策略——锁全表。
长期稳定更新的攒劲资源: >>>点此立即查看<<<
这种锁表行为带来的直接症状,通常非常明显:
ORA-00054: resource busy and acquire with NOWAIT specified。v$lock视图中,能看到大量针对基表的TM锁,状态多为ROW EXCLUSIVE或SHARE。当出现这些现象时,该怎么快速定位呢?可以按以下几步走:
v$session视图,看看阻塞会话的sql_id是否指向DBMS_MVIEW.REFRESH过程或相关的内部刷新SQL。SELECT * FROM USER_MVIEW_LOGS WHERE MASTER = ‘YOUR_PARTITIONED_TABLE’;。如果查询结果显示日志表只有ROWID列,而缺少分区键列,那就基本坐实了问题根源——日志没有提供分区裁剪的依据。COMPLETE REFRESH。这种刷新方式会隐式地锁住整个基表并重写整个物化视图,比FAST REFRESH的锁冲突要激烈得多。那么,有没有办法让Oracle“聪明”起来,只锁定需要刷新的那个分区呢?答案是肯定的,但需要满足两个缺一不可的条件:物化视图日志必须包含分区键列,并且物化视图的定义里必须显式引用这个列,以便Oracle进行分区裁剪。
举个例子,假设我们有一张分区表Sales,按照sale_date字段进行范围分区,并且希望以这个日期作为刷新边界。具体操作如下:
CREATE MATERIALIZED VIEW LOG ON Sales ADD (sale_date) WITH ROWID, SEQUENCE (sale_date, amount, prod_id) INCLUDING NEW VALUES;
sale_date这个分区键列出现在SELECT列表中,并且不能被任何函数包裹(比如写成TRUNC(sale_date)就不行)。
CREATE MATERIALIZED VIEW mv_sales_daily
BUILD IMMEDIATE REFRESH FAST ON DEMAND
AS SELECT sale_date, prod_id, SUM(amount) amt
FROM Sales GROUP BY sale_date, prod_id;
DBMS_MVIEW.REFRESH接口进行刷新。
EXEC DBMS_MVIEW.REFRESH(‘mv_sales_daily’, METHOD => ‘F’,
ROLLBACK_SEG => NULL, PUSH_DEFERRED_RPC => TRUE,
REFRESH_AFTER_ERRORS => FALSE, PURGE_OPTION => 1,
PARALLELISM => 0, HEAP_SIZE => 0,
ATOMIC_REFRESH => FALSE);
这里有个关键点:只有当物化视图定义包含了分区键,并且日志也同步记录后,Oracle内部才会启用分区感知的刷新逻辑,从而实现分区粒度的加锁。
关于ATOMIC_REFRESH => FALSE这个参数,很多人的理解是它能减少锁。这个说法没错,但它有明确的前提和不容忽视的副作用。
设置为FALSE后,刷新过程将放弃事务的原子性保障。具体来说,Oracle会先TRUNCATE物化视图,然后再执行INSERT操作。这样做的好处是跳过了物化视图表本身TM锁的升级过程,对缓解锁冲突的效果确实明显。但代价是,在TRUNCATE完成之后、INSERT结束之前的这个短暂窗口期,物化视图可能处于空状态,或者查询会返回旧数据。
ON COMMIT或ON DEMAND刷新模式使用,ON STATEMENT模式不支持此参数。TRUNCATE操作本身仍需对物化视图表加一个短暂的EXCLUSIVE锁(通常在毫秒级),但这远比全量INSERT过程中累积行锁的时间要短得多。v$locked_object视图,如果配置生效,应该看不到物化视图表上的锁,而只能看到基表上针对特定变更分区的细粒度锁。解决了锁的问题,是不是就高枕无忧了?未必。分区表物化视图在高并发场景下的真实瓶颈,往往藏在更深层的资源争用里,而不仅仅是锁等待。
MLOG$表热点:所有分区的DML变更,都会写入同一张物化视图日志表。这张表很容易成为I/O和buffer busy waits的源头。一个有效的解决方案是,手动对MLOG$表进行分区(例如按照SNAPTIME$$字段做范围分区),并且确保其上的索引也做相应的分区。MLOG$表依赖的隐式序列(SEQUENCE$$)更新,可能引发enq: TX row lock contention等待事件。可以考虑关闭日志的SEQUENCE记录(改用ROWID结合时间戳来判断变更顺序),但这需要接受在极端时序情况下,可能出现少量刷新遗漏的风险。PARALLELISM > 1,初衷是加速,但可能适得其反。如果多个并行进程试图同时刷新同一个分区,反而会加剧分区间的锁竞争。更稳妥的建议是,优先保证单个分区的刷新速度足够快,然后通过调度系统,错峰调用不同分区的刷新任务。所以,在进行真正的压力测试时,眼光不能只盯着锁等待事件。enq: TX - index contention、buffer busy waits、log file sync这些等待事件,往往才是分区物化视图在高并发下的真正性能卡点。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述