UPDATE /+ PARALLEL /对非分区表无效,因Oracle并行DML要求目标表必须是分区表,否则优化器静默忽略提示;非分区表缺乏天然数据边界,无法安全划分并行任务与独立提交。

非分区大表想并行更新,DBMS_PARALLEL_EXECUTE 是唯一靠谱的原生方案;直接加 /*+ PARALLEL */ 提示无效,优化器会静默忽略。
为什么 UPDATE /*+ PARALLEL */ 对非分区表完全没用
Oracle 的并行 DML 要求目标表必须是分区表,否则优化器根本不会启动并行执行计划——v$px_session 查不到进程,EXPLAIN PLAN 里也看不到 UPDATE (PARALLEL) 操作符。这不是 bug,是设计限制:非分区表没有天然的数据边界,无法安全地划分并行任务和独立提交。
常见错误现象包括:
- 执行耗时与串行无差别,但不报错
-
SELECT * FROM v$px_session返回空结果集 - 整张表被锁住(而非仅 chunk 范围),阻塞其他会话
CREATE_CHUNKS_BY_ROWID 是最稳的切块方式
按 ROWID 切块不依赖业务字段、不扫描全表统计、执行计划稳定,适合绝大多数场景。它把表按物理存储位置切成连续行段,每个 chunk 对应一段 ROWID 范围。
使用前提与关键参数:
- 表不能是索引组织表(IOT)或临时表,否则报
ORA-29400: data cartridge error CHUNKING BY ROWID NOT SUPPORTED FOR THIS TABLE TYPE -
CHUNK_SIZE建议设为5000–50000行:太小导致调度开销占比高;太大则单 chunk 失败时重试成本高 - 执行前确认
job_queue_processes > 0(常见设为1000),否则任务创建成功但永不执行,卡在"PL/SQL lock timer" - 切块后立刻查
dba_parallel_execute_chunks,确认STATUS = 'ASSIGNED'且CHUNK_ID连续无空缺
RUN_TASK 必须封装成带绑定变量的动态 SQL
RUN_TASK 不是执行任意 PL/SQL 块,而是对每个 chunk 拼接一条独立的动态 SQL,并用绑定变量传入该 chunk 的 START_ROWID 和 END_ROWID。这条 SQL 必须能完成全部逻辑,且不能含 DDL 或 COMMIT/ROLLBACK(提交由框架自动完成)。
典型封装方式:
DECLARE
l_task_name VARCHAR2(30) := 'MY_UPDATE_TASK';
l_sql VARCHAR2(1000);
BEGIN
l_sql := 'UPDATE /*+ ROWID(t) */ my_large_table t ' ||
'SET status = ''PROCESSED'' ' ||
'WHERE t.rowid BETWEEN :start_id AND :end_id';
<p>DBMS_PARALLEL_EXECUTE.RUN_TASK(
task_name => l_task_name,
sql_stmt => l_sql,
language_flag => DBMS_SQL.NATIVE,
parallel_level => 8
);
END;</p>
容易踩的坑:
- 未加
/*+ ROWID(t) */提示,导致优化器走全表扫描而非快速 ROWID 定位 - SQL 中用了未声明
DETERMINISTIC的自定义函数,导致并行降级为串行 - 绑定变量名写错(如写成
:start而非:start_id),运行时报ORA-01008: not all variables bound
权限、事务隔离与失败重试的实际约束
这个包不是“开箱即用”的黑盒工具,它的行为直接受限于底层机制:
- 调用者必须有
CREATE JOB权限,且对目标表有UPDATE权限;若用CREATE_CHUNKS_BY_NUMBER_COL还需对分片列有查询权限 - 每个 chunk 独立提交,所以失败时只回滚当前 chunk,已提交的 chunk 不受影响——这是优势,但也意味着你得自己检查
dba_parallel_execute_chunks.STATUS找出失败项 - chunk 级别锁的是
ROWID范围,不是整表,但若更新涉及大量关联子查询或触发器,仍可能引发意外锁争用 - 并行度
parallel_level实际受cpu_count和parallel_max_servers限制,设太高反而触发排队等待
真正容易被忽略的是:chunk 切分后,ROWID 分布可能不均——尤其当表刚经历大量 DELETE 后未 SHRINK SPACE,空块残留会导致某些 chunk 实际行数极少,拖慢整体进度。上线前最好先用 ANALYZE TABLE ... LIST CHAINED ROWS 或 DBA_TAB_PARTITIONS 辅助评估数据物理分布。











