update比insert产生更多redo,因其本质是“先删后插”,需同时记录新数据变更、undo回滚信息(undo变更本身也生成redo),且每个受影响索引条目均单独写入redo。

Update 为什么比 Insert 产生更多 Redo
Oracle 的 UPDATE 操作本质是“先删后插”——它不仅要记录新数据块的变更,还要记录旧数据在 undo 段中的回滚信息(而 undo 变更本身也强制生成 Redo)。再加上索引键值变更时,每个受影响的索引条目都要单独写入 Redo(哪怕只改一个字段,只要该字段在任一索引中,就触发索引页修改),所以 UPDATE 的 Redo 量天然高于同等行数的 INSERT。
常见错误现象:log file switch (checkpoint incomplete) 等待事件飙升、归档空间快速耗尽、AWR 报告中 Redo size per transaction 超过 1MB/事务。
- 索引越多,
UPDATE触发的索引维护 Redo 就越分散、越不可控 - 单行
UPDATE配合AUTOCOMMIT(如 JDBC 默认)会把 Redo 分散到多个小事务中,无法合并写入,实际 Redo 总量反而更高 - 使用
SELECT FOR UPDATE后再UPDATE,会额外增加锁相关 Redo 开销
删减非必要索引能直接降低 Redo 吗
能,但必须确认索引是否真被用于查询。Oracle 不会对“未被使用的索引”自动禁用 Redo 记录——只要索引存在且字段被更新,索引叶节点和根节点的变更就会进 Redo。
实操建议:
- 先用
DBA_INDEX_USAGE或V$OBJECT_USAGE查看索引实际使用频次(注意:需开启监控后运行足够时间) - 对长期
USED = NO的索引,用DROP INDEX删除,而非UNUSABLE(后者仍参与 Redo 生成) - 复合索引中仅部分字段被更新时,Redo 仍覆盖整个索引条目;若业务只查前导列,可考虑拆分为单列索引+选择性重建
- 唯一约束依赖的索引不能删,但可评估是否真需要“唯一”语义——有时应用层校验 + 普通索引更轻量
批量提交(Group Commit)怎么设才有效
关键不是“一次 commit 多少行”,而是让 Redo 写入尽可能合并到同一日志组内。Oracle 的 Redo 写入是以 buffer 为单位刷盘的,频繁小事务会让 Redo buffer 频繁 flush,而大事务则可能复用同一批 buffer 块。
参数与操作要点:
-
COMMIT前累积 1000~5000 行更新较合理(具体取决于单行 Redo 量,可通过 AWR 中Redo size per transaction反推) - 避免在循环内写
COMMIT,改用显式计数器控制提交节奏,例如:IF MOD(i, 2000) = 0 THEN COMMIT; END IF; - 禁用
IMMEDIATE模式(如 PL/SQL 中COMMIT WRITE IMMEDIATE),改用默认COMMIT WRITE WAIT BATCH,允许 Redo 合并 - 确认数据库未启用
FAST_START_MTTR_TARGET过小(如
临时表为什么 Redo 更少
全局临时表(GLOBAL TEMPORARY TABLE)的数据只对当前会话可见,且事务结束后自动清空,因此 Oracle 对其 DML 允许绕过部分 Redo 记录逻辑——尤其是不记录 undo 段变更的完整镜像,也不触发索引的 full key 重写。
但要注意:
- 临时表上的索引仍会产生 Redo,只是量级远低于普通表;如果业务允许,可考虑临时表 + 无索引设计,最后再 merge 到主表
-
ON COMMIT DELETE ROWS比ON COMMIT PRESERVE ROWS更省 Redo,因后者需保留跨事务状态 - 临时表不能替代分区裁剪等架构优化;若更新集中在某几个分区,优先考虑按更新频率做分区,比换表更治本
真正容易被忽略的是:Redo 量暴增往往不是单一原因,而是“索引冗余 + 小事务 + 高频 SELECT FOR UPDATE”三者叠加。排查时别只盯 SQL 写法,先看 V$SEGSTAT 里哪些段的 logical reads 和 db block changes 异常偏高——那才是 Redo 的真实源头。











