别用隐式游标for rec in (select...)做大批量更新,应改用bulk collect+forall;因其每轮循环触发完整sql解析、redo单条写入、上下文切换频繁,10万行可慢5–10倍,并易致ora-04030。
直接结论:别用 for rec in (select ...) 做大批量更新,改用 bulk collect + forall,否则 10 万行更新可能慢 5–10 倍,还容易触发 ora-04030(pga 耗尽)。
为什么隐式游标循环 UPDATE 在 Oracle 19c 里特别慢
常见错误现象:FOR rec IN (SELECT id, name FROM src WHERE status = 'PENDING') 配合循环内 UPDATE target SET ... WHERE id = rec.id,执行时每条 UPDATE 都走完整 SQL 解析 → 执行 → redo 写入 → latch 争用路径。
关键问题在于:
- 每次循环都触发一次上下文切换(PL/SQL ⇄ SQL 引擎),10 万次就是 10 万次切换
- redo 日志按单条记录生成,日志量翻倍,归档压力陡增
- 没加
FOR UPDATE时,若源数据被并发修改,结果不可控;加了又锁表太久 - 异常中断后,已执行的几十条 UPDATE 已提交,无法整体回滚,只能靠幂等条件(如
WHERE status != 'PROCESSED')兜底
FORALL 批量更新必须配对 BULK COLLECT,且 LIMIT 要设对
正确写法不是“把整个结果集一次性读进内存”,而是分批提取 + 分批绑定执行。Oracle 19c 对 FORALL 有隐式分片优化,但前提是单次绑定数据量可控。
实操建议:
-
LIMIT设为500~2000:实测多数 OLTP 场景下这是性能甜点区;设成10000容易 PGA 爆涨、GC 频繁,反而更慢 - 目标表若有大量索引或启用触发器,
FORALL优势会快速衰减——此时不如拆成多个小事务,配合COMMIT WRITE BATCH NOWAIT - 务必关闭
AUTOCOMMIT,否则每次FORALL后自动提交,等于没批量 - 游标
OPEN/FETCH/CLOSE不要在循环里反复调用,应在外层控制
示例片段:
DECLARE
TYPE t_id_tab IS TABLE OF source_table.id%TYPE;
TYPE t_name_tab IS TABLE OF source_table.name%TYPE;
l_ids t_id_tab;
l_names t_name_tab;
CURSOR c_data IS SELECT id, name FROM source_table WHERE status = 'PENDING';
BEGIN
OPEN c_data;
LOOP
FETCH c_data BULK COLLECT INTO l_ids, l_names LIMIT 1000;
EXIT WHEN l_ids.COUNT = 0;
FORALL i IN 1..l_ids.COUNT
UPDATE target_table SET name = l_names(i), updated_at = SYSDATE
WHERE id = l_ids(i);
COMMIT;
END LOOP;
CLOSE c_data;
END;
ORA-06512 错误怎么快速定位和避免
这个错误几乎全是 FORALL 绑定集合长度不一致导致的,比如 id_arr.COUNT ≠ name_arr.COUNT,或者用了 INDICES OF 却漏填某个索引位置的元素。
调试要点:
- 所有参与
FORALL的集合(如id_arr、name_arr)必须用同一段逻辑填充,且COUNT严格相等 - 避免混用
INDICES OF idx_list和VALUES OF val_list,极易错位 - 加一句
DBMS_OUTPUT.PUT_LINE('id:'||id_arr.COUNT||' name:'||name_arr.COUNT);,比看错误堆栈快得多 - 启用
SAVE EXCEPTIONS后,SQL%BULK_EXCEPTIONS(i).ERROR_INDEX指向的是原数组下标(不是内部执行序号),但若用了INDICES OF,它指向的是idx_list的位置,得再套一层映射才能拿到真实id_arr下标
复杂点常被忽略:批量更新 ≠ 无脑加大 LIMIT
很多人以为“越大越快”,把 BULK COLLECT LIMIT 设到 5000 甚至 10000,结果 PGA 内存飙升、临时段写入激增、游标失效频繁。Oracle 19c 的优化是有前提的:SQL 要能走绑定变量重用路径,且单次绑定数据结构稳定。一旦目标表字段类型含 CLOB、BLOB 或触发器逻辑复杂,批量收益会断崖下跌。这时候宁可降 LIMIT 到 500,多跑几轮,也比卡死在 PGA 耗尽强。











