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

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

相关推荐
异世界贤狼转生码农1 小时前
MongoDB Windows 系统实战手册:从配置到数据处理入门
数据库·mongodb
QuZhengRong2 小时前
【数据库】Navicat 导入 Excel 数据乱码问题的解决方法
android·数据库·excel
码农阿豪2 小时前
Windows从零到一安装KingbaseES数据库及使用ksql工具连接全指南
数据库·windows
时序数据说7 小时前
时序数据库市场前景分析
大数据·数据库·物联网·开源·时序数据库
听雪楼主.11 小时前
Oracle Undo Tablespace 使用率暴涨案例分析
数据库·oracle·架构
我科绝伦(Huanhuan Zhou)11 小时前
KINGBASE集群日常维护管理命令总结
数据库·database
妖灵翎幺11 小时前
Java应届生求职八股(2)---Mysql篇
数据库·mysql
HMBBLOVEPDX11 小时前
MySQL的事务日志:
数据库·mysql
weixin_4196583113 小时前
MySQL数据库备份与恢复
数据库·mysql
专注API从业者14 小时前
基于 Flink 的淘宝实时数据管道设计:商品详情流式处理与异构存储
大数据·前端·数据库·数据挖掘·flink