直接全表更新百万级数据会因全表扫描、行锁争用、undo/redo压力导致卡死甚至OOM;必须分批,优先用主键或时间字段分段,ROWID仅为兜底方案。
为什么直接用 WHERE 条件更新百万级表会卡死?
因为 oracle 在没有合适索引或条件太宽时,会走全表扫描 + 逐行锁(row-level lock),导致大量 enq: tx - row lock contention 等待,同时 undo 和 redo 压力陡增。更糟的是,如果事务没提交,整个更新过程可能被阻塞甚至 oom。
关键不是“能不能做”,而是“要不要让这一条 UPDATE 语句扛下全部压力”。答案是否定的——必须分批。
ROWID 分批的核心逻辑和安全前提
ROWID 是 Oracle 表中每行物理位置的唯一标识,查询极快、不依赖索引、无业务语义,天生适合分片。但注意:它只在表未发生 ALTER TABLE MOVE、SHRINK SPACE 或分区 DDL 后才稳定;且不能跨分区表的多个段直接比较大小。
实操建议:
- 先确认目标表是否为普通堆表(非 IOT / 全局临时表):
SELECT segment_type FROM dba_segments WHERE segment_name = 'YOUR_TABLE' - 用
MIN(ROWID)和MAX(ROWID)获取边界值,避免用ROWNUM或OFFSET(它们不保证物理连续性) - 每次取固定数量(如 5000–10000 行),别贪大——太大仍会触发大量 undo 和日志写入
- 必须显式加
FOR UPDATE SKIP LOCKED(如果并发更新同一张表),否则可能重复处理或死锁
一个可落地的分批更新脚本模板(PL/SQL)
以下不是玩具示例,而是生产环境常用结构,已规避常见陷阱:
DECLARE
v_low_rid ROWID;
v_high_rid ROWID;
v_batch_size NUMBER := 5000;
BEGIN
-- 一次性获取全表 ROWID 范围(快!)
SELECT MIN(ROWID), MAX(ROWID) INTO v_low_rid, v_high_rid
FROM your_table WHERE your_condition;
<p>WHILE v_low_rid <= v_high_rid LOOP
UPDATE your_table
SET col1 = 'new_val'
WHERE ROWID BETWEEN v_low_rid AND (
SELECT MIN(rid) FROM (
SELECT ROWID rid FROM your_table
WHERE ROWID > v_low_rid
ORDER BY ROWID
FETCH NEXT (v_batch_size - 1) ROWS ONLY
)
)
AND your_condition; -- 再次过滤,防止越界误改</p><pre class='brush:php;toolbar:false;'>COMMIT; -- 每批独立事务,释放锁和 undo
-- 推进下一批起点:查出当前批次最高 ROWID 的下一个
SELECT MIN(ROWID) INTO v_low_rid
FROM your_table
WHERE ROWID > v_low_rid + v_batch_size - 1
AND your_condition;END LOOP; END; /
注意:ROWID + N 不合法,所以上面用了嵌套子查询找“第 N 个 ROWID”。更稳妥的做法是用游标 + BULK COLLECT LIMIT 配合 FORALL,但那需要额外内存缓冲,不适合超大数据集。
比 ROWID 更稳的替代方案:主键分段 + ROWNUM
如果你的表有单调递增主键(如 ID),且数据分布相对均匀,用主键分段反而更可控、可预测、易中断续跑:
例如:
UPDATE your_table SET col1 = 'x' WHERE id BETWEEN 1000001 AND 1005000 AND your_condition;
优势明显:
- 主键范围扫描(Index Range Scan)比 ROWID 扫描更容易被 CBO 优化
- 可提前估算总批次数:
CEIL((MAX(ID)-MIN(ID))/batch_size) - 支持按时间字段(如
CREATE_TIME)分月/周切片,便于归档协同 - 遇到失败可精确重跑某 ID 段,不用再算 ROWID 边界
ROWID 是兜底手段,不是首选。真正要警惕的,是把分批逻辑写死在应用层却忽略数据库端锁升级行为——比如 JDBC 默认 auto-commit 关闭,一条 batch update 没 commit,照样锁全表。


















