引言
在处理大规模数据时,传统的串行SQL执行方式往往成为性能瓶颈——单个服务器进程只能使用一个CPU核心,面对百万行甚至亿行级别的表扫描、索引创建、数据装载等操作时,响应时间可能长达数小时。Oracle的并行查询(Parallel Query,简称PQ)技术正是为解决这一问题而生,它将一个大型任务分解为多个小任务,由多个并行进程同时处理,充分利用服务器的多核CPU、内存和I/O带宽资源,显著缩短处理时间。
本文将全面介绍Oracle并行查询的工作原理、体系架构、配置方法、关键参数以及常见问题排查,帮助你安全高效地驾驭并行查询这一强大特性。
一、并行查询的核心概念与架构
1.1 什么是并行查询?
并行查询是指Oracle将一个SQL操作的执行任务拆分成多个子任务,由多个并行的服务器进程(Parallel Server Processes,简称PX Slaves)同时执行,最后将结果汇总返回给用户。每个并行进程独立处理一部分数据,理想情况下可以将执行时间缩短到原来的1/N(N为并行度)。
1.2 并行查询的进程架构
Oracle并行执行使用一个“生产者-消费者”模型,包含两类关键进程:
| 进程类型 | 角色 | 数量 |
|----------|------|------|
|Query Coordinator(QC)| 协调者进程,即用户的会话进程。负责解析SQL、生成并行执行计划、分配任务、汇总结果 | 1个 |
|PX Slave Processes| 并行执行进程,实际执行数据扫描、连接、排序等操作 | Degree of Parallelism × 2(通常) |
一个典型的并行查询会用到2 × DOP(Degree of Parallelism)个并行进程——一半作为生产者(数据扫描),另一半作为消费者(数据加工),它们之间通过表队列(Table Queue)传递数据。
1.3 并行执行的适用场景
并行查询并非万能,它最适用于以下场景:
- 大规模全表扫描:对百万行以上的表进行扫描、聚合、排序
- 大型索引创建/重建:加速DDL操作
- 大批量数据装载:INSERT INTO ... SELECT、CTAS
- 分区表操作:天然支持按分区粒度并行
- 大型表连接:Hash Join、Sort-Merge Join等
- 数据仓库和OLAP系统:分析型负载为主的环境
不适用场景:
- 小型表操作(数据量不足以分摊并行调度开销)
- 高并发的OLTP事务(并行会抢夺CPU资源,影响并发用户)
- 频繁的索引唯一扫描(单行查找无法并行化)
二、并行查询的配置方法
2.1 数据库层面的基础配置
在使用并行查询之前,必须确保数据库有足够的并行进程资源:
-- 查看当前并行相关参数 SHOW PARAMETER parallel; -- 关键参数说明 -- parallel_max_servers:实例允许的最大并行进程总数 -- parallel_min_servers:实例启动时预先创建的并行进程数 -- parallel_degree_policy:并行度策略(MANUAL/LIMITED/AUTO/ADAPTIVE) -- parallel_degree_limit:单个操作允许的最大并行度基础配置建议:
-- 设置最大并行进程数(根据CPU核数和预期并发并行操作数量) -- 公式参考:parallel_max_servers = CPU_CORES × 并发并行用户数 × (每个用户的DOP × 2) ALTER SYSTEM SET parallel_max_servers = 64 SCOPE=BOTH; -- 设置最小并行进程数(避免频繁创建销毁进程的开销) ALTER SYSTEM SET parallel_min_servers = 16 SCOPE=BOTH; -- 设置并行策略为手动控制(推荐在OLTP为主的系统中使用MANUAL) ALTER SYSTEM SET parallel_degree_policy = MANUAL SCOPE=BOTH; -- 设置单个操作的并行度上限 ALTER SYSTEM SET parallel_degree_limit = 16 SCOPE=BOTH;2.2 在对象级别设置并行度
方式一:创建表时指定并行度
CREATE TABLE sales_2024 PARALLEL 8 AS SELECT * FROM sales WHERE sale_year = 2024;方式二:修改已有表的并行度
-- 设置表的并行度 ALTER TABLE sales PARALLEL 8; -- 设置索引的并行度 ALTER INDEX idx_sales_date PARALLEL 4; -- 查看对象的并行度设置 SELECT table_name, degree FROM dba_tables WHERE owner = 'SH'; SELECT index_name, degree FROM dba_indexes WHERE owner = 'SH';方式三:取消对象的并行设置
-- 恢复为默认并行度(通常为1,即串行) ALTER TABLE sales NOPARALLEL; ALTER INDEX idx_sales_date NOPARALLEL;2.3 在SQL语句中使用Hint指定并行度
Hint方式最为灵活,可以针对单条SQL临时启用或调整并行度:
-- 指定全表扫描的并行度 SELECT /*+ PARALLEL(s 8) */ region, SUM(amount) FROM sales s GROUP BY region; -- 指定多表连接的并行度 SELECT /*+ PARALLEL(e 4) PARALLEL(d 4) */ e.employee_id, e.last_name, d.department_name FROM employees e, departments d WHERE e.department_id = d.department_id; -- 指定整个语句的默认并行度(所有对象使用相同DOP) SELECT /*+ PARALLEL(8) */ customer_id, COUNT(*) FROM orders WHERE order_date > SYSDATE - 365 GROUP BY customer_id;2.4 使用自动并行度(Auto DOP)
从Oracle 11gR2开始,Oracle支持自动计算并行度:
-- 开启自动并行度 ALTER SYSTEM SET parallel_degree_policy = AUTO SCOPE=BOTH; -- 此时Oracle会根据SQL的资源消耗估算自动决定是否并行及并行度 -- 无需在对象或SQL中显式指定PARALLEL自动并行的触发条件:
- SQL的估算执行时间超过
PARALLEL_MIN_TIME_THRESHOLD(默认10秒) - 涉及的对象有足够的空闲并行进程
- 系统整体负载允许
三、并行查询的关键参数详解
3.1 parallel_max_servers
这是最重要的并行限制参数。如果所有并行进程都被占用,新的并行请求将降级为串行执行或排队等待。
-- 查看当前并行进程使用情况 SELECT * FROM v$px_process_sysstat; -- 查看当前活跃的并行进程 SELECT sid, serial#, degree, req_degree FROM v$px_session WHERE qcsid IS NOT NULL; -- 查看并行进程使用率(接近100%说明需要增加parallel_max_servers) SELECT (SELECT COUNT(*) FROM v$px_process WHERE status = 'IN USE') / (SELECT value FROM v$parameter WHERE name = 'parallel_max_servers') * 100 AS usage_pct FROM dual;3.2 parallel_degree_policy
| 值 | 含义 |
|----|------|
|MANUAL| 手动控制并行度,仅当对象设置PARALLEL或SQL使用Hint时才并行 |
|LIMITED| 类似MANUAL,但某些自动并行特性可能启用 |
|AUTO| Oracle自动判断是否并行及计算DOP |
|ADAPTIVE| 基于运行时统计信息动态调整并行度(12c+) |
3.3 parallel_degree_limit
限制单个并行操作的并行度上限,防止某条SQL耗尽所有并行进程:
-- 将单条SQL的DOP限制为CPU核数的2倍 ALTER SYSTEM SET parallel_degree_limit = CPU;可选值:CPU(CPU核数)、IO(IO带宽相关)、整数(绝对上限)。
3.4 parallel_force_local
控制并行进程是否跨RAC节点执行:
-- 限制并行只在本地节点执行(避免跨节点数据传输) ALTER SYSTEM SET parallel_force_local = TRUE SCOPE=BOTH; -- 允许跨RAC节点并行(利用全集群资源) ALTER SYSTEM SET parallel_force_local = FALSE SCOPE=BOTH;四、并行查询的执行计划解读
4.1 识别并行执行计划
启用并行后,执行计划中会出现特殊的并行操作符:
-- 查看带有并行信息的执行计划 EXPLAIN PLAN FOR SELECT /*+ PARALLEL(s 4) */ region, SUM(amount) FROM sales s GROUP BY region; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(format => 'ALL -PROJECTION'));典型的并行执行计划输出:
---------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| ---------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 36 | 10 (10) | 00:00:01| | | | 1 | PX COORDINATOR | | | | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10001 | 4 | 36 | 10 (10) | 00:00:01| Q1,01 | P->S | | 3 | HASH GROUP BY | | 4 | 36 | 10 (10) | 00:00:01| Q1,01 | PCWP | | 4 | PX RECEIVE | | 4 | 36 | 10 (10) | 00:00:01| Q1,01 | PCWP | | 5 | PX SEND HASH | :TQ10000 | 4 | 36 | 10 (10) | 00:00:01| Q1,00 | P->P | | 6 | HASH GROUP BY | | 4 | 36 | 10 (10) | 00:00:01| Q1,00 | PCWP | | 7 | PX BLOCK ITERATOR | | 10000 | 90000 | 9 (0) | 00:00:01| Q1,00 | PCWC | | 8 | TABLE ACCESS FULL| SALES | 10000 | 90000 | 9 (0) | 00:00:01| Q1,00 | PCWP | ----------------------------------------------------------------------------------------------关键并行操作符解释:
| 操作符 | 含义 |
|--------|------|
|PX COORDINATOR| 查询协调者(QC),用户的会话进程 |
|PX SEND QC| 并行进程将数据发送给QC |
|PX RECEIVE| 并行进程接收其他并行进程发送的数据 |
|PX SEND HASH/RANGE| 并行进程之间通过Hash或Range方式分发数据 |
|PX BLOCK ITERATOR| 并行进程以数据块为粒度迭代处理数据 |
|TQ列 | Table Queue编号,标识数据在不同进程组之间的流动 |
|IN-OUT列 | P->S(并行到串行)、P->P(并行到并行)、PCWP(并行组合与父操作并行)、PCWC(并行组合与子操作并行) |
4.2 理解Table Queue与数据流
每个TQ(Table Queue)代表一次数据重分发,过多的Table Queue意味着大量进程间通信开销。
五、并行查询的监控与诊断
5.1 实时监控并行查询
-- 查看当前正在执行的并行操作 SELECT qcsid, -- QC的会话ID qcserial#, -- QC的序列号 degree, -- 实际使用的并行度 req_degree, -- 请求的并行度 slave_set -- 当前并行进程组 FROM v$px_session WHERE qcsid IS NOT NULL; -- 查看每个并行进程的工作状态 SELECT px.sid, s.username, s.program, px.qcsid, px.server_group, px.server_set, px.server#, s.sql_id, ROUND(s.last_call_et/60, 2) AS active_minutes FROM v$px_session px, v$session s WHERE px.sid = s.sid AND px.qcsid IS NOT NULL ORDER BY px.qcsid, px.server_group, px.server#;5.2 查看并行操作的历史统计
-- v$pq_sesstat:会话级并行统计 SELECT * FROM v$pq_sesstat WHERE statistic LIKE '%Queries%'; -- v$pq_sysstat:系统级并行统计 SELECT * FROM v$pq_sysstat;5.3 并行操作降级的诊断
当系统并行进程不足时,请求的DOP与实际使用的DOP可能存在差异:
-- 检查并行降级情况 SELECT name, value FROM v$sysstat WHERE name LIKE '%parallel%' AND name LIKE '%downgraded%'; -- 检查并行进程排队等待情况 SELECT name, value FROM v$sysstat WHERE name LIKE 'Parallel operations%queued';六、并行查询的常见问题与解决方案
6.1 并行进程不足
现象:SQL使用了并行Hint,但执行计划显示串行执行,或执行时间远超预期。
-- 诊断方法 SELECT * FROM v$px_process_sysstat WHERE statistic IN ('Servers In Use', 'Servers Highwater'); -- 如果Servers In Use经常接近parallel_max_servers,需要增加该参数 ALTER SYSTEM SET parallel_max_servers = 128 SCOPE=BOTH;6.2 并行度设置过高导致性能下降
现象:并行度设置过大,CPU上下文切换开销超过并行收益,甚至导致系统整体变慢。
建议:
- 初始并行度不超过
CPU_CORES × 2 - 对于I/O密集型操作,可适当增大DOP
- 对于CPU密集型操作,DOP接近CPU核数即可
-- 查看CPU核数 SELECT value FROM v$parameter WHERE name = 'cpu_count'; -- 合理的默认并行度 ALTER TABLE sales PARALLEL (DEGREE DEFAULT); -- DEFAULT模式下,Oracle根据cpu_count和parallel_threads_per_cpu自动计算6.3 并行DDL操作后未关闭并行
常见陷阱:执行ALTER TABLE sales PARALLEL 8后,后续所有对该表的查询都会默认使用并行,在高并发OLTP环境下可能耗尽并行进程。
-- 最佳实践:DDL操作后恢复为串行 ALTER TABLE sales PARALLEL 8; CREATE INDEX idx_sales_date ON sales(sale_date) PARALLEL 8; ALTER TABLE sales NOPARALLEL; ALTER INDEX idx_sales_date NOPARALLEL;6.4 数据倾斜导致并行效率低下
当数据分布不均匀时(如按销售区域分区,某个区域数据量远大于其他区域),部分并行进程可能提前完成而闲置,最慢的进程决定整体执行时间。
解决方案:
- 使用
HASH分发方式而非RANGE分发 - 在分区设计时关注数据均衡性
- Oracle 12c+的Adaptive Parallel Distribution可自动应对数据倾斜
6.5 并行与RAC跨节点通信开销
RAC环境下,如果并行进程跨节点分布,Table Queue的数据传输需要经过私有网络,可能成为瓶颈。
-- 限制并行在本地节点执行 ALTER SESSION SET parallel_force_local = TRUE; -- 或在SQL中指定 SELECT /*+ PARALLEL(s 8) NO_PARALLEL_PROPAGATION */ ...七、并行查询最佳实践
7.1 并行度选择原则
| 负载类型 | 建议DOP | 说明 |
|----------|---------|------|
| 小表(<1万行) | 1(串行) | 并行调度开销大于收益 |
| 中型表(1万-100万行) | 2-4 | 适度并行 |
| 大型表(>100万行) | 4-16 | 根据CPU和I/O资源确定 |
| 超大表(>1亿行) | 16-64 | 需充分测试并监控资源 |
| DDL操作(索引创建) | CPU_CORES × 2 | 利用全部CPU资源加速DDL |
7.2 资源管理策略
-- 使用Resource Manager限制并行资源使用 BEGIN DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE( plan => 'DAYTIME_PLAN', group_or_subplan => 'OLTP_GROUP', parallel_degree_limit_p1 => 2, -- 限制OLTP组的并行度 mgmt_p1 => 80 -- CPU使用上限80% ); END; / -- 为并行查询创建专用的服务 BEGIN DBMS_SERVICE.CREATE_SERVICE( service_name => 'batch_parallel_svc', network_name => 'batch_parallel_svc' ); END; / -- 在应用端通过服务名连接,隔离OLTP和批处理负载7.3 并行与分区结合
分区表天然支持分区级并行(Partition-Wise Join),可以大幅减少数据分发开销:
-- 创建分区表 CREATE TABLE sales ( sale_id NUMBER, sale_date DATE, region VARCHAR2(50), amount NUMBER ) PARTITION BY RANGE (sale_date) INTERVAL (NUMTOYMINTERVAL(1, 'MONTH')) ( PARTITION p202301 VALUES LESS THAN (TO_DATE('2023-02-01', 'YYYY-MM-DD')) ) PARALLEL 8;八、实战案例:优化大表扫描的性能
场景:某数据仓库需要对一年的销售明细(12亿行,约500GB)按区域汇总,串行执行需要2小时。
原始SQL(串行):
SELECT region, SUM(amount), COUNT(*) FROM sales_detail WHERE sale_date BETWEEN DATE '2023-01-01' AND DATE '2023-12-31' GROUP BY region;优化方案:
-- 1. 临时设置表的并行度 ALTER SESSION SET parallel_degree_policy = MANUAL; -- 2. 使用并行Hint执行查询 SELECT /*+ PARALLEL(s 16) */ region, SUM(amount), COUNT(*) FROM sales_detail s WHERE sale_date BETWEEN DATE '2023-01-01' AND DATE '2023-12-31' GROUP BY region; -- 3. 查看执行计划确认并行已启用 SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(format => 'ALLSTATS LAST'));执行计划关键输出:
| Id | Operation | Name | E-Rows | A-Time | Buffers | | 0 | SELECT STATEMENT | | | | | | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10001 | 6 | | | | 3 | HASH GROUP BY | | 6 |00:08:35 | 152K | | 4 | PX RECEIVE | | 300M| | | | 5 | PX SEND HASH | :TQ10000 | 300M| | | | 6 | PX BLOCK ITERATOR | | 300M|00:07:12 | 12.4M | | 7 | TABLE ACCESS FULL| SALES_DETAIL | 300M|00:06:45 | 12.3M |优化效果:
- 并行16个进程执行,总耗时从2小时降至8分35秒
- Buffer Cache使用152K个块,逻辑读可控
- 需确保
parallel_max_servers >= 32(16×2)
九、总结与建议
- 并行查询是Oracle处理大规模数据的利器,通过将任务分解为多个并行进程同时处理,充分利用多核CPU和I/O带宽,显著缩短处理时间。
- 配置并行查询需要从三个层面考虑:数据库参数(
parallel_max_servers、parallel_degree_policy)、对象设置(ALTER TABLE PARALLEL n)、SQL Hint(/+ PARALLEL(t n)/),三者灵活组合使用。
- 执行计划中的
PX COORDINATOR、PX SEND、PX RECEIVE等操作符是识别并行查询的关键标志,TQ列展示了数据在进程组之间的流动路径。
- 并行查询并非银弹:小表无需并行、OLTP环境慎用并行、DDL后记得关闭并行、需关注并行进程是否充足。
- 最佳实践:从低并行度开始测试,逐步增加直至CPU或I/O饱和;使用Resource Manager隔离OLTP和批处理负载;监控
v$px_session和v$px_process_sysstat确保并行执行健康。
调优箴言:并行查询是用资源换时间的典型策略,它的本质不是“让SQL跑得更快”,而是“用更多的资源让SQL更快地完成”。在启用并行之前,先确保SQL本身已经是串行最优的——并行一个糟糕的SQL只会更快地耗尽系统资源。