Oracle 9i & 10g编程艺术-深入数据库体系结构——第14章:并行执行

 

第14章                      并行执行

并行执行(parallel execution)是Oracle企业版才有的特性(标准版中没有这个特性),最早于1994年在Oracle 7.1.6 中引入。所谓并行执行,是指能够将一个大型串行任务(任何DML,或者一般的DDL)物理地划分为多个较小的部分,这些较小的部分可以同时得到处理。Oracle中的并行执行正是模拟了我们在实际生活中经常见到的并发处理。你可能很少看到一个人单枪匹马地盖房子;更常见的是由多个团队并发地工作,迅速地建成房屋。采用这种方式,某些操作可以划分为较小的任务,从而并发地执行。例如,铺设管线和电路配线就可以同时进行,以减少整个工作所需的总时间。

Oracle中的并行执行遵循了几乎相同的逻辑。Oracle通常可以将某个大“任务”划分为较小的部分,并且并发地执行各个部分。例如,如果需要对一个大表执行全表扫描,那么Oracle完全可以建立4个并行会话(P001~P004)来一起执行完全扫描,每个会话分别读取表中一个不同的部分(既然这样能更快地完成任务,没有理由不这样做)。如果需要存储P001~P004扫描的数据,这个工作可以再由另外4个并行会话(P005~P008)来执行,最后它们可以将结果发送给这个查询的总体协调会话。

并行执行作为一个工具,如果使用得当,可以使某些操作的响应时间大幅改善,使速度呈数量级增长。但是,如果把并行执行当作一个“fast=true”型的提速开关,结果往往适得其反。在这一章中,我们并不打算明确地解释Oracle中如何实现并行查询,也不会介绍由并行操作可能得到的多种计划组合,在此不会讨论诸如此类的内容。我认为,这些内容已经在各种Oracle手册(Oracle Administrator’s GuideOracle Concepts Guide,特别是Oracle Data Warehousing Guide)中得到了很好的说明。这一章的目标只是让你对并行执行适用于哪些类型的问题(以及对哪些类型的问题不适用)有所了解。具体来讲,我们会介绍何时使用并行执行,在此之后,还将介绍下面的内容:

q         并行查询:这是指能使用多个操作系统进程或线程来执行一个查询。Oracle会发现能并行执行的操作(如全表扫描或大规模排序),并创建一个查询计划来实现)。

q         并行DMLPDML):这在本质上与并行查询很相似,但是PDML主要是使用并行处理来执行修改(INSERTUPDATEDELETEMERGE)。在这一章中,我们将介绍PDML,并讨论PDML所固有的一些限制。

q         并行DDL:并行DDL是指Oracle能并行地执行大规模的DDL操作。例如,索引重建、创建一个新索引、数据加载以及大表的重组等都可以使用并行处理。在我看来,这正是数据库中并行化的“闪光之处”,所以我们会用很多的篇幅重点讨论这方面内容。

q         并行恢复:这是指数据库能并行地执行实例(甚至介质)恢复,以减少从故障恢复所需的时间。

q         过程并行化:这是指能并行地运行所开发的代码。在这一章中,我会讨论两种过程并行化方法。第一种方法中,Oracle以一种对开发人员透明的方式并行地运行开发的PL/SQL代码(开发人员并不开放代码;而是由Oracle透明地为其并行执行代码)。此外还有另一种方法,我把它成为“DIY并行化”(do-it-yourself parallelism),即开发的代码本身被设计为要并行地执行。

1.1   何时使用并行执行

并行执行可能很神奇。一个过程原本需要执行数小时或者数天,通过使用并行执行,可能几分钟内就可以完成。将一个大问题分解为小部分,这在有些情况下可以显著地减少处理时间。不过,考虑并行执行时,要记住一个基本概念,这可能很有用。下面这句话选自Jonathan Lewis所著的Practical Oracle8i: Building Efficient Databases(Addison-Wesley, 2001),尽管很简短,但它对这个概念做了很好的总结:

并行查询(PARALLEL QUERY)选项本质上是不可扩缩的。

并行执行本质上是一个不可扩缩的解决方案,设计为允许单个用户或每个特定SQL语句占用数据库的所有资源。如果某个特性允许一个人使用所有可用的资源,倘若再允许两个人使用这个特性,就会遇到明显的竞争问题。随着系统上并发用户数的增加,要应付这些并发用户,系统上的资源(内存、CPUI/O)开始表现出有些勉为其难,此时能不能部署并行操作就有疑问了。例如,如果你有一台4CPU的主机,平均有32个用户同时执行查询,那你很可能不希望并行执行他们的操作。如果允许每个用户执行一个“并行度为2”的查询,这就会在一台仅有4CPU的主机上发生64个并发操作。即使并行执行之前这台机器原本不算疲于奔命,现在也很可能难逃此劫。

简单地说,并行执行也可能是一个很糟糕的想法。在许多情况下,应用并行处理后,可能只会带来资源占用的增加,因为并行执行试图使用所有可用的资源。在一个系统(如一个OLTP系统)中,如果资源必须由多个并发事务所共享,你就会观察到,并行执行可能导致响应时间增加。Oracle会避开某些在串行执行计划中能高效使用的执行技术,而采用诸如全面扫描等执行路径,希望能把较大的批量操作划分为多个部分,并且并行地完成这些部分,从而能优于串行计划。但是如果并行执行应用不当,不仅不能解决性能问题,反而会成为性能问题的根源。

所以,在应用并行执行之前,需要保证以下两点成立:

q         必须有一个非常大的任务,如对50GB数据进行全面扫描。

q         必须有足够的可用资源。在并行全面扫描50GB数据之前,你要确保有足够的空闲CPU(以容纳并行进程),还要有足够的I/O通道。50GB数据可能分布在多个物理磁盘上(而不只是一个物理磁盘),以允许多个并发读请求能同时发生,从磁盘到计算机应当有足够多的I/O通道,以便能并行地从磁盘获取数据,等等。

如果只有一个小任务(通常OLTP系统中执行的查询就是这种典型的小任务),或者你的可用资源不足(这也是OLTP系统中很典型的情况),其中CPUI/O资源通常已经得到最大限度的使用,那就根本不用考虑并行执行。

关于并行处理的类比

我经常使用一个类比来描述并行处理,解释为什么需要有一个大任务,还要求数据库中有足够多的空闲资源。这个类比是写一个有10章的详尽报告,每一章之间都要相互独立。例如,可以考虑你手上的这本书。本章与第9章是独立的,不必按先后顺序写这两章。

如何完成这两个任务?你认为并行处理对哪个任务有好处?

1.      一页的总结

在这个类比中,第一个任务是要完成只有一页的总结,这并不是一个大任务。你可以自己来写,也可以把这个任务安排给某个人。为什么呢?因为倘若将这个处理“并行化”,所需的工作量反而会超过你自己写这一页所需的工作。如果要实现“并行化”,你可能必须坐下来,想清楚应该有12段,并确定每一段不能依赖于其他段落,然后召开一个小组会议,选择12个人,向他们解释问题是什么,并安排每个人完成一段,然后作为一个协调员收集所有段落,按正确的顺序摆好,验证这些段落无误,然后打印报告。与你自己串行地写这一页相比,上述过程很可能需要花费更长的时间。对于这样一个小规模的项目,管理这么一大群人的开销远远超过了并行编写12段所能得到的收益。

数据库中的并行执行也是同样的道理。如果一个任务只需要几秒(或更短时间)就能串行地完成,引入并行执行后,相关的管理开销可能会让整个过程花费更长的时间。

2.      10章的报告

现在再来分析第二个任务。你希望你尽快地写出这个10章的报告,对此最慢的办法就是将所有工作都安排给某一个人(相信我说的话,因为我很清楚这一点,看看这本书就知道了!有时我真是希望能有15个“我”在同时写)。此时,你要召开会议,审查处理步骤,分配工作,然后作为协调员收集结果,完成最后的报告并提交。这个过程可能不是在1/10的时间内完成,而可能在1/8左右的时间内完成。同样,这么说有一个附加条件,你要有足够的空闲资源。如果你的员工很多,而且目前手头都没有什么具体工作,那么划分这个工作绝对是有意义的。

不过,假设你是经理,请考虑以下情况:你的员工可能手头有很多任务。在这种情况下,就必须谨慎对待这个大项目。你要保证不要让你的员工疲于奔命;你不希望他们过度疲劳,超过承受极限。如果你的资源(你的员工)无力处理,你就不能再分配更多的工作,否则他们肯定会辞职。如果你的员工已经是满负荷的,增加更多的工作只会导致所有进度都落空,所有项目都延迟。

Oracle中的并行执行也是一样。如果一个任务要花几分钟、几小时或者几天的时间执行,引入并行执行可能会使这个任务的运行快上8倍。但是,重申一遍,如果资源已经被过度使用(员工已经超负荷工作),就要避免引入并行执行,因为系统可能会变得更迟钝。尽管Oracle服务器进程不会递交“辞职书”,但它们可能会用完所有的RAM并失败,或者只会长时间地等待I/OCPU,看上去就好像没有做任何工作一样。

如果牢记这个类比(但要记住不能不合理地过分夸大类比),对于并行化是否有用,你就有了一个常识性的指导原则。如果有一个任务需要花几秒的时间完成,要想通过使用并行执行让它更快一些,就很可能成问题,而且通常可能适得其反。如果已经在过度使用资源(也就是说,你的资源已经得到充分利用),再增加并行执行很可能会使情况更糟,而不是更好。如果有一个相当大的任务,而且有充分的额外资源,并行执行就再合适不过了。在这一章中,我们将介绍充分利用执行资源的一些方法。

1.2   并行查询

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

big_table@ORA 10G > select count(status) from big_table;

通过并行查询,这个查询可以使用多个并行会话;将BIG_TABLE划分为较小的不重叠的部分(片);要求各个并行会话读取表并统计它那一部分中的行数。这个会话的并行查询协调器再从各个并行会话接收各个统计结果,进一步汇总这些结果,将最后的答案返回给客户应用。如果用图形来表示,这个过程如图14-1所示。

P000P001P002P003进程称为并行执行服务器(parallel execution server),有时也称为并行查询(parallel queryPQ)从属进程。各个并行执行服务器都是单独的会话,就像是专业服务器进程一样连接数据库。每个并行执行服务器分别负责扫描BIG_TABLE中一个部分(各个部分都不重叠),汇总其结果子集,将其输出发回给协调服务器(即原始会话的服务器进程),它再将这些子结果汇总为最终答案。

14-1    并行select count(status)示意图

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

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

注意      我们从这个计划输出中去掉了ROWSBYTESTIME这几列,以便能在一页篇幅内放下。不过,查询的总时间是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._ora 10g '

big_table@ORA10GR1> select count(status) from big_table;

 

COUNT(STATUS)

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

10000000

 

big_table@ORA10GR1> host ps -auxww | grep '^ora10gr1.*ora_p00._ora 10g '

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行的查询协调器SIDquery 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个数据文件)。实现并行执行时,通常“最好”将数据尽可能地分布在多个物理设备上。可以通过多种途径做到这一点:

q         跨磁盘使用RAID条纹(RAID striping);

q         使用ASM(利用其内置条纹);

q         使用分区将BIG_TABLE物理地隔离到多个磁盘上;

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值