当我们要在操作中处理大量数据,比如全表扫描大表或者创建一个大的索引,可以将这个工作通过使用多个进程来分成多个小任务来完成,这就叫作并行执行或者并行处理。并行执行当要访问大量数据时能够减少响应时间,但不是任何时候并行执行都比串行执行快。并行执行在以下情况下是非常有用的:
查询要使用大表扫描,连接或分区索引扫描
创建大索引和大表
批量插入,更新和删除
聚合操作
并行执行的工作原理
一个查询提交到Oracle服务器并解析。在优化时如果考虑使用并行执行,那么在执行时间用户影子进程将成为查询协调者(QC)并且根据需要为会话分配并行子进程。QC根据并行子进程的数量并使用基于rowid范围或分区(从8i开始)来将工作进行分解。为了达到平均分布工作量,一个简单的分布算法被使用。生产者读取数据并将数据存储在表队列中由消费者或者QC来从表队列中读取数据。
如果SQL语句要执行排序,那么由消费者并行子进程从生产者相关的表队列中读取数据并且进行排序并将排序后的数据存储到与消费者并行子进程相关的新表队列中。这些队列然后将由QC进程进行读取。
如果SQL语句不执行排序,那么QC将直接读取生产者子进程相关的表队列。
Query Coordinator(QC)查询协调者:由会话派生出来的前台进程,用来从查询子进程中接收数据
Slaves:子进程从磁盘或表队列结构(也可能是其它的子进程)中读取数据并且将数据存储到它们自己的表队列中。当子进程从磁盘读取数据时,将会执行直接I/O路么读。这意味着将会跨过buffer cache。事实上,子进程将会强制将已经被更新但还没有被刷新到磁盘的数据块从buffer cache中刷新到磁盘,然后使用直接路径I/O来读取数据。
子进程将等待数据进入队列和离开队列的消息。有两种类型的子进程
生产者和消费者:
生产者子进程根据QC所给定的rowid范围或分区来查看数据块和检索相关数据。然后这些数据会被存储到表队列中由QC或消费者子进程来进行读取
消费者子进程当需要从由生产者子进程填充的表队列中读取数据时才产生。读取数据后经过消费者子进程处理后返回给QC处理。
在一个最简单的查询中(数据不需要进行排序),那么不需要产生消费者进程并且QC会直接从生产者子进程表队列中读取数据。
出现生产者和消费者的原因是因为当并行查询需要排序时并行度有时要求双倍数量的查询子进程。
SQL> select /*+ parallel */ * from scott.emp; EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ---------- ---------- --------- ---------- ------------ ---------- ---------- ---------- 7369 SMITH CLERK 7902 17-DEC-80 800 20 7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30 7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30 7566 JONES MANAGER 7839 02-APR-81 2975 20 7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30 7698 BLAKE MANAGER 7839 01-MAY-81 2850 30 7782 CLARK MANAGER 7839 09-JUN-81 2450 10 7788 SCOTT ANALYST 7566 19-APR-87 3000 20 7839 KING PRESIDENT 17-NOV-81 5000 10 7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30 7876 ADAMS CLERK 7788 23-MAY-87 1100 20 EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ---------- ---------- --------- ---------- ------------ ---------- ---------- ---------- 7900 JAMES CLERK 7698 03-DEC-81 950 30 7902 FORD ANALYST 7566 03-DEC-81 3000 20 7934 MILLER CLERK 7782 23-JAN-82 1300 10 14 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 2873591275 -------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib | -------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 14 | 532 | 2 (0)| 00:00:01 | | | | | 1 | PX COORDINATOR | | | | | | | | | | 2 | PX SEND QC (RANDOM)| :TQ10000 | 14 | 532 | 2 (0)| 00:00:01 | Q1,00 | P->S | QC (RAND) | | 3 | PX BLOCK ITERATOR | | 14 | 532 | 2 (0)| 00:00:01 | Q1,00 | PCWC | | | 4 | TABLE ACCESS FULL| EMP | 14 | 532 | 2 (0)| 00:00:01 | Q1,00 | PCWP | | -------------------------------------------------------------------------------------------------------------- Note ----- - automatic DOP: Computed Degree of Parallelism is 2 Statistics ---------------------------------------------------------- 130 recursive calls 4 db block gets 155 consistent gets 18 physical reads 0 redo size 1401 bytes sent via SQL*Net to client 415 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 11 sorts (memory) 0 sorts (disk) 14 rows processed
上面的查询没有要求排序所以只产生的生产者,也只有一个表队列TQ10000,一组并行子进程就是生产者子进程
SQL> select /*+ parallel */ * from scott.emp order by ename; EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ---------- ---------- --------- ---------- ------------ ---------- ---------- ---------- 7876 ADAMS CLERK 7788 23-MAY-87 1100 20 7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30 7698 BLAKE MANAGER 7839 01-MAY-81 2850 30 7782 CLARK MANAGER 7839 09-JUN-81 2450 10 7902 FORD ANALYST 7566 03-DEC-81 3000 20 7900 JAMES CLERK 7698 03-DEC-81 950 30 7566 JONES MANAGER 7839 02-APR-81 2975 20 7839 KING PRESIDENT 17-NOV-81 5000 10 7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30 7934 MILLER CLERK 7782 23-JAN-82 1300 10 7788 SCOTT ANALYST 7566 19-APR-87 3000 20 EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ---------- ---------- --------- ---------- ------------ ---------- ---------- ---------- 7369 SMITH CLERK 7902 17-DEC-80 800 20 7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30 7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30 14 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 3979194000 ----------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib | ----------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 14 | 532 | 3 (34)| 00:00:01 | | | | | 1 | PX COORDINATOR | | | | | | | | | | 2 | PX SEND QC (ORDER) | :TQ10001 | 14 | 532 | 3 (34)| 00:00:01 | Q1,01 | P->S | QC (ORDER) | | 3 | SORT ORDER BY | | 14 | 532 | 3 (34)| 00:00:01 | Q1,01 | PCWP | | | 4 | PX RECEIVE | | 14 | 532 | 2 (0)| 00:00:01 | Q1,01 | PCWP | | | 5 | PX SEND RANGE | :TQ10000 | 14 | 532 | 2 (0)| 00:00:01 | Q1,00 | P->P | RANGE | | 6 | PX BLOCK ITERATOR | | 14 | 532 | 2 (0)| 00:00:01 | Q1,00 | PCWC | | | 7 | TABLE ACCESS FULL| EMP | 14 | 532 | 2 (0)| 00:00:01 | Q1,00 | PCWP | | ----------------------------------------------------------------------------------------------------------------- Note ----- - automatic DOP: Computed Degree of Parallelism is 2 Statistics ---------------------------------------------------------- 19 recursive calls 4 db block gets 13 consistent gets 0 physical reads 0 redo size 1406 bytes sent via SQL*Net to client 415 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 3 sorts (memory) 0 sorts (disk) 14 rows processed
上面的查询要求排序,所以出现了生产者与消费者,生产者表队列为:TQ10000,消费者表队列为TQ10001
子进程从表队列中等待离队信息时,'PX Deque wait'等待是并行查询正常操作的一部分。子进程在工作之前必须等待离队的消息才能开始进行。
Table Queues(TQ)队列它允许进程将行记录发送给其它进程。比如数据从生产者子进程发送到消费者子进程。消费者子进程也可以将数据发送给QC。
与并行执行相关的数据库参数
parallel_max_servers
一个实例可以使用的最大并行子进程数,如果为0则意味着不能并行执行语句
parallel_min_servers
实例在启动时产生的最小并行子进程数,这些子进程在实例的生命周期内都是被保留存在的。
parallel_min_percent
如果优化器已经决定了查询将以并行方式来执行,但执行时系统没有足够的资源来满足所请求的并行度,那么缺省情况下,查询将以串行方式来执行并且不会给用户返回任何信息。这就很容易造成一个查询的执行时间非常长。这个参数提供了一个方法当没有足够资源可用时来阻查询以串行方式来执行并显示错误信息。它定义了一个并行查询可以执行的最小资源数。它用可以并行执行子进程的百分比
来表示。
如果这个参数没有被设置且并行执行所需要的资源不能满足那么查询将会以串行方式来执行。
如果这个参数被设置并且期望的查询子进程的百分比不满足,那么将会报错(ORA-12827)而不是串行执行语句。
如果并行执行需要的资源不可用,当parallel_min_percent=0时,语句将会串行执行而不会报错。当parallel_min_percent=50,意味着查询执行是最佳并行执行时间的2倍是可以接受的。当parallel_min_percent=100,意味着除非并行查询所需要的资源完全可用才会并行执行否则会报错ORA-12827。
parallel_adaptive_multi_user
当parallel_adaptive_multi_user设置为true时,启用自适应算法来使用并行执行提高多用户环境中的性能。算法会根据查询开始执行时间的系统负载来自动减少所请求的并行度。实际上有效的并行度是基于缺省并行度,或都表的并行度或hints除以减小因子。
例如:在一个有17个CPU的主机上缺省并行度可以被设置为32。如果用户执行一个并行查询,得到的并行度是32,能有效的使用系统中的所有CPU和内存。当第二个用户登录系统,并执行一个并行行查询,得到的并行度将是16,当系统中的用户增加时,算法将继续减小并行度直到用户使用的并行度等于1为止,也就是系统有32个用户登录时。
parallel_automatic_tuning
设置parallel_automatic_tuning参数将会对PX参数有影响,当parallel_automatic_tuning=false时:
parallel_executon_message_size的缺省值为2Kbyte
parallel_adaptive_multi_user的缺省值为false
large_pool_size不受影响
prcoesses不受影响
parallel_max_servers 5
当parallel_automatic_tuning=true时:
parallel_executon_message_size的缺省值为4Kbyte
parallel_adaptive_multi_user的缺省值为true
large_pool_size 将基于其它条种参数来进行复杂计算来计算出增加值
prcoesses 如果processes parallel_max_servers 如果parallel_adaptive_multi_user=true时
(cpus * parallel_threads_per_cpu * _parallel_adaptive_max_users * 5),否则
(cpus * parallel_threads_per_cpu * _parallel_adaptive_max_users * 8)
parallel_theads_per_cpu缺省值依赖于操作系统,缺省值为2
parallel_force_local
parallel_force_local参数控制RAC环境中的并行执行。缺省情况下,优化器可以从RAC中的任何节点或所有节点中选择并行执行SQL语句的并行进程。当parallel_force_local设置为true时,那么并行进程就只能是与查询协调者(执行sql语句的节点)在同一个RAC节点中,也就是说并行进程是不能跨节点的.
parallel_degree_policy
parallel_degree_policy参数可以被设置为manual,auto或limited在Oracle11.1中parallel_degree_policy缺省设置为manual(禁用了automatic degree of parallelism,statement queuing与in-memory parallel execution)
parallel_min_time_threshold
parallel_min_time_threshold参数用来指定SQL语句是否并行执行一个阈值,也就是当优化器根据统计信息所估算的执行时间如果大于这个参数值就是使用并行,如果估算的执行时间小于这个参数值就会串行执行。这个参数值缺省值是10秒。并且自动并行度只要在parallel_degree_policy参数被设置为auto或limited时才会生效。
parallel_degree_limit
使用自动并行度时,Oracle会自动决定是否以并行方式来执行SQL语句以及所使用的并行度。优化根据语句所请求的资源来决定一个语句的并行度。然而优化器所使用的并行度是受限制的以防止并行进程击垮系统。也就是是系统中所能使用的并行度的上限为parallel_degree_limit参数值。它有三个参数值可以选择:
CPU
最大并行度由系统中的CPU数量来限制。其计算公式为
parallel_degree_limit=parallel_thread_per_cpu*cpu_count当然,你也可以将parallel_degree_limit的值设置为一个具体的值,以达到明确控制实际并行度的目的。
IO
优化器能使用的最大并行度由系统的I/O能力来限制。这个值等于系统总吞吐量除以每个进程的最大I/O带宽。但在Oracle 11.2中为了将parallel_degree_limit设置为IO必须执行dbms_resource_manager.calibrate_io过程来收集系统的I/O统计信息。这个过程将会计算系统的总吞吐量与每个进程的最大IO带宽。
具体数字
当自动并行度被激活时,指定一个SQL语句所能使用的最大并行度。这个参数只有当parallel_degree_policy设置为auto或limited时才生效。
parallel_servers_target
Parallel_servers_target用于控制在自动并行被完全开启的情况下,待并行执行的目标SQL是立即被执行还是进入并行执行队列,它的默认值为4*cpu_count*parallel_threads_per_cpu(在Oracle 11gr2中,如果你开启了SGA和PGA的自动调整,则parallel_servers_target的默认值为8*cpu_count*parallel_threads_per_cpu)。
优化器
并行执行只能用于CBO。在有些情况下,当表或索引有非0并行度时将会强制使用CBO。有以下hint影响并行执行:
.parallel
.noparallel
.pq_distribute
.parallel_index
.noparallel_index
如果使用RBO,那么任何并行hints都会被忽略。
如何判断语句是否使用并行执行
1.检查执行计划
如果没有并行的相关信息,那么没有使用并行执行。检查plan table中由并行子进程所使用的其它列。如果SQL包含hints比如/*+ rowid(a1) */并且这些hints没有出现在原代码中,那么可能是并行查询所使用的。ROWID hint是一种内部方法用于处理并行查询。注意ROWID hint有特定的意思,意味首跨过了buffer cache。事实上它会造成buffer被刷新到磁盘因此对基本的数据文件可以执行直接
I/O。
有两个脚本用来格式化plan_table的查询。一个用来格式化串行执行计划的输出(utlxpls.sql),这些脚本都可以到$ORACLE_HOME/rdbms/admin/目录下找到。为了避免截断输出执行以下设置
'set charwidth 108' in svrmgrl
'set linesize 108' in SQL*Plus
2.执行查询
SQL> select * from v$pq_sesstat; STATISTIC LAST_QUERY SESSION_TOTAL ------------------------------ ---------- ------------- Queries Parallelized 0 0 DML Parallelized 0 0 DDL Parallelized 0 0 DFO Trees 0 0 Server Threads 0 0 Allocation Height 0 0 Allocation Width 0 0 Local Msgs Sent 0 0 Distr Msgs Sent 0 0 Local Msgs Recv'd 0 0 Distr Msgs Recv'd 0 0 11 rows selected. SQL> select count(*) from t1; COUNT(*) ---------- 22040576 SQL> select * from v$pq_sesstat; STATISTIC LAST_QUERY SESSION_TOTAL ------------------------------ ---------- ------------- Queries Parallelized 1 1 DML Parallelized 0 0 DDL Parallelized 0 0 DFO Trees 1 1 Server Threads 2 0 Allocation Height 1 0 Allocation Width 2 0 Local Msgs Sent 29 29 Distr Msgs Sent 29 29 Local Msgs Recv'd 29 29 Distr Msgs Recv'd 29 29 11 rows selected. 上面查询信息的第一行可以看到这个会话的最后一个查询是并行执行。 3.检查子进程活动视图 查询v$pq_slave两次
SQL> SELECT slave_name,status, cpu_secs_total FROM v$pq_slave; SLAV STAT CPU_SECS_TOTAL ---- ---- -------------- PZ99 IDLE 0 SQL> select count(*) from t1; COUNT(*) ---------- 22040576 SQL> SELECT slave_name,status, cpu_secs_total FROM v$pq_slave; SLAV STAT CPU_SECS_TOTAL ---- ---- -------------- P000 BUSY 0 PZ99 IDLE 0
如果没有返回记录,说明没有并行子进程运行。如果在2次运行之间CPU利用率没有差异,那么在这期间没有CPU活动。
可以通过v$session_wait来查询PQ活动,对于8以下的版本可以执行以下查询
SQL> SELECT sid, event, seq#,p1,p2,p3, wait_time FROM v$session_wait WHERE upper(event) like ('PX%') ORDER BY 1; SID EVENT ---------- ---------------------------------------------------------------- SEQ# P1 P2 P3 WAIT_TIME ---------- ---------- ---------- ---------- ---------- 33 PX Deq: Execute Reply 31451 200 1 0 0
4.使用event 10046 level 12诊断事件来跟踪查询协调者QC
并行执行的性能
以并行方式来执行查询而不是串行,可以提高性能。然而,并行查询是否最有效,有许多因素需要考虑。多个并行子进程会比单个进程消耗更多的CPU,每个并行子进程都有它自己的地址空间(内存分配)
对于读取大量数据除了使用并行执行来减少执行时间之外没有其它选择。通过将读取工作负载分解到多个处理器或并行子进程来执行最大化了数据吞吐量。排序操作也使用子进程的'table queue structures'来进行管理。这将有效地增加整个系统的CPU负载。如果系统当前CPU利用率已经最大化(或接近最大化),并行查询不会得到任何好处。如果没有更多的CPU可以使用,那么并行查询操作将以串行方式来执行。
另外并行子进程的读取数据的额外IO请求也会对I/O子系统的负载产生压力。在有些情况下磁盘I/O可能会成为瓶颈。跨多个磁盘来分布数据会所有帮助。
另外对于适用索引查询而不是并行查询的语句串行方式执行可能会运行地更快
.nested loops vs hash/sort merge join
通常来说并行查询是使用快速表扫描来访问数据并且基于rowid范围来分给并行子进程。通常nestedloop join对于快速表扫描不是很有效(除非驱动表输入的记录数很小)。Hash Joins与Sort Merge在处理大理数据时会更有效。然而,对于HJ与SMJ不会对来自驱动表(也就是驱动行记录)进行行消除。这种行消除会减小被调用数据集的大小。因为被消除的数据量这就可能意味着使用索引访问数据的串
行执行会比并行执行更快。
.创建并行子进程,平均分解数据并将数据传给多个进程和合并结果的总成本可能会比串行执行的成本更高
.数据倾斜
并行执行是基于rowid范围来将数据平均分给并行子进程。因为分给每个并行子进程的相同数据块包含首相同记录。事实上,有些数据块完全是空的。这种问题在大量数据被归档或被删除之后更为突出,因此造成了许多空块或稀疏的数据块。这会造成数据的不均匀分布进而造成并行查询比串行查询运行的更慢,因为一个并行子进程执行工作(例如一个并行子进程的对分布的数据串行访问会引起瓶颈)。
对于这种情况除了重新组织数据几乎没有方法可以解决。
性能概述
并不是所有的并行查询都比串行查询执行快。有些查询适合并行查询,有些适合串行查询。如果使用并行执行,那么应该尽量最大化磁盘I/O的吞吐量。确保:
.有足够的并行子进程来有效检索数据
.不能有太多的并行子进程(避免超过CPU)
.设置内存参数(sort_area_size等)因此不会内存溢出与引起内存交换
.数据均匀分布给多个磁盘,因此并行子进程没有I/O竞争
.需要并行执行的查询类型在适合并行执行
.查看并行子进程是否出现数据的不均匀分布这就说明了数据倾斜