物化视图停摆:processes与job_queue_processes
2026/9/18 12:24:56 网站建设 项目流程

做数据库运维的人多少都被这几个词绕晕过:processes、job_queue_processes 和物化视图。它们单看都是老熟人,可一旦串在一起出问题,排查路径就变得相当绕。前阵子又碰到一个现场,说好的定时刷新物化视图,数据却停在三天前不动了,告警日志里刷出一片 ORA-12012,值班的哥们第一反应是刷新脚本写错了,折腾了大半天才发现根子其实在参数上。这事儿挺典型,processes 是实例能承载的进程总数,job_queue_processes 决定有多少"工人"去跑作业,而物化视图的自动刷新恰恰就是把活派给作业队列去干的。三者是一条链上蚂蚱,任何一环收紧,最后都会让物化视图数据变陈旧。这篇就按我自己的排查习惯,把这条链从头到尾拆一遍,给做运维、数据仓库、报表开发的朋友一份能直接抄作业的参考。

1. 三个参数为什么总被绑在一起

1.1 一个典型的物化视图停摆现场

场景大概是这样的:某业务库有一批物化视图,配置成每天早上六点自动刷新,给下游报表用。某天开始,报表数据连续几天没更新。登进去看 DBA_MVIEWS,LAST_REFRESH_DATE 停在某个时间点再也不往前走了,STALENESS 那一列显示成 STALE 甚至 UNUSABLE。第一反应是刷新脚本有问题,去翻 DBA_JOBS 和 DBA_SCHEDULER_JOBS,发现作业本身好好的,NEXT_DATE 一直在往后滚,可 LAST_DATE 就是不动,FAILURES 也未必往上涨。

这时候去看告警日志和作业运行明细,往往能看到一类熟悉的味道:ORA-12012,后面跟着一句 error on auto execute of job。意思是"系统想自动执行这个作业,但没执行成功"。再往深挖,可能是作业根本没被调度起来,也可能是调度起来之后执行中途被打断。前者通常指向 job_queue_processes 太小或者干脆是 0,后者常常指向 processes 这类实例级上限被顶满,导致作业进程起不来。

我见过最隐蔽的一种情况,是 job_queue_processes 设得挺大,processes 也看着够用,但库上跑了一大堆并行查询,PX 进程把 processes 的额度吃光了,物化视图刷新的作业排到队尾迟迟没进程可用。表面上参数都对,实际就是没资源。所以这三个东西不能分开看,必须放进同一张资源账本里算。

1.2 把依赖链条拆开看

很多人对"物化视图自动刷新"有个误解,以为数据库内部有个专门的管家定时去刷。实际不是。Oracle 里物化视图的自动刷新,本质上是把一个刷新动作包装成一个数据库作业,交给作业调度系统去执行。链条大致是这样的:

  • 第一层是 processes。它定义整个数据库实例最多能同时存在多少个操作系统进程,包括后台进程、服务器进程、并行执行进程,以及作业进程。
  • 第二层是 job_queue_processes。它决定实例里同时能有多少个作业执行进程(老版本是 Jnnn,12.2 之后底层换成作业从属进程)。作业调度系统拿到待执行的作业,就得从这个池子里分配进程去干活。
  • 第三层才是物化视图。刷新动作被注册成作业,排队等着被作业进程认领执行。

用一句话概括:processes 是工厂的总用电额度,job_queue_processes 是这条产线上的工人编制,物化视图刷新是从产线上走下来的产品。总用电不够,工人再多也开不了机;工人编制为零,产品永远出不来;工人够用但被别的产线抢了电,也得排队。

把这条链条记住,后面排查就有方向感了:物化视图数据不动,先看作业有没有在跑,再看作业主力(job_queue_processes)够不够、开没开,最后看整机资源(processes)有没有被吃满。三步走下来,八九不离十。

提示:物化视图的"自动"刷新依赖数据库作业系统,这一点是理解所有相关问题的钥匙。凡是作业系统被限制或资源不足,物化视图的"自动"就会变成"不动"。

2. processes 参数:实例进程额度的总闸

2.1 它到底在管什么

PROCESSES 是实例级静态参数,定义的是这个 Oracle 实例允许同时存在的操作系统进程上限。注意这里的操作对象是"进程",不是"会话"。一个会话通常对应一个服务器进程,但进程的消耗远不止会话那一份。具体来说,下面这些都要从 PROCESSES 的额度里扣:

  • 后台进程。比如 PMON、SMON、DBWn、LGWR、CKPT、ARCn、MMON、MMNL 等等,一个正常的单实例库,后台进程加起来十几到几十个不等,RAC 环境更多。
  • 服务器进程。每个用户连接对应一个服务器进程,这是消耗的大头。
  • 并行执行进程。PX 进程,跑并行查询、并行 DML、并行建索引时大量产生。
  • 作业进程。这就是 job_queue_processes 对应的那部分。

很多人以为 PROCESSES 只影响连接数,其实并行查询和作业进程都算在里面。这就是为什么"连接数没超,怎么还报进程不够"的情况经常发生——额度被 PX 或者作业进程悄悄吃掉了。理解这一点,是算准 PROCESSES 的前提。

2.2 默认值与容量推算

不同版本 PROCESSES 的默认值不太一样,SESSIONS 默认值又是根据 PROCESSES 推导出来的,公式是 SESSIONS = (1.1 × PROCESSES) + 5。

| 版本 | 默认 PROCESSES | 推导默认 SESSIONS | | Oracle 9i / 10g / 11g | 150 | 170 | | Oracle 12c / 18c / 19c | 300 | 335 |

默认值对小型库够用,但一旦上连接池、跑并行、还有一堆物化视图作业,150 这种默认值很快就见底。我在估算时习惯用下面这个思路,而不是拍脑袋:

需要设置的 PROCESSES ≥ 后台进程数 + 峰值并发服务器进程数 + 峰值并行进程数 + 作业进程数 + 预留缓冲。

举个例子,某报表库后台进程约 40 个,业务峰值连接 300,偶尔跑并行取数,峰值并行进程约 60,物化视图作业进程配置 job_queue_processes=20。那么 40 + 300 + 60 + 20 = 420,再加 10% 到 20% 缓冲,PROCESSES 至少按 500 来配,而不是贴着 420 配。为什么不贴着配?因为进程申请是动态的,几个大查询同时并发,峰值会瞬间冲高,贴着配必翻车。

注意:PROCESSES 是静态参数,改完要重启实例才生效。这一点跟后面要讲的 job_queue_processes 完全相反,一个要重启,一个能在线改,别搞混。

2.3 改大之后不能只看数据库

调大 PROCESSES 只解决了数据库这一头,操作系统那一头没跟上一样白搭。改完之后至少要同步检查几件事:

  • 操作系统对 oracle 用户的进程数限制。如果用的是系统级 ulimit,nproc 或 max user processes 这道坎没过,数据库说能开 500 个进程,系统只让开 300,照样报错。
  • 内存。每个服务器进程都要占 PGA,进程数上去,PGA 总占用会跟着涨,要确认没有触及物理内存红线。
  • 如果是容器或虚拟化环境,还要确认宿主机层面的进程上限。

我踩过一次坑,就是把 PROCESSES 从 300 调到 800,重启后看着一切正常,结果业务高峰期反而更容易报进程相关的错误。排查发现是操作系统的用户级进程数没放开,等于数据库和系统在互相打架。所以每次调这个参数,我都写成固定动作:数据库参数、操作系统限制、内存余量,三样一起过一遍。

3. job_queue_processes:作业调度的工人编制

3.1 作业队列进程是干什么的

JOB_QUEUE_PROCESSES 控制实例中同时可以有多少个作业执行进程。所有通过 DBMS_JOB(旧接口)和 DBMS_SCHEDULER(新接口)提交的作业,最终都要靠这些进程去执行。物化视图的自动刷新、统计信息的定时收集、各种自定义的定时批处理,全都在这个池子里排队。

这个参数最要命的一点,是它允许设成 0。一旦设成 0,作业调度基本等于停摆,所有依赖作业系统的自动化动作全部卡住。物化视图不会自动刷新,定时统计信息不会收集,自定义批处理作业也不会执行。偏偏这个参数在有些环境里会被"顺手"调成 0,比如为了临时降低系统负载,或者某些模板化的初始化脚本里就写死了 0,事后没人改回来。我处理过的物化视图停摆问题里,有小一半都能追到这一条。

3.2 12c 前后这个参数的变化

job_queue_processes 的默认值和底层实现,在 12c 前后有明显差异,这点值得单独拿出来说。

| 版本区间 | 默认值 | 取值范围 | 底层实现说明 | | 9i / 10g / 11g | 10 | 0 - 1000 | 作业队列进程 Jnnn,直接受该参数控制 | | 12.1 | 1000 | 0 - 1000 | 仍以作业队列进程为主 | | 12.2 及以后 | 1000 | 0 - 1000(兼容保留) | 底层切换为作业从属进程,参数主要为兼容旧行为 |

11g 时代默认只有 10 个作业进程,如果库上挂了几十个物化视图刷新作业,加上统计信息收集,作业就开始排队。排队的后果不一定报错,而是"该刷的没按时刷",从表面看就是数据陈旧。到了 12c,默认值抬到 1000,宽松了很多,但 12.2 之后底层换成了作业从属进程,进程命名也从 Jnnn 变成了另一套体系,排查时看 V$PROCESS 里的进程名会和老版本对不上,这点初次遇到容易懵。

需要强调的是,即便在 12.2 之后,JOB_QUEUE_PROCESSES 设成 0 仍然会阻断作业执行。所以"新版不用管这个参数"是错的,它依然是个开关。

3.3 这个值到底设多少合适

给一个我常用的判断逻辑:

  • 绝对不能是 0。生产库上把这个参数设成 0,等于主动关掉所有自动化作业,除非有明确的临时目的并记得改回来。
  • 先数作业量。用查询统计一下当前库上活跃的作业数量,尤其是物化视图刷新、统计信息收集这类固定消耗。
  • 按"并发作业峰值"来配,而不是按"作业总数"来配。多数作业是错峰执行的,不需要每个作业配一个进程。
  • 留出冗余。一般我会按"刷新高峰时段内可能同时触发的作业数 × 1.5"来估。

| 场景 | 建议 job_queue_processes | | 小型库,作业很少 | 保持默认或 10 - 20 | | 有中量物化视图定时刷新 | 20 - 50 | | 大量物化视图、多个刷新组集中刷新 | 50 - 100 甚至更高 | | 明确要临时停掉所有作业 | 0(必须记录并尽快恢复) |

这个参数是动态的,可以在线调整,改完立即生效,不用重启:

-- 查看当前值 SHOW PARAMETER job_queue_processes; -- 在线调整,立即生效并写入参数文件 ALTER SYSTEM SET job_queue_processes = 50 SCOPE = BOTH;

提示:调大这个参数时,别忘了同步核对 PROCESSES。作业进程也占 PROCESSES 额度,如果 JOB_QUEUE_PROCESSES 从 10 一口气提到 100,而 PROCESSES 没留出这 90 的余量,要么新作业进程起不来,要么挤占服务器进程,两头不讨好。

4. 物化视图刷新机制拆解

4.1 物化视图的"自动"从哪来

先把概念捋清楚。物化视图是把查询结果物理存下来的一张表,比普通视图多的就是"数据落盘"这件事带来的性能收益。代价是数据会过期,得靠刷新机制把它跟基表对齐。刷新有两大触发方式:

  • ON COMMIT。基表一提交,物化视图就跟着更新。这种方式要求基表上建了物化视图日志,而且刷新方式必须是 FAST,否则会直接报错。它的好处是数据几乎实时,坏处是给基表的每一次提交都增加了额外开销。
  • ON DEMAND。不跟着基表走,靠手动或者定时来刷新。定时刷新从使用者角度看是"自动"的,但底层就是把刷新动作注册成一个作业,交给作业调度系统。

关键就在 ON DEMAND 的定时刷新。它不是数据库里有个内建的定时器直接触发,而是通过作业系统排队执行。也就是说,只要作业系统出问题,ON DEMAND 的自动刷新就会停。ON COMMIT 因为走的是提交时的同步动作,一般不依赖作业进程,所以受影响程度小一些,但也不是完全无关,某些场景下维护动作仍会用到调度资源。

4.2 刷新方式的选择与代价

物化视图的刷新方式有 FAST、COMPLETE、FORCE 三种,刷新方法又有 ON COMMIT 和 ON DEMAND,组合起来决定行为。

| 刷新方式 | 含义 | 前提条件 | 代价 | | FAST | 只把基表的变化增量同步过来 | 基表要有物化视图日志 | 低,但依赖日志 | | COMPLETE | 清空后重新算一遍 | 无特殊前提 | 高,尤其是大表 | | FORCE | 优先 FAST,不行就 COMPLETE | 视情况 | 不确定,取决于能否走 FAST |

我个人的习惯是:能 FAST 就 FAST,实在不行再用 COMPLETE,极少用 FORCE。原因是 FORCE 的行为不可预测,平时都走 FAST 很快,某天日志出点什么问题,它偷偷切成 COMPLETE,一个大表全量刷新直接把资源吃满,这种"平时没事、偶发雪崩"最难排查。宁可让 FAST 明确失败报错,也不要让 FORCE 悄悄降级。

创建物化视图时的典型写法:

CREATE MATERIALIZED VIEW mv_sales_agg BUILD IMMEDIATE REFRESH FAST ON DEMAND START WITH SYSDATE NEXT SYSDATE + 1 AS SELECT dept_id, SUM(amount) total_amount FROM sales GROUP BY dept_id;

其中 START WITH 和 NEXT 决定了它按什么节奏自动刷新,这个节奏最终落地成作业。

4.3 刷新组和批量管理

当一个系统里物化视图数量多了之后,逐个管理很累,这时会用到刷新组。DBMS_REFRESH 可以把多个物化视图塞进一个组,统一按一个节奏刷新,这样可以控制并发、减少作业数量。

-- 创建一个刷新组,每小时刷新一次 BEGIN DBMS_REFRESH.MAKE( name => 'refresh_group_1', list => '', next_date => SYSDATE, interval => 'SYSDATE + 1/24' ); END; /

刷新组的好处是把一堆刷新动作合并到少量作业里,减轻作业系统的压力。但要注意,刷新组本身也是作业,一样依赖 job_queue_processes 和 processes。组内物化视图一次性刷新,如果都走 COMPLETE,资源峰值会很高,我一般会错峰,或者给组内刷新加入顺序和分批。

4.4 怎么查看刷新状态和作业

排查这类问题时,下面几条查询基本是标配,建议收进自己的工具箱。

-- 看物化视图的刷新状态和最后刷新时间 SELECT owner, mview_name, refresh_mode, refresh_method, last_refresh_type, last_refresh_date, staleness FROM dba_mviews ORDER BY last_refresh_date; -- 看物化视图的刷新时间明细 SELECT owner, mview_name, last_refresh_date, staleness FROM dba_mview_refresh_times; -- 看旧接口作业的状态 SELECT job, schema_user, last_date, next_date, broken, failures FROM dba_jobs; -- 看新接口调度作业的状态 SELECT owner, job_name, enabled, state, last_start_date, next_run_date FROM dba_scheduler_jobs; -- 看作业最近一次运行的错误信息 SELECT job_name, status, error#, actual_start_date, additional_info FROM dba_scheduler_job_run_details ORDER BY actual_start_date DESC;

物化视图的 STALENESS 列特别有用:FRESH 表示和基表对齐,STALE 表示已经过期,UNUSABLE 表示刷新过程中出了问题、增量刷新已不可用。看到 UNUSABLE,基本意味着 FAST 走不通了,得先解决物化视图日志的问题。

5. 实战:一次物化视图集体停摆的排查

5.1 现象描述

再回到开头那个现场,把它完整走一遍。现象是:一批物化视图刷新时间统一停在一个时间点,往后不再更新;下游报表数据陈旧;作业列表里 NEXT_DATE 一直在滚,但 LAST_DATE 不动;告警日志里能看到 ORA-12012。

这种"集体停摆"比单个物化视图停更有指向性。单个停可能是物化视图自身或日志问题,集体停大概率是公共资源的问题——作业系统、进程额度、或者作业并发被限制。排查要从这个判断出发,先看公共资源。

5.2 排查路径

我一般按这个顺序走:

第一,确认作业系统是不是活着。

SHOW PARAMETER job_queue_processes;

如果返回 0,基本当场破案。不是 0 就继续往下。

第二,看作业进程实际有没有起来。

SELECT name, description, paddr FROM v$bgprocess WHERE name LIKE 'J%' OR name LIKE 'S%'; SELECT COUNT(*) FROM dba_scheduler_running_jobs;

老版本看 Jnnn 进程,新版本作业从属进程可能以别的名字出现。如果参数值非 0,但进程数量远小于参数值,说明进程起不来,要怀疑 processes 额度。

第三,核对 processes 的实际占用。

SHOW PARAMETER processes; SELECT COUNT(*) FROM v$process; SELECT program, COUNT(*) FROM v$process GROUP BY program ORDER BY COUNT(*) DESC;

把 v$process 里的进程按 program 分组,一眼就能看出额度被谁占了。如果看到大量并行进程,就知道是被 PX 吃掉了;如果服务器进程接近上限,就是连接数问题。

第四,看作业的运行明细和错误。

SELECT job_name, status, error#, additional_info FROM dba_scheduler_job_run_details WHERE status = 'FAILED' ORDER BY actual_start_date DESC FETCH FIRST 20 ROWS ONLY;

结合起来看,就能判断是"没被调度起来"还是"调度起来后失败"。

5.3 根因与修复

那次现场的结论是:作业系统参数没设成 0,作业进程也能起来一部分,但 processes 被业务的并行取数查询占满,导致物化视图刷新作业排到队尾,一直抢不到进程,时间一长越堆越多,一部分作业因为长时间没执行而在日志里报出 ORA-12012。

修复动作分三步:短期先把 PROCESSES 调大并同步放开操作系统限制,让堆积的作业能跑起来;中期把业务的并行度收一收,避免少数大查询独占额度;长期把物化视图刷新作业和业务高峰错开,同时给关键刷新作业设置合理的优先级。

修复后观察 DBA_MVIEWS 的 LAST_REFRESH_DATE 不再停滞,报表数据恢复。整个过程最费时间的其实不是修复,而是定位——因为一开始没人把这三个参数串起来想,才会在单个物化视图上反复绕圈。

注意:排查这类问题,最忌讳盯着单个物化视图死磕。只要出现"一批物化视图同时停",就该立刻转向公共资源排查,作业参数、进程额度、并行占用,一项项过。

6. 参数配置与避坑清单

6.1 常见问题速查

把踩过的坑整理成一张表,遇到类似现象可以直接对号。

| 现象 | 可能原因 | 排查方向 | 处理建议 | | 物化视图数据陈旧,LAST_REFRESH_DATE 不动 | job_queue_processes 为 0 | 查参数值 | 改为非 0 并核对 processes | | 一批物化视图同时停摆 | processes 额度被占满 | 查 v$process 分组 | 调大 processes 并收并行 | | 作业 NEXT_DATE 滚动但 LAST_DATE 不动 | 作业一直没被调度执行 | 查作业进程数 | 检查 job_queue_processes 和资源 | | 告警日志出现 ORA-12012 | 作业自动执行失败 | 查作业运行明细 | 按错误号具体分析 | | 报错进程数达到上限 | processes 或系统限制过小 | 查参数和 OS 限制 | 两边同步放开 | | 定时刷新偶发变慢或雪崩 | FORCE 刷新降级为 COMPLETE | 看刷新类型 | 改用明确 FAST 并监控日志 | | 刷新报物化视图日志问题 | 日志缺失或失效 | 查基表 mlog | 重建物化视图日志 |

6.2 我自己总结的几条经验

第一条,processes 一定要留冗余。别贴着计算出来的人数去配,宁可多留 20%。进程申请是动态的,峰值说来就来,贴着配迟早要还。

第二条,动 processes 之前先看操作系统。数据库参数和 OS 限制是两道闸,只开一道等于没开。我见过太多"参数改了没用"的情况,根子都在 OS 那一层。

第三条,job_queue_processes 永远不要设 0 当"临时降载"手段。真要降载,去限制具体作业的执行时间或者频率,别一刀切把整个作业系统关了。这个参数一旦忘了改回来,物化视图、统计信息、所有批处理全停,损失远大于那点临时负载。

第四条,能用 FAST 就别用 FORCE。FORCE 的自动降级行为很隐蔽,平时看不出来,出事就是大事。宁可让 FAST 明确报错,你至少知道有问题。

第五条,物化视图刷新要有错峰意识。把所有物化视图的 NEXT 时间都设成同一个整点,等于人为制造一个资源峰值。按业务重要性错开,或者用刷新组分批刷,压力会平缓很多。

第六条,监控里加上物化视图的 staleness。别等业务来投诉才发现数据陈旧。搞个定时任务,每天扫一遍 DBA_MVIEWS,凡是 STALE 超过预期时间的就发提醒,把问题消灭在投诉之前。

最后再分享一个我常用来快速定位的小技巧:当不确定问题出在作业系统还是单个物化视图时,先手动执行一次刷新看看。

BEGIN DBMS_MVIEW.REFRESH('MV_SALES_AGG', 'C'); END; /

如果手动刷新能成功,说明物化视图本身和基表、日志都没毛病,问题十有八九在自动调度的作业环节;如果手动刷新也报错,那就顺着错误号去查物化视图和基表本身。这一招能帮你快速把问题范围缩小一半,省下大量来回折腾的时间。根据我这些年的实际使用,排查效率提升最明显的往往不是多复杂的工具,而是这种把问题一刀切两半的土办法。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询