并行执行就是同时开启多个进程/线程来完成同一个任务,并行执行的每个进程/线程都会消耗额外的硬件资源,所以并行执行的本质就是以额外的硬件资源消耗来换取执行时间的缩短。说白了就是把一个大任务拆分为多个小的子任务,并把该任务的执行方式由一个单进程/线程依次执行改成由多个进程/线程同时并发执行,而且每个子进程/线程只执行拆分后的子任务。
开启并行执行的几种方法:
会话级:
alter session force parallel dml;
alter session enable parallel ddl;
alter session disable parallel dml;
语句级:
alter session force parallel query parallel 2;
alter session disable parallel query;
对象级:
alter table t1 noparallel;
alter index idx_t1 parallel 3;
Oracle中的并行执行操作
在所有的操作中,并行度,取决于当前系统的Idle,切勿盲目使用!
1、并行查询
典型的查询如:全表扫描、快速索引全扫描、分区索引范围扫描,或者全部扫描的表连接(包括hash join、排序合并连接和嵌套循环连接)。
需要满足以下任意条件:
(1) SQL语句中有hint提示,比如parallel或parallel_index
(2) SQL语句中的对象呗设置了并行属性(DEGREE)
如:select /*+parallel(t1 2) */ * from t1;
2、并行DDL
典型的DDL语句如下:create table as select,create index,rebulid index,rebuild index partition,move/split/coalesce partition。这些语句如果并行执行,通常情况下都可以缩短执行时间。
示例:
create index index_name on table_name(aa,bb) parallel;
但是必须要注意的是:在并行执行完DDL语句后,可能会导致相关的对象默认并行度的变化
SQL> select index_name,degree from dba_indexes where index_name like 'IDX_T%'; INDEX_NAME DEGREE ------------------------------ ---------------------------------------- IDX_T1 1 IDX_T2 DEFAULT
使用并行创建后的并行度会有1变成DEFAULT。
DEFAULT的值:
For a single instance, DOP = PARALLEL_THREADS_PER_CPU x CPU_COUNT
For an Oracle RAC configuration, DOP = PARALLEL_THREADS_PER_CPU x CPU_COUNT x INSTANCE_COUNT
这意味着以后在访问索引IDX_T2时,CBO可能会考虑并行执行。这可能会引发一些问题,所以在并行执行完DDL语句后,通常应调整其并行度为1;
alter index idx_t2 noparallel;
这里特别需要注意的是,create table的时候却并不会修改其并行:
20:58:03 SQL> create table t1 as select * from dba_users; Table created. Elapsed: 00:00:00.11 20:58:17 SQL> create table t2 as select * from dba_users parallel; Table created. Elapsed: 00:00:00.05 select table_name,degree from dba_tables where table_name like 'T%'; TABLE_NAME DEGREE ------------------------------ -------------------- T1 1 T2 1
3、并行DML
Oracle中的一些DML语句也是可以并行执行的,比如:insert as select、update、delete和merge。也一样可以达到缩短执行时间的效果。
这里需要注意的是,真正并行执行需要使用alter session force parallel dml或者使用alter session enable parallel dml和加并行hint的DML联合使用。
21:19:28 SQL> select count(*) from t1; COUNT(*) ---------- 6120 Elapsed: 00:00:00.01 21:19:43 SQL> update t1 set username='z'; 6120 rows updated. Elapsed: 00:00:00.26 --接下去使用并行执行来看看 21:20:09 SQL> update /*+ parallel(t2) */ t2 set username='z'; 6120 rows updated. Elapsed: 00:00:00.35
启用并行hint后执行上述更新操作后发现执行时间反而更长。
简单的对比下SQL的执行:
update t1 set username='z' Plan hash value: 2927627013 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | UPDATE STATEMENT | | | | 29 (100)| | | 1 | UPDATE | T1 | | | | | | 2 | TABLE ACCESS FULL| T1 | 6706 | 111K| 29 (0)| 00:00:01 | --------------------------------------------------------------------------- Plan hash value: 3186537911 --------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib | --------------------------------------------------------------------------------------------------------------- | 0 | UPDATE STATEMENT | | | | 8 (100)| | | | | | 1 | UPDATE | T2 | | | | | | | | | 2 | PX COORDINATOR | | | | | | | | | | 3 | PX SEND QC (RANDOM)| :TQ10000 | 6797 | 112K| 8 (0)| 00:00:01 | Q1,00 | P->S | QC (RAND) | | 4 | PX BLOCK ITERATOR | | 6797 | 112K| 8 (0)| 00:00:01 | Q1,00 | PCWC | | |* 5 | TABLE ACCESS FULL| T2 | 6797 | 112K| 8 (0)| 00:00:01 | Q1,00 | PCWP | | ---------------------------------------------------------------------------------------------------------------
实际并行只发现在扫描表T2上,真正的更新操作并没有并行执行。
那么怎么样才能是DML语句可以使用并行执行呢?
alter session force parallel dml;
如果出现:
ERROR:
ORA-12841: Cannot alter the session parallel DML state within a transaction
则是因为,刚刚有执行过的语句还未commit或rollback,只需要commit或者rollback即可。
存储过程可以使用:
IF …
THEN COMMIT;
END IF;
or
IF …
THEN …
ELSE
ROLLBACK;
END IF;
再次执行:
21:37:58 SQL> alter session force parallel dml;
Session altered.
Elapsed: 00:00:00.00
21:38:04 SQL> update t1 set username=’z’;
6120 rows updated.
Elapsed: 00:00:00.16
Session altered.
Elapsed: 00:00:00.00
21:38:04 SQL> update t1 set username=’z’;
6120 rows updated.
Elapsed: 00:00:00.16
明显可以看出执行时间的缩短。
上述并行更新的执行计划为:
update t1 set username='z' Plan hash value: 3991856572 --------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib | --------------------------------------------------------------------------------------------------------------- | 0 | UPDATE STATEMENT | | | | 8 (100)| | | | | | 1 | PX COORDINATOR | | | | | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10000 | 6706 | 111K| 8 (0)| 00:00:01 | Q1,00 | P->S | QC (RAND) | | 3 | UPDATE | T1 | | | | | Q1,00 | PCWP | | | 4 | PX BLOCK ITERATOR | | 6706 | 111K| 8 (0)| 00:00:01 | Q1,00 | PCWC | | |* 5 | TABLE ACCESS FULL| T1 | 6706 | 111K| 8 (0)| 00:00:01 | Q1,00 | PCWP | | ---------------------------------------------------------------------------------------------------------------
可以看到,上述更新操作是真正并行执行的,并行部分不仅发生在全表扫描T1部分,而且也发生在更新部分。
除了上述的alter session force parallel dml之外,还可以使用alter session enable parallel dml和加并行hint的DML联合使用也同样可以达到真正并行执行DML操作。这里特别需要注意的是仅仅修改表的并行度和仅使用并行hint,都不能真正并行执行DML。
4、并行数据加载
SQL*Loader导入文本数据,针对同一个表,使用DIRECT方式和并行导入,如下所示:
sqlldr userid=user/pwd control=load1.ctl direct=true parallel=true
sqlldr userid=user/pwd control=load2.ctl direct=true parallel=true
这里启用2个会话,同时并行执行对同一个表的导入。当然源数据得手工拆分成两份。
指定加载:
sqlldr userid=user/pwd control=load1.ctl direct=true
指定并行和加载:
sqlldr userid=user/pwd control=load2.ctl direct=true parallel=true
5、并行收集统计信息
在使用DBMS_STATS包收集统计信息时指定相应的并行执行,可以一定程度上缓解统计信息收集时间过长的问题,实际上这也是DBMS_STATS包比analyze命令的强大之处。
如:exec DBMS_STATS.GATHER_TABLE_STATS(ownname=>’Z’,tabname=>’T1′,ESTIMATE_PERCENT=>5,method_opt=>’for all columns size 1′,cascade=>true,force=>true,degree=>8);
这里特别要注意的是,degree值取决于系统的idle,切勿盲目的。