33-Oracle Parallel 并行处理的选择和实践

小伙伴们是不是偶尔会遇见超大的表,几百万、上千万行的数据,查询或是导出各种卡顿,服务器现在都是十核上百核心了,内存也轻轻松松到几十g上百g了。如果使用并行来用好机器的实力,跑起来,快起来。使用并行查询将执行时间大大缩短,在什么场景上可以使用呢,有啥要规避的。

一、Parallel并行

​1. 技术本质​

通过任务分解-并行执行-结果合并流程(QC进程+PX Slaves架构),将单进程任务拆分为多进程并发处理,实现近乎线性的性能扩展

  1. 适用场景

|---------|--------------------|-----------------------|---|
| 类型 | ​举例​ | ​性能提升依据 | |
| OLAP查询​ | 亿级表全表扫描、多表JOIN | 并行扫描使多线程查询耗时降至串行 |
| ​批量DML​ | 千万级数据UPDATE/INSERT | PDML通过并行写入减少事务提交次数 |
| 对象维护​ | 大表在线重定义、索引创建 | 并行建索引速度提升 |
| ​数据迁移​ | Data Pump导出TB级数据 | PARALLEL参数加速RAC集群资源利用 |

规避的场景​:

高频OLTP事务(行级锁冲突)、含触发器依赖操作、资源受限(CPU/I/O 瓶颈)系统开了会更卡。

二、实践脚本

​1. 创建测试表(千万行数据)​​真实的医疗和工业生产,大量的这样的表。
bash 复制代码
ALTER SESSION ENABLE PARALLEL DML;
ALTER SESSION FORCE PARALLEL QUERY PARALLEL 32;
ALTER TABLE target_table NOLOGGING;  -- 减少Redo日志
ALTER INDEX idx_parallel REBUILD PARALLEL 32;  -- 完成后并行重建索引

-- 创建大表(2千万行)19c环境,sql中的connect by功能太香了速度快,
--然而1亿条的时候直接ORA-300009,32C/128G,SGA=64G
-- 启用并行 DML 并设置并行度(推荐 DOP = CPU 核心数 × 0.75)
ALTER SESSION ENABLE PARALLEL DML;
ALTER SESSION FORCE PARALLEL QUERY PARALLEL 24;
ALTER SESSION FORCE PARALLEL DDL PARALLEL 24;

-- 创建表(NOLOGGING + PARALLEL + 直接路径插入)
CREATE TABLE sales_data 
PARALLEL 32 
NOLOGGING 
AS 
SELECT /*+ APPEND PARALLEL(24) */
    ROWNUM AS id,
    TRUNC(DBMS_RANDOM.VALUE(1,1000)) AS product_id,
    TRUNC(SYSDATE - DBMS_RANDOM.VALUE(1,3650)) AS sale_date,
    ROUND(DBMS_RANDOM.VALUE(10,5000), 2) AS amount
FROM DUAL 
CONNECT BY LEVEL <= 20000000;

-- 恢复表属性(避免后续操作继承并行设置)
ALTER TABLE sales_data NOPARALLEL;
ALTER TABLE sales_data LOGGING;
--
----实际运行
[oracle@test19:/home/oracle]# sqlplus / as sysdba
SQL*Plus: Release 19.0.0.0.0 - Production on 星期六 6月 14 14:29:23 2025
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.
连接到:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

SYS@test19> show pdbs;

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDBRS6                         READ WRITE NO
SYS@test19> alter session set container=PDBRS6;
----

创建时候机器负载

2. 场景1聚合统计:并行查询 vs 串行查询
bash 复制代码
--比较执行时间,十几倍的提升
-- 串行查询(全表扫描)
SET TIMING ON
SELECT /*+ NO_PARALLEL */ 
    product_id, 
    SUM(amount) 
FROM sales_data 
GROUP BY product_id; 
-- 执行时间:已用时间:  00: 00: 02.98

-- 并行查询(DOP=8)
ALTER SESSION FORCE PARALLEL QUERY PARALLEL 8;
SELECT /*+ PARALLEL(sales_data, 8) */ 
    product_id, 
    SUM(amount) 
FROM sales_data 
GROUP BY product_id;
-- 执行时间:已用时间:  00: 00: 00.45
3. 场景2批量更新:并行DML vs 串行DML
bash 复制代码
-- 串行更新
UPDATE /*+ NO_PARALLEL */ sales_data 
SET amount = amount * 1.1 
WHERE product_id BETWEEN 100 AND 200; 
--已更新 2022843 行。
--已用时间:  00: 01: 46.64

-- 并行DML(需显式启用)
ALTER SESSION ENABLE PARALLEL DML;

UPDATE /*+ PARALLEL(sales_data, 8) */ sales_data 
SET amount = amount * 1.1 
WHERE product_id BETWEEN 100 AND 200;
COMMIT;  -- 必须提交释放资源
已更新 2022843 行。
已用时间:  00: 00: 17.97
COMMIT
提交完成。
已用时间:  00: 00: 00.11
4. 场景3索引创建:并行DDL
bash 复制代码
-- 串行创建索引
CREATE INDEX idx_serial ON sales_data(product_id);
-- 索引已创建。
--已用时间:  00: 00: 25.31
-- 并行创建索引(DOP=12)
SYS@test19> DROP INDEX idx_serial;
索引已删除。
已用时间:  00: 00: 00.17
CREATE INDEX idx_parallel ON sales_data(product_id) PARALLEL 12;
SYS@test19> CREATE INDEX idx_parallel ON sales_data(product_id) PARALLEL 12;
索引已创建。
-- 已用时间:  00: 00: 02.37 
-- 关闭并行属性
ALTER INDEX idx_parallel NOPARALLEL;
索引已更改。
已用时间:  00: 00: 00.02

三、最佳实践与避坑指南

​1. 并行度计算公式​

DOP=(CPU C​ores×parallel t​hreads p​erc​ pu)×0.75−−预留25%

bash 复制代码
-- 查看系统参数  
SHOW PARAMETER cpu_count;              -- CPU核心数
SYS@test19> SHOW PARAMETER cpu_count;
NAME                                 TYPE                              VALUE
------------------------------------ --------------------------------- ------------------------------
cpu_count                            integer                           32
  
SHOW PARAMETER parallel_threads_per_cpu; --虚拟CPU 
SYS@test19> SHOW PARAMETER parallel_threads_per_cpu
NAME                                 TYPE                              VALUE
------------------------------------ --------------------------------- ------------------------------
parallel_threads_per_cpu             integer                           1
 
2. 对应配置
bash 复制代码
-- 限制全局并行进程数(防资源耗尽)  
ALTER SYSTEM SET parallel_max_servers = 64;  
-- 启用自适应并行(Oracle 11g+)  
ALTER SESSION SET parallel_degree_policy = AUTO;  
SYS@test19> ALTER SYSTEM SET parallel_max_servers = 64;
系统已更改。
已用时间:  00: 00: 00.01
SYS@test19> ALTER SESSION SET parallel_degree_policy = AUTO;
会话已更改。
已用时间:  00: 00: 00.00
4. 常见问题
  • 1:并行更新后查询报错 ORA-12838原因:未提交事务导致后续会话无法读取被修改的表。解决:执行 COMMIT 后重试。
  • 2:并行DDL引发 ORA-00600 错误原因:parallel_execution_message_size 参数过小。
bash 复制代码
ALTER SYSTEM SET parallel_execution_message_size=8192 SCOPE=SPFILE;  
SHUTDOWN IMMEDIATE;  
STARTUP;
--
SYS@test19> show parameter parallel_execution_message_size;

NAME                                 TYPE                              VALUE
------------------------------------ --------------------------------- ------------------------------
parallel_execution_message_size      integer                           16384
3​:生效​验证步骤​,并行查询:
bash 复制代码
-- 生成执行计划(不实际执行SQL)
EXPLAIN PLAN FOR 
SELECT /*+ PARALLEL(sales_data, 8) */ 
    product_id, SUM(amount) 
FROM sales_data 
GROUP BY product_id;
-- 检查执行计划中的并行标识
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(FORMAT=>'ALL'));
SYS@test19> show parameter parallel_execution_message_size;

NAME                                 TYPE                              VALUE
------------------------------------ --------------------------------- ------------------------------
parallel_execution_message_size      integer                           16384
EXPLAIN PLAN FOR
SELECT /*+ PARALLEL(sales_data, 8) */
    product_id, SUM(amount)
FROM sales_data
  5  GROUP BY product_id;

已解释。

已用时间:  00: 00: 00.04
SYS@test19> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(FORMAT=>'ALL'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3011834688

--------------------------------------------------------------------------------------------------------------------
| Id  | Operation                | Name       | Rows  | Bytes | Cost (%CPU)| Time     |    TQ  |IN-OUT| PQ Distrib |
--------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |            |    20M|   503M|  3005   (4)| 00:00:01 |        |      |            |
|   1 |  PX COORDINATOR          |            |       |       |            |          |        |      |            |
|   2 |   PX SEND QC (RANDOM)    | :TQ10001   |    20M|   503M|  3005   (4)| 00:00:01 |  Q1,01 | P->S | QC (RAND)  |
|   3 |    HASH GROUP BY         |            |    20M|   503M|  3005   (4)| 00:00:01 |  Q1,01 | PCWP |            |
|   4 |     PX RECEIVE           |            |    20M|   503M|  3005   (4)| 00:00:01 |  Q1,01 | PCWP |            |
|   5 |      PX SEND HASH        | :TQ10000   |    20M|   503M|  3005   (4)| 00:00:01 |  Q1,00 | P->P | HASH       |
|   6 |       HASH GROUP BY      |            |    20M|   503M|  3005   (4)| 00:00:01 |  Q1,00 | PCWP |            |
|   7 |        PX BLOCK ITERATOR |            |    20M|   503M|  2914   (1)| 00:00:01 |  Q1,00 | PCWC |            |
|   8 |         TABLE ACCESS FULL| SALES_DATA |    20M|   503M|  2914   (1)| 00:00:01 |  Q1,00 | PCWP |            |
--------------------------------------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1
   8 - SEL$1 / SALES_DATA@SEL$1

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "PRODUCT_ID"[NUMBER,22], SUM()[22]
   2 - (#keys=0) "PRODUCT_ID"[NUMBER,22], SUM()[22]
   3 - (#keys=1; rowset=256) "PRODUCT_ID"[NUMBER,22], SUM()[22]
   4 - (rowset=256) "PRODUCT_ID"[NUMBER,22], SYS_OP_MSR()[25]
   5 - (#keys=1) "PRODUCT_ID"[NUMBER,22], SYS_OP_MSR()[25]
   6 - (#keys=1; rowset=256) "PRODUCT_ID"[NUMBER,22], SYS_OP_MSR()[25]
   7 - (rowset=256) "PRODUCT_ID"[NUMBER,22], "AMOUNT"[NUMBER,22]
   8 - (rowset=256) "PRODUCT_ID"[NUMBER,22], "AMOUNT"[NUMBER,22]

Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1
---------------------------------------------------------------------------

   8 -  SEL$1 / SALES_DATA@SEL$1
           -  PARALLEL(sales_data, 8)

Note
-----
   - dynamic statistics used: dynamic sampling (level=4)
   - Degree of Parallelism is 8 because of hint

已选择 45 行。

已用时间:  00: 00: 00.24

"数据量越大,并行收益越高;资源越充足,加速比越接近线性"​​,

同时这是一把双刃剑,避免滥用并行!优先通过索引优化、SQL 调优解决性能问题。

仅在硬件资源充足且数据量巨大时启用并行,否则可能拖垮系统

相关推荐
Hello.Reader3 小时前
Redis 延迟监控深度指南
数据库·redis·缓存
ybq195133454313 小时前
Redis-主从复制-分布式系统
java·数据库·redis
好奇的菜鸟6 小时前
如何在IntelliJ IDEA中设置数据库连接全局共享
java·数据库·intellij-idea
tan180°6 小时前
MySQL表的操作(3)
linux·数据库·c++·vscode·后端·mysql
满昕欢喜7 小时前
SQL Server从入门到项目实践(超值版)读书笔记 20
数据库·sql·sqlserver
Hello.Reader8 小时前
Redis 延迟排查与优化全攻略
数据库·redis·缓存
简佐义的博客9 小时前
破解非模式物种GO/KEGG注释难题
开发语言·数据库·后端·oracle·golang
爬山算法9 小时前
MySQL(116)如何监控负载均衡状态?
数据库·mysql·负载均衡
老纪的技术唠嗑局12 小时前
OceanBase PoC 经验总结(二)—— AP 业务
数据库
阿里云大数据AI技术12 小时前
OpenSearch 视频 RAG 实践
数据库·人工智能·llm