DBMS_JOB刷新任务卡在enq: JI-contention主因是调度重叠争抢JI enqueue,需错开JOB启动时间、显式声明刷新组依赖、补授ALTER ANY MATERIALIZED VIEW权限,并强制atomic_refresh=>FALSE避免长事务阻塞。
DBMS_JOB刷新任务卡在enq: JI - contention怎么办
这不是锁表本身的问题,而是多个dbms_job作业被调度到同一秒内启动,争抢同一个ji enqueue资源。oracle用这个锁来串行化同一物化视图的并发刷新,一旦时间重叠,后启动的作业就会卡住。
- 查重叠作业:
SELECT job, what, next_date, interval FROM dba_jobs WHERE what LIKE '%dbms_mview.refresh%' ORDER BY next_date,重点看next_date是否密集扎堆(比如同秒或相邻秒) - 别用
'SYSDATE + 1/24'这种粗粒度间隔——它不保证错开,尤其在高并发JOB提交时容易撞车 - 改用带偏移的表达式,例如
'TRUNC(SYSDATE) + 2/24 + (job_id * 5)/1440'(单位分钟),让每个JOB启动时间相差5分钟 - 已卡住时,先查持有JI的会话:
SELECT sid, program FROM v$session WHERE event = 'enq: JI - contention',再结合dba_jobs_running定位源头
刷新组(REFRESH GROUP)内MV执行顺序错乱引发隐式死锁
用DBMS_REFRESH.MAKE建的组默认按MV创建顺序执行,但若组内MV存在真实依赖(如MV_B查MV_A),而Oracle没识别出该依赖,就会并行刷新,导致MV_B读到MV_A的中间态数据,触发ORA-12008或约束冲突,看起来像死锁。
- 检查组内状态:
SELECT rname, mview_name, broken FROM dba_refresh_children WHERE rname = 'YOUR_GROUP_NAME',broken = 'YES'往往是顺序错乱信号 -
DBMS_REFRESH.MAKE本身不声明依赖,必须补调DBMS_REFRESH.ADD显式加入后置MV,例如DBMS_REFRESH.ADD(rname => 'GRP_A', list => 'MV_B', lax => TRUE) - 确保MV_B的定义中
FROM MV_A能被优化器识别为可重写对象——即MV_A需启用ENABLE QUERY REWRITE - 更稳的做法是拆组,用
DBMS_JOB链控制:JOB1刷MV_A → JOB1结尾调用DBMS_JOB.SUBMIT启动JOB2
DBMS_JOB作业因权限缺失失败却被误判为死锁
手动刷新成功但自动JOB失败,90%以上是后台进程jnnn权限不足——它不继承角色权限,只认显式授予的直接权限。现象是JOB反复失败、broken = TRUE、next_date = 4000-01-01,v$session里却看不到明显等待事件,容易当成死锁处理。
- 查JOB状态:
SELECT job, broken, failures, last_date, what FROM user_jobs WHERE what LIKE '%MV_NAME%',确认broken = 'Y'且failures > 0 - 必须显式授予
ALTER ANY MATERIALIZED VIEW:GRANT ALTER ANY MATERIALIZED VIEW TO your_user——靠CONNECT或自定义角色无效 - 别漏掉基表和日志表的
SELECT权限:如果MV跨用户,要对所有涉及的基表(如SCHEMA_B.TABLE_X)和对应MLOG$_TABLE_X表单独GRANT SELECT - 确认
job_queue_processes > 0且数据库不在RESTRICTED SESSION状态
atomic_refresh=TRUE导致长事务卡死,看起来像死锁
默认atomic_refresh => TRUE会让整个刷新包在一个事务里完成:DELETE旧数据 → INSERT新数据 → COMMIT。百万级以上数据下,这过程极易撑爆UNDO、触发ORA-01555,或因TX锁阻塞其他DML而反向拖慢自己,v$session显示ACTIVE但SQL_ID为空,容易误判为死锁。
- 手动刷新时必须显式传参:
DBMS_MVIEW.REFRESH('MV_NAME', atomic_refresh => FALSE),改用TRUNCATE+INSERT模式 - 定时任务中不能依赖默认值——封装过程里必须硬编码
atomic_refresh => FALSE - 副作用是刷新过程中MV短暂为空,应用层需容忍;若不能容忍,得用分区交换方案,而非强行保“始终有数据”
- 配合
PARALLEL加速前,先确认日志表支持并行FAST:SELECT ROWIDS, PRIMARY_KEY, SEQUENCE FROM USER_MVIEW_LOGS WHERE MASTER = 'YOUR_TABLE'三项都需为YES
真正卡住的作业往往不是单一原因,而是调度重叠、权限缺失、事务模式、依赖顺序四者叠加。排查时别一上来就KILL SESSION,先看v$session.event和dba_jobs.broken状态,再决定是调时间、补权限、改参数,还是重构刷新逻辑。


















