《oracle编程艺术:深入数据库体系结构》之 十四 并行执行

故事扮演 提交于 2019-12-03 03:55:42

所谓并行执行,是指能够将一个大型串行任务(任何DML,或者一般的DDL)物理地划分为多个较小的部分,这些较小的部分可以同时得到处理。

1 何时使用并行执行

并行执行本质上是一个不可扩缩的解决方案,设计为允许单个用户或每个特定SQL语句占用数据库的所有资源。如果某个特性允许一个人使用所有可用的资源,倘若再允许两个人使用这个特性,就会遇到明显的竞争问题。

在应用并行执行之前,需要保证以下两点成立:
必须有一个非常大的任务,如对50GB数据进行全面扫描。
必须有足够的可用资源。在并行全面扫描50GB数据之前,你要确保有足够的空闲CPU(以容纳并行进程),还要有足够的I/O通道,等等。

2 并行查询

scott@ORCL>explain plan for
  2  select count(status) from big_table;

已解释。

scott@ORCL>select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------
Plan hash value: 599409829

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

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

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

|   0 | SELECT STATEMENT   |           |     1 |     7 | 47422   (1)| 00:09:30 |

|   1 |  SORT AGGREGATE    |           |     1 |     7 |            |          |

|   2 |   TABLE ACCESS FULL| BIG_TABLE |    11M|    76M| 47422   (1)| 00:09:30 |

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


已选择9行。

这是一个典型的串行计划。这里不涉及并行化,因为我们没有请求启用并行查询,而默认情况下并不启用并行查询。

启用并行查询有多种方法,可以直接在查询中使用一个提示,或者修改表,要求考虑并行执行路径。

可以具体指定这个表的执行路径中要考虑的并行度。例如,可以告诉Oracle:“我们希望你在创建这个表的执行计划时使用并行度4”:

scott@ORCL>alter table big_table parallel 4;

表已更改。

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

scott@ORCL>alter table big_table parallel;

表已更改。

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

scott@ORCL>explain plan for
  2  select count(status) from big_table;

已解释。

scott@ORCL>select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------
Plan hash value: 2894119656

--------------------------------------------------------------------------------
---------------------------------
| Id  | Operation              | Name      | Rows  | Bytes | Cost (%CPU)| Time
   |    TQ  |IN-OUT| PQ Distrib |
--------------------------------------------------------------------------------
---------------------------------
|   0 | SELECT STATEMENT       |           |     1 |     7 |  6574   (1)| 00:01:
19 |        |      |            |
|   1 |  SORT AGGREGATE        |           |     1 |     7 |            |
   |        |      |            |
|   2 |   PX COORDINATOR       |           |       |       |            |
   |        |      |            |
|   3 |    PX SEND QC (RANDOM) | :TQ10000  |     1 |     7 |            |
   |  Q1,00 | P->S | QC (RAND)  |
|   4 |     SORT AGGREGATE     |           |     1 |     7 |            |
   |  Q1,00 | PCWP |            |
|   5 |      PX BLOCK ITERATOR |           |    11M|    76M|  6574   (1)| 00:01:
19 |  Q1,00 | PCWC |            |
|   6 |       TABLE ACCESS FULL| BIG_TABLE |    11M|    76M|  6574   (1)| 00:01:
19 |  Q1,00 | PCWP |            |
--------------------------------------------------------------------------------
---------------------------------

已选择13行。

并行查询就是这样工作的,它要求一系列并行执行服务器同心协力地工作,生成子结果,这些子结果可以传送给其他并行执行服务器做进一步的处理,也可以传送给并行查询的协调器。

在这个特定的例子中,BIG_TABLE分布在一个表空间的4个不同的设备上(这个表空间有4个数据文件)。实现并行执行时,通常“最好”将数据尽可能地分布在多个物理设备上。可以通过多种途径做到这一点:

跨磁盘使用RAID条纹(RAID striping);
使用ASM(利用其内置条纹);
使用分区将BIG_TABLE物理地隔离到多个磁盘上;
使用一个表空间中的多个数据文件,第二允许Oracle在多个文件中为BIG_TABLE段分配区段。

3 并行DML

我们可以观察到为并行执行服务器创建的各个独立事务。

由于PDML采用的一种伪分布式的实现,因此存在一些限制:

PDML操作期间不支持触发器。因为触发器可能会向更新增加大量开销。

PDML期间,不支持某些声明方式的引用完整性约束,因为表中的每一片(部分)会在单独的会话中作为单独的事务进行修改。

在提交或回滚之前,不能访问用PDML修改的表。

PDML不支持高级复制(因为复制特性的实现要基于触发器)。

不支持延迟约束(也就是说,采用延迟模式的约束)。

如果表是分区的,PDML只可能在有位图索引或LOB列的表上执行,而且并行度取决于分区数。在这种情况下,无法在分区内并行执行一个操作,因为每个分区只有一个并行执行服务器来处理。

执行PDML时不支持分布式事务。

PDML不支持聚簇表。

4 并行DDL

以下SQL DDL命令允许”并行化“:

CREATE INDEX:多个并行执行服务器可以扫描表、对数据排序,并把有序的段写出到索引结构。

CREATE TABLE AS SELECT:执行SELECT的查询可以使用并行查询来执行,表加载本身可以并行完成。

ALTER INDEX REBUILD:索引结构可以并行重建。

ALTER TABLE MOVE:表可以并行移动。

ALTER TABLE SPLIT|COALESCE PARTITION:单个表分区可以并行地分解或合并。

ALTER INDEX SPLIT PARTITION:索引分区可以并行地分解。

前4个命令还适用于单个的表/索引分区,可以并行地MOVE一个表的单个分区。

并行DDL才是Oracle中并行执行最突出的优点。并行查询主要是为最终用户设计的,那么并行DDL则是为DBA/开发人员设计的。

4.1 并行DDL和使用外部表的数据加载

我们只是使用一个“真实“表(而不是外部表),由此加载另一个表,这类似于许多人在数据仓库中使用暂存表(staging table)加载数据的做法:
(1) 使用某个抽取、转换、加载(extract, transform, load, ETL)工具来创建输入文件。
(2) 将执行输入文件加载到暂存表。
(3) 使用对这些暂存表的查询加载一个新表。

使用前面的BIG_TABLE(启用了并行),将把这个表联结到第二个表USER_INFO,其中包含ALL_USERS字典视图中与OWNER相关的信息。将这个信息逆规范化为一个平面结构。

首先创建USER_INFO表,启用并行操作,然后生成这个表的统计信息:

scott@ORCL>create table user_info as select * from all_users;

表已创建。

scott@ORCL>alter table user_info parallel;

表已更改。

scott@ORCL>exec dbms_stats.gather_table_stats( user, 'USER_INFO' );

PL/SQL 过程已成功完成。


scott@ORCL>alter table big_table parallel;

表已更改。

scott@ORCL>explain plan for
  2  create table new_table parallel
  3  as
  4  select a.*, b.user_id, b.created user_created
  5  from big_table a, user_info b
  6  where a.owner = b.username ;

已解释。

scott@ORCL>select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------
Plan hash value: 3867074464

--------------------------------------------------------------------------------
-----------------------------------
| Id  | Operation                | Name      | Rows  | Bytes | Cost (%CPU)| Time
     |    TQ  |IN-OUT| PQ Distrib |
--------------------------------------------------------------------------------
-----------------------------------
|   0 | CREATE TABLE STATEMENT   |           |    11M|  1327M| 11266   (1)| 00:0
2:16 |        |      |            |
|   1 |  PX COORDINATOR          |           |       |       |            |
     |        |      |            |
|   2 |   PX SEND QC (RANDOM)    | :TQ10001  |    11M|  1327M|  6598   (1)| 00:0
1:20 |  Q1,01 | P->S | QC (RAND)  |
|   3 |    LOAD AS SELECT        | NEW_TABLE |       |       |            |
     |  Q1,01 | PCWP |            |
|*  4 |     HASH JOIN            |           |    11M|  1327M|  6598   (1)| 00:0
1:20 |  Q1,01 | PCWP |            |
|   5 |      PX RECEIVE          |           |    39 |   741 |     2   (0)| 00:0
0:01 |  Q1,01 | PCWP |            |
|   6 |       PX SEND BROADCAST  | :TQ10000  |    39 |   741 |     2   (0)| 00:0
0:01 |  Q1,00 | P->P | BROADCAST  |
|   7 |        PX BLOCK ITERATOR |           |    39 |   741 |     2   (0)| 00:0
0:01 |  Q1,00 | PCWC |            |
|   8 |         TABLE ACCESS FULL| USER_INFO |    39 |   741 |     2   (0)| 00:0
0:01 |  Q1,00 | PCWP |            |
|   9 |      PX BLOCK ITERATOR   |           |    11M|  1119M|  6585   (1)| 00:0
1:20 |  Q1,01 | PCWC |            |
|  10 |       TABLE ACCESS FULL  | BIG_TABLE |    11M|  1119M|  6585   (1)| 00:0
1:20 |  Q1,01 | PCWP |            |
--------------------------------------------------------------------------------
-----------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("A"."OWNER"="B"."USERNAME")

已选择22行。

如果从第4步向下看,这些就是查询(SELECT)部分。BIG_TABLE的扫描和与USER_INFO的散列联结是并行执行的,并将各个子结果加载到表的某个部分中。每个并行执行服务器完成其联结和加载工作后,会把其结果发送给查询协调器。在这里,结果只是提示“成功“还是”失败“,因为工作已经执行。

仅此而已,利用并行直接路径加载,使得问题变得很容易。对于这些操作,最重要的是要考虑如何使用空间(或不使用)。有一种称为区段截断(extent trimming)的副作用相当重要。

4.2 并行DDL和区段截断

并行DDL依赖于直接路径操作。数据由一个操作(如CREATE TABLE AS SELECT)来创建新的区段,并直接写入这些区段,数据直接从查询写到磁盘(放在这些新分配的区段中)。每个并行执行服务器执行自己的部分CREATE TABLE AS SELECT工作,并且都会写至自己的区段。INSERT /*+ APPEND */(直接路径插入)会在一个段的HWM“之上“写,每个并行执行服务器再写至其自己的一组区段,而不会与其他并行执行服务器共享。因此,如果执行一个并行CREATE TABLE AS SELECT,并使用4个并行执行服务器来创建表,就至少有4个分区,可能还会更多。每个并行执行服务器会分配其自己的区段,向其写入,等填满时,再分配另一个新的区段,并行执行服务器不会使用由其他并行执行服务器非品牌的区段。

区段截断和本地管理表空间

UNIFORM SIZE是指表空间中的每个区段大小总是完全相同;AUTOALLOCATE则表示Oracle会使用一种内部算法来确定每个区段应该是多大。

如果使用UNIFORM SIZE,Oracle就不能执行区段截断。所有区段都只能有惟一一种大小,不能有任何区段小于(或大于)这个大小。

AUTOALLOCATE区段支持区段截断,AUTOALLOCATE方法使用一些特定大小的区段,而且能使用不同大小的空间。利用这种算法,一段时间后将允许使用表空间中的所有空闲空间。在字典管理的表空间中,如果请求一个100MB区段,倘若Oracle只找到了99MB的自由区段,请求还是会失败(尽管离要求只差了一点点)。与字典管理表空间不同,有AUTOALLOCATE区段的本地管理表空间可以更为灵活。为了试图使用所有空闲空间,它可以减小所请求的空间大小。

这里将建立一个外部表,它能用于并行直接路径加载。为了讨论区段截断,我们需要建立加载示例,然后在不同的条件下执行加载,并分析结果。

 

待续P762

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!