最近碰到一个挺典型的案例,记录一下排查过程。
现象
归档日志突然暴涨。导致归档目录满。平时每天 600M 到 800M,某天开始连续多天冲到 11G、15G 以上,而且没有回落迹象。
这种情况一般就两个方向:写入量突然变大,或者有什么任务在持续制造 redo。
第一步:查 TOP SQL
先拉了一下高消耗 SQL:
SELECT *
FROM (
SELECT sql_id, executions, buffer_gets, disk_reads
FROM v$sql
ORDER BY buffer_gets DESC
)
WHERE ROWNUM <= 10;
确实看到一条显眼的 UPDATE:
UPDATE MZYS_BRCF CF
SET CF.OPERATIONTIME = TO_CHAR(SYSDATE,'YYYY-MM-DD HH24:MI:SS')
WHERE ...
执行次数高、扫描量大、带 EXISTS 子查询——第一眼很像问题 SQL。但冷静想想,这类操作很常见,不至于把归档直接打到 15G+,方向应该不在这。
关键线索:时间线对不上
翻了一下变更记录:3 月 14 日新增了用户 TXCJX,归档暴涨从 3 月 20 日才开始,中间差了将近一周。
这个延迟很有意思。大概率是新功能上线之后,数据逐渐积累,到一定量级才开始放量——不是瞬间触发的,而是"养大了才炸"。
顺着这个思路,去查 JOB。
找到真正的原因
顺着这个思路,先查了一下 shared pool 里和这张表相关的 SQL:
SELECT sql_id, sql_text
FROM v$sql
WHERE sql_text LIKE '%TXCRMYY_JX_DET%';
结果很说明问题。sql_id = dd0kvh81az21j 的 INSERT 和 sql_id = 118fz5vvdb9j8 的 DELETE 各出现了八九次,说明这两条 SQL 在短时间内被反复执行。同时还能看到 JOB 的调度入口:
-- 118fz5vvdb9j8(反复出现)
delete from "TXCJX"."MVIEW_TXCRMYY_JX_DET"
-- dd0kvh81az21j(反复出现)
INSERT /*+ BYPASS_RECURSIVE_CHECK */ INTO "TXCJX"."MVIEW_TXCRMYY_JX_DET"
select ... from view_TXCRMYY_JX_DET@db_qazhis
-- JOB 调度入口
dbms_refresh.refresh('"TXCJX"."MVIEW_TXCRMYY_JX_DET"');
这已经是实锤了,不是推断。接着去查 JOB 确认细节:
SELECT schema_user, what
FROM dba_jobs
WHERE schema_user = 'TXCJX';
结果是:
dbms_refresh.refresh('"TXCJX"."MVIEW_TXCRMYY_JX_DET"');
物化视图刷新任务。继续往下看:
SELECT mview_name, refresh_method
FROM dba_mviews
WHERE owner = 'TXCJX';
REFRESH_METHOD = COMPLETE。
再看调度频率:SYSDATE + 10/24/60,每 10 分钟一次。
然后看实际执行的 SQL:
INSERT INTO TXCJX.MVIEW_TXCRMYY_JX_DET
SELECT ... FROM view_TXCRMYY_JX_DET@db_qazhis
通过 DBLINK 从远程库拉数据。
问题就很清楚了
把这几件事合在一起看:
COMPLETE 刷新的本质不是"更新",是先 DELETE 整张表,再重新 INSERT 一遍。物化视图大约 18 万行,每 10 分钟执行一次,一天下来是 144 次。
粗算一下:18 万行 × 144 次 ≈ 2600 万行写入,每次还是 DELETE + INSERT 的组合,redo 翻倍。
600M 涨到 15G,完全合理,甚至算是"正常结果"。
为什么这个设计容易踩坑
这个方案把三个风险叠在了一起:
COMPLETE 刷新本身代价就高,每次都是全量重建,不是增量更新。DBLINK 的场景通常也做不了增量,因为远程库的数据结构不一定有时间戳或版本字段可以用。再加上 10 分钟这个频率,一天 144 次,任何单次的代价都会被无限放大。
这三件事单独拎出来可能都不是大问题,但叠在一起,redo 放大几乎是必然的结果。
怎么处理
第一步先降频,把 10 分钟改成 1 小时或者按天跑,归档立刻能降下来,用于止血。
根本上要改刷新方式。数据量大的情况下不应该用 COMPLETE。理论上可以通过 DBLINK 带 WHERE 条件做增量,但这里的数据源是远程库上的一张视图 view_GLZRMYY_JX_DET@db_qazhis,视图本身不一定有时间戳,底层基表的字段能不能透传出来也不确定。跨库的情况下,触发器、LogMiner 这些手段也都用不上。所以增量能不能做,得先确认远程视图里有没有可靠的增量字段:
WHERE JFSJ > last_sync_time -- 前提是这个字段可信且有索引
如果字段不存在或者不可信,增量就做不了,降频比强行改增量更稳,根据业务对数据实时性的实际要求来定,从 10 分钟改成每小时甚至每天,redo 会成比例下降。
尽量避免 DELETE 全表,如果还是要全量刷,可以改用 MERGE,只更新有差异的行,减少不必要的 redo 生成。
小结
这次归档暴涨,本质不是数据库出了什么问题,而是数据同步方式选错了。全量刷新 + DBLINK + 高频执行,三者叠加,redo 就一定会炸。
遇到类似情况,排查顺序基本是:有没有大批量写入、有没有定时任务、有没有"全量操作"。不要上来就怀疑数据库本身,先把写入行为摸清楚。




