Oracle分区表UPDATE需同时满足四个条件才启用并行DML:会话级启用ENABLE_PARALLEL_DML、SQL中显式添加PARALLEL提示、WHERE条件实现精准分区裁剪、避免绑定变量导致裁剪失效。
Oracle 分区表更新时 ENABLE_PARALLEL_DML 不生效?
并行 dml 默认是关闭的,哪怕表是分区的、会话开了 alter session enable parallel dml,执行 update 仍走串行。根本原因是:oracle 要求显式指定并行度(或依赖优化器自动选择),且语句本身需满足并行触发条件——比如必须带分区裁剪(where 条件能精准定位到具体分区),否则优化器直接忽略并行提示。
常见错误现象:UPDATE t PARTITION (p2024) SET ... 看似指定了分区,但没加 /*+ PARALLEL(t, 4) */ 提示,或漏了 ENABLE_PARALLEL_DML;更隐蔽的是,用了绑定变量导致分区裁剪失败,优化器无法确认操作范围,自动降级为串行。
- 必须在会话级先执行
ALTER SESSION ENABLE PARALLEL_DML,仅ALTER SESSION ENABLE PARALLEL QUERY不够 -
UPDATE语句里要带/*+ PARALLEL(t, N) */提示,N 建议设为分区数 × CPU 核心数 / 2,避免过度争用 - WHERE 条件必须能精确落到单个或多个已知分区,例如
WHERE dt >= DATE '2024-01-01' AND dt (前提是 dt 是分区键且是范围分区) - 避免在 WHERE 中使用函数(如
TO_CHAR(dt, 'YYYY')),否则分区裁剪失效,整个表被扫描,无法并行
如何让不同分区真正“同时”被更新?
Oracle 并行 DML 的“同时”不是靠人工拆成多个 UPDATE 语句去跑,而是由一个语句触发并行服务器进程(PX),每个进程负责一部分分区数据。关键在于:分区必须可独立处理,不能有跨分区约束或唯一索引冲突。
使用场景典型如按月分区的日志表,批量修正某几个月的数据;若表上有全局唯一索引,并行更新可能因键冲突报 ORA-12838: cannot read/modify an object after modifying it in parallel。
- 优先使用本地索引(
LOCAL),避免全局索引带来的并行限制 - 如果必须用全局索引,更新前临时禁用(
ALTER INDEX ... UNUSABLE),更新完重建,否则并行会退化 - 不要手动写多个
UPDATE ... PARTITION (p1)+UPDATE ... PARTITION (p2),这本质是串行提交,还增加事务开销 - 检查执行计划,确认
UPDATE操作节点下有PX SEND和PX RECEIVE,才是真并行
PARALLEL 提示不起作用的三个硬性条件
即使语法全对、会话已启用,以下任一条件不满足,Oracle 就强制走串行路径,且不会报错,只默默忽略提示。
- 目标表没有
PARALLEL属性(SELECT degree FROM user_tables WHERE table_name = 'T'返回 1 或空),需提前执行ALTER TABLE t PARALLEL 4 - 语句中引用了非并行函数(如
UTL_HTTP.REQUEST、自定义 PL/SQL 函数未声明DETERMINISTIC),整个语句被标记为不可并行 - 事务中已有其他 DML 操作(尤其是未提交的 INSERT/UPDATE),并行 DML 要求“干净”的事务上下文
分区更新后为什么 DBA_TAB_STATISTICS 显示 STALE?
并行 DML 修改大量数据后,统计信息不会自动刷新,且 Oracle 会把该表标记为 STALE = 'YES'。这不是 bug,是机制:并行操作绕过了常规的 DML 统计收集路径。
容易被忽略的是,后续查询可能因旧统计信息走错执行计划,尤其分区裁剪失效后性能断崖下跌。
- 更新完成后立即执行
DBMS_STATS.GATHER_TABLE_STATS,注意加granularity => 'PARTITION'参数,只扫变动分区 - 别依赖
ESTIMATE_PERCENT => DBMS_STATS.AUTO_SAMPLE_SIZE在大分区上耗时过长,可指定小比例如5 - 如果表有子分区,
granularity必须设为'SUBPARTITION',否则统计信息仍是粗粒度的
并行 DML 的核心不在“怎么写多条语句”,而在让一条语句被 Oracle 正确识别为可并行、可分区裁剪、无锁冲突的合法操作——漏掉任意一个环节,看起来像并行,实际还是一个人在干。










