所谓并行执行,是指能够将一个大型串行任务(任何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
来源:oschina
链接:https://my.oschina.net/u/1862478/blog/2244807