再上一篇:13.6小结
上一篇:14.1何时使用并行执行
主页
下一篇:14.3并行DML
再下一篇:14.4并行DDL
文章列表

14.2并行查询

Oracle 9i 10g编程艺术:深入数据库体系结构

并行查询允许将一个SQL SELECT语句划分为多个较小的查询,每个部分的查询并发地运行,然后会 将各个部分的结果组合起来,提供最终的答案。例如,考虑以下查询:
big_table@ORA10G> select count(status) from big_table;

通过并行查询,这个查询可以使用多个并行会话;将BIG_TABLE划分为较小的不重叠的部分(片);
要求各个并行会话读取表并统计它那一部分中的行数。这个会话的并行查询协调器再从各个并行会话接收 各个统计结果,进一步汇总这些结果,将最后的答案返回给客户应用。如果用图形来表示,这个过程如图
14-1所示。
P000、P001、P002和P003进程称为并行执行服务器(parallel execution server),有时也称为并 行查询(parallel query,PQ)从属进程。各个并行执行服务器都是单独的会话,就像是专业服务器进程 一样连接数据库。每个并行执行服务器分别负责扫描BIG_TABLE中一个部分(各个部分都不重叠),汇总其 结果子集,将其输出发回给协调服务器(即原始会话的服务器进程),它再将这些子结果汇总为最终答案。

图 14-1 并行select count(status)示意图
可以通过一个解释计划来查看这个查询。使用一个有10,000,000行数据的BIG_TABLE,我们将逐步 启用这个表的一个并行查询,来了解如何“看到”实际的并行查询。这个例子在一个4CPU主机上执行,所 有并行参数都取默认值;也就是说,这是一个初始安装,只设置了必要的参数,包括 SGA_TARGET(设置为

1GB)、CONTROL_FILES、DB_BLOCK_SIZE(设置为 8KB)和 PGA_AGGREGATE_TARGET(设置为 512MB)。起初, 我们可能会看到以下计划:
big_table@ORA10GR1> explain plan for
2 select count(status) from big_table; Explained.
big_table@ORA10GR1> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT
---------------------------------------- Plan hash value: 1287793122
---------------------------------------------------------------------------------------

-----------------

| Id | Operation | Name | Rows |Bytes | Cost (%CPU) | Time |

--------------------------------------------------------------------------------------------------------

----------------------

| 0 | SELECT STATEMENT | | 1 | 17 | 32390 (2) |00:06:29 |

| 1 | SORT AGGREGATE | | 1 | 17 | |

|

| 2 | TABLE ACCESS FULL | BIG_TABLE | 10M | 162M | 32390 (2) |00:06:29

|

--------------------------------------------------------------------------------------------------------

----------------------

这是一个典型的串行计划。这里不涉及并行化,因为我们没有请求启用并行查询,而默认情况下并不
启用并行查询。
启用并行查询有多种方法,可以直接在查询中使用一个提示,或者修改表,要求考虑并行执行路径(在 这里,我们将会使用后一种做法)。
可以具体指定这个表的执行路径中要考虑的并行度。例如,可以告诉Oracle:“我们希望你在创建这 个表的执行计划时使用并行度4”:
big_table@ORA10GR1> alter table big_table parallel 4;
Table altered.

但我更喜欢这样告诉Oracle:“请 考虑并行执行,但是你要根据当前的系统工作负载和查询本身来
确定适当的并行度”。也就是说,并行度要随着系统上工作负载的增减而变化。如果有充足的空闲资 源, 并行度会上升;如果可用资源有限,并行度则会下降。这样就不会为机器强加一个固定的并行度。利用这 种方法,允许Oracle动态地增加或减少查询所需的并发资源量。
因此,我们只是通过以下ALTER TABLE命令来启用对这个表的并行查询:
big_table@ORA10GR1> alter table big_table parallel;
Table altered.

仅此而已。现在,对这个表的操作就会考虑并行查询。重新运行解释计划,可能看到以下结果:
big_table@ORA10GR1> explain plan for

2 select count(status) from big_table; Explained.
big_table@ORA10GR1> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
----------------------------------------- Plan hash value: 1651916128
----------------------------------------------------------------------------

|Id | Operation | Name |Cost(%CPU) | TQ

|IN-OUT |PQ Distrib |

----------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | 4465 (1) | | |

|

| 1 | SORT AGGREGATE | | | | |

|

| 2 | PX COORDINATOR | | | | |

|

| 3 | PX SEND QC (RANDOM) | :TQ10000 | |Q1,00 | P->S |QC (RAND) |

| 4 | SORT AGGREGATE | | |Q1,00 | PCWP |

|

| 5 | PX BLOCK ITERATOR | | 4465 (1) |Q1,00 | PCWC |

|

| 6 | TABLE ACCESS FULL | BIG_TABLE | 4465 (1) |Q1,00 | PCWP | |

----------------------------------------------------------------------------


注意 我们从这个计划输出中去掉了ROWS、BYTES和TIME这几列,以便能在一页篇幅内放下。不过,
查询的总时间是00:00:54,而不是先前对串行计划估计的00:06:29。要记住,这些只是估计,不 能保证肯定如此!另外,这是Oracle 10g的计划输出,Oracle9i的计划输出提供的细节更少(只 有4步,而不是7步),但是最终结果是一样的。
如果自下而上地读这个计划(从ID=6开始),它显示了图14-1中所示的步骤。全表扫描会分解为多 个较小的扫描(第5步)。每个扫描汇总其COUNT(STATUS)值(第4步)。这些子结果将传送给并行查询协
调器(第2 步和第3 步),它会进一步汇总这些结果(第1步),并输出答案。
如果在一个新启动的系统上执行这个查询(这个系统还没有执行其他任何并行执行),可以观察到以 下结果。在此使用Linux ps命令来找出并行查询进程(我们希望找不出这样的进程,因为系统开始时没有 执行任何并行执行),启用并行执行后再运行这个查询,然后再次查找出并行查询进程:
big_table@ORA10GR1> host ps -auxww | grep '^ora10gr1.*ora_p00._ora10g'
big_table@ORA10GR1> select count(status) from big_table;
COUNT(STATUS)
-------------
10000000
big_table@ORA10GR1> host ps -auxww | grep '^ora10gr1.*ora_p00._ora10g' ora10gr1 3411 35.5 0.5 1129068 12200 ? S 13:27 0:02 ora_p000_ora10gr1 ora10gr1 3413 28.0 0.5 1129064 12196 ? S 13:27 0:01 ora_p001_ora10gr1 ora10gr1 3415 26.0 0.5 1129064 12196 ? S 13:27 0:01 ora_p002_ora10gr1 ora10gr1 3417 23.3 0.5 1129044 12212 ? S 13:27 0:01 ora_p003_ora10gr1 ora10gr1 3419 19.5 0.5 1129040 12228 ? S 13:27 0:01 ora_p004_ora10gr1 ora10gr1 3421 19.1 0.5 1129056 12188 ? S 13:27 0:01 ora_p005_ora10gr1 ora10gr1 3423 19.0 0.5 1129056 12164 ? S 13:27 0:01 ora_p006_ora10gr1
ora10gr1 3425 21.6 0.5 1129048 12204 ? S 13:27 0:01 ora_p007_ora10gr1

可能看到,Oracle现在启动了8个并行执行服务器。如果你很好奇,想“看看”并行查询,使用两

个会话就能很容易地满足你的好奇心。在运行并行查询的会话中,我们先来确定这个会话的SID:
big_table@ORA10GR1> select sid from v$mystat where rownum = 1;
SID
----------
162
在另一个会话中,准备好要运行的查询:
ops$tkyte@ORA10GR1> select sid, qcsid, server#, degree
2 from v$px_session
3 where qcsid = 162

在SID=162的会话中开始并行查询之后不久,返回到第二个会话,运行这个查询:
4 /
SID QCSID SERVER# DEGREE
---------- ---------- ---------- ----------
145 162 1 8
150 162 2 8
147 162 3 8
151 162 4 8
146 162 5 8
152 162 6 8
143 162 7 8
144 162 8 8
162 162
9 rows selected.

在此可以看到,动态性能视图中的这9行(对应9个会话)中,并行查询会话(SID=162)的SID就

是这9 行的查询协调器SID(query coordinator SID, QCSID)。现在,这个会话“正在协调”或控制着这 些并行查询资源。可以看到每个会话都有自己的SID;实际上,每个会话都是一个单独的Oracle会话,执 行并行查询时从V$SESSION中也可以看出这一点:
ops$tkyte@ORA10GR1> select sid, username, program
2 from v$session
3 where sid in ( select sid
4 from v$px_session

5 where qcsid = 162 )
6 /
SID USERNAME PROGRAM
---------- ------------------------------ -------------------------------
143 BIG_TABLE oracle@dellpe (P005)
144 BIG_TABLE oracle@dellpe (P002)
145 BIG_TABLE oracle@dellpe (P006)
146 BIG_TABLE oracle@dellpe (P004)
147 BIG_TABLE oracle@dellpe (P003)
150 BIG_TABLE oracle@dellpe (P001)
151 BIG_TABLE oracle@dellpe (P000)
153 BIG_TABLE oracle@dellpe (P007)
162 BIG_TABLE sqlplus@dellpe (TNS V1-V3)
9 rows selected.
注意 如果你的系统中没有出现并行执行,就不要指望V$SESSION中会显示并行查询服务器。它们会
在V$PROCESS中,但是除非真正使用,否则不会建立会话。并行执行服务器会连接到数据库,但 是不会建立一个会话。关于会话和连接的区别,有关详细内容请参见第5章。
简而言之,并行查询就是这样工作的(实际上,一般的并行执行就以这种方式工作)。它要求一系列 并行执行服务器同心协力地工作,生成子结果,这些子结果可以传送给其他并行执行服务器做进一步的处 理,也可以传送给并行查询的协调器。
在这个特定的例子中,如所示的那样,BIG_TABLE分布在一个表空间的4 个不同的设备上(这个表空 间有4 个数据文件)。实现并行执行时,通常“最好”将数据尽可能地分布在多个物理设备上。可以通过多 种途径做到这一点:
跨磁盘使用RAID条纹(RAID striping); 使用ASM(利用其内置条纹);
使用分区将BIG_TABLE物理地隔离到多个磁盘上; 使用一个表空间中的多个数据文件,第二允许 Oracle在多个文件中为BIG_TABLE段分配区
段。
一般而言,如果能访问尽可能多的资源(CPU、内存和I/O), 并行执行就能最好地发挥作用。但这 并不是说:如果整个数据集都在一个磁盘上,就从并行查询得不到任何好处,并非如此;不过如果整个数 据集都在一个磁盘上, 可能确实不如使用多个磁盘那样能有更多收获。即使使用一个磁盘,在响应时间上 也可能可以得到一定的速度提升,原因在于:给定的一个并行执行服务器在统计行 时并不读取这些行,反 之亦然。所以,与执行串行相比,两个并行执行服务器可以在更短的时间内完成所有行的统计。
类似地,即使在一台单 CPU主机上,甚至也能得益于并行查询。一个串行SELECT COUNT(*)不太可能
100%地完全占用单CPU主机上的CPU,其部分时间会用于执行(和等待)磁盘的物理I/O。通过并行查询, 你就能充分利用主机上的资源(在这里就是CPU和I/O),而不论是什么资源。
最后一点又把我们带回到先前引用的Practical Oracle8i: Building Efficient Databases中的那 句话:并行查询本质上是不可扩缩的。如果在这台单CPU主机上利用两个并行执行服务器允许四个会话同 时执行查询,你可能会发现,与串行处理相比,响应时间反而更长了。需要一个稀有资源的进程越多,满 足所有请求所需的时间也越长。
而且要记住,并行查询需要保证两个前提。首先,你需要执行一个大任务,例如,一个长时间运行的 查询,这个查询的运行时间要分钟、小时或天为单位来度量,而不是秒或次秒。这说明,并行查询不能作 为典型OLTP系统的解决方案,因为在OLTP系统中,你不会执行长时间运行的任务。在这些系统上启用执 行通常是灾难性的。其次,你需要有充足的空闲资源,如CPU、I/O和内存。如果缺少任何一种资源,并行 查询可能会过度使用资源,这就会负面地影响总体性能和运行时间。
过去,对于许多数据仓库来说,人们总认为必须应用并行查询,这只是因为过去(例如,1995年) 数据仓库很稀少,通常只有一个很小、很集中的用户群。而在2005年的今天,到处都有数据仓库,而且数 据仓库的用户群与事务性系统的用户群同样庞大。这说明,在给定时刻,你可能没有足够的空闲资源来启 用这些系统上的并行查询。但这并不是说“这种情况下并行执行通常没有用”,而是说,并行执行更应该算 是一个DBA工具(在“并行DDL“一节将介绍),而不是一个并行查询工具。