物化视图complete刷新io高是因全量扫描+直写磁盘、临时表空间争用及默认事务性刷新导致;应通过合理设置parallelism、atomic_refresh=false、隔离临时表空间来精准控压。
物化视图刷新本身就会触发大量物理读、排序、日志写入,io高不是异常,而是默认行为没控住——关键在dbms_mview.refresh调用时是否显式约束资源消耗,而不是等它跑满磁盘或撑爆临时表空间。
为什么COMPLETE刷新IO特别高
COMPLETE刷新会重建整个物化视图:先SELECT全量基表(可能跨上百个分区),再INSERT /*+ APPEND */进新段,全程绕过缓冲区直写磁盘。若基表有10TB数据,哪怕只刷一次,IO峰值就可能打穿存储带宽。
- 临时表空间暴涨:排序、哈希连接、物化中间结果全挤在
TEMP里,尤其当_sort_multiblock_read_count偏大或统计信息陈旧时 - redo/undo激增:
ATOMIC_REFRESH => TRUE(默认)强制走事务性truncate+insert,每行都记undo - 没设
parallelism参数时,Oracle可能启用隐式并行(由PARALLEL_DEGREE_POLICY控制),反而让IO更分散难控
如何用parallelism精准压住IO峰值
parallelism不是越大越好,它直接决定并发扫描进程数和临时段分配量。盲目设成32,可能让IO从“高”变成“不可用”。
- 先查当前系统并行能力:
SELECT VALUE FROM V$PARAMETER WHERE NAME = 'parallel_max_servers',设的parallelism不能超过这个值 - 对IO敏感场景,建议从
2或4起步测试,比如:DBMS_MVIEW.REFRESH('MV_SALES', method => 'C', parallelism => 4) - 搭配
ATOMIC_REFRESH => FALSE使用:此时并行度只作用于INSERT /*+ APPEND */阶段,跳过truncate锁表和undo生成,IO压力下降30%~50% - 避免在OLTP时段用高并行——它会抢占buffer cache、争抢db file scattered read等待事件
用refresh_after_errors + rollback_seg规避IO雪崩
刷新中途失败(比如磁盘满、ORA-1652)后,默认会回滚整个事务,undo段反复写、临时段不释放,IO持续高位不降。
- 加
refresh_after_errors => TRUE:出错后跳过失败MV,继续刷列表里剩下的,防止单点卡死拖累全局 - 显式指定
rollback_seg => NULL(Oracle 10g+已废弃回滚段,但留着能避免兼容层误判) - 更彻底的做法是拆分
list参数:把大MV按业务域分组,比如list => 'MV_SALES_DAILY,MV_SALES_WEEKLY'改成两次调用,每次只刷一个,IO可预测
临时表空间IO过载的即时止损法
当V$SESSION_LONGOPS显示刷新会话卡在Sort/Hash-File Creation,且dba_temp_files使用率超90%,说明临时表空间已成瓶颈。
- 立刻查谁在吃临时空间:
SELECT s.sid, s.username, t.blocks * 8192 / 1024 / 1024 mb FROM v$sort_usage t, v$session s WHERE t.session_addr = s.saddr - 别急着
resize临时文件——先杀掉非核心会话,腾出空间给刷新事务完成 - 长期方案:为物化视图刷新专用创建独立临时表空间,绑定到资源消费者组:
CREATE TEMPORARY TABLESPACE mv_temp TEMPFILE '/u01/oradata/mv_temp01.dbf' SIZE 2G;,再在DBMS_SCHEDULER作业里指定job_class关联该表空间
真正难控的从来不是单次IO峰值,而是多个刷新作业共享同一临时表空间+同一资源组+未设并行上限——它们会相互放大IO毛刺。把parallelism、ATOMIC_REFRESH、临时表空间隔离这三件事做实,比调优SQL语句对IO的影响大得多。











