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 调优解决性能问题。

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

相关推荐
Sunshine~L&H37 分钟前
Mac 上使用 mysql -u root -p 命令,出现“zsh: command not found: mysql“?如何解决
数据库·mysql·macos
chanalbert2 小时前
数据库连接池深度研究分析报告
数据库·spring
snpgroupcn2 小时前
泰国零售巨头 CJ Express 借助 SAP 内存数据库实现高效数据管理
数据库·express·零售
明月看潮生4 小时前
青少年编程与数学 01-011 系统软件简介 19 SSMS 数据库管理工具
数据库·青少年编程·编程与数学
blammmp5 小时前
Redis : set集合
数据库·redis·缓存
翔云1234565 小时前
精准测量 MySQL 主从复制延迟—pt-heartbeat工具工作原理
数据库·mysql
厚衣服_35 小时前
第15篇:数据库中间件高可用架构设计与容灾机制实现
java·数据库·中间件
明月看潮生6 小时前
青少年编程与数学 01-011 系统软件简介 13 Microsoft SQL Server数据库
数据库·microsoft·青少年编程·系统软件
LUCIAZZZ6 小时前
项目拓展-Jol分析本地对象or缓存的内存占用
java·开发语言·jvm·数据库·缓存·springboot
寒山李白6 小时前
MySQL分库分表面试题深度解析
数据库·mysql·面试题