alter session disable parallel dml; -- parallel 을 disable 한다.
update /*+ parallel(tb_cus 8) */ t_big_table
set owner = 'hhh';
-- 실행결과 DBMS_XPLAN
SQL_ID 2nqb0hxvwurs1, child number 0
-------------------------------------
update /*+ parallel(tb_cus 8) */ t_big_table set owner = 'hhh'
Plan hash value: 2407949350
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
--------------------------------------------------------------------------------------------------------
| 1 | UPDATE | T_BIG_TABLE | 3 | | 0 |00:00:02.85 | 74046 | 79 |
| 2 | PX COORDINATOR | | 3 | | 99035 |00:00:00.46 | 100 | 79 |
| 3 | PX SEND QC (RANDOM)| :TQ10000 | 0 | 46981 | 0 |00:00:00.01 | 0 | 0 |
| 4 | PX BLOCK ITERATOR | | 0 | 46981 | 0 |00:00:00.01 | 0 | 0 |
|* 5 | TABLE ACCESS FULL| T_BIG_TABLE | 0 | 46981 | 0 |00:00:00.01 | 0 | 0 |
--------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access(:Z>=:Z AND :Z<=:Z)
분배방법 | 설명 |
---|---|
Hash | 조인기준컬럼을 Hash Function 을 이용하여 양쪽 테이블을 Mapping 하는 방식임. 조인컬럼 기준으로 각각의 Temp 성 매핑테이블을 만들고 마지막에 Join 하는 방식이다. |
Broadcast | 조인된 양쪽테이블에서 한쪽 테이블의 모든 Row를 다른쪽 테이블을 Scan 시에 분배하는 방식이다. BroadCast 받는 Table의 Scan 시에 자동으로 조인이 되므로 따로 Join Operation 이 필요가 없다. 하지만 Broadcast 하는측 테이블의 사이즈가 커지면 Parallel Slave 마다 Outer Table 을 반복적으로 BroadCast 해야 하므로 비효율이 커진다. |
Partition | 파티션을 이용하여 조인이된 양쪽테이블의 Row 를 분배한다. 2개의 조인된 테이블 중에서 조인컬럼을 기준으로 반드시 한쪽 테이블은 파티션이 되어 있어야한다. 파티션이 안된 테이블을 조인컬럼을 기준으로 파티션하여 Row 를 분배하는 방식이다. 이분배방식은 Partition Wise Join 과 관계가 있다. |
None | 이미 조인컬럼기준으로 파티션 된 테이블은 Row 가 파티션기준으로 자동으로 분배되거나 Broadcast 방식일 경우 분배를 받는쪽 테이블의 Row 는 따로 분배가 필요 없으므로 None 으로 표현된다. |
Outer Table | Inner Table | 설명 |
---|---|---|
HASH | HASH | 양쪽 테이블의 사이즈가 비슷하고 Hash Join 이나 Sort Merge 조인을 사용할때 권장된다. |
BROADCAST | NONE | Outer Table 의 사이즈와 Inner Table 의 사이즈를 비교하여 Outer 테이블의 사이즈가 훨씬적을때 권장된다. 예를들면 코드 테이블과 대용량 테이블을 조인할때 적격이다. 왜냐하면 Inner Table 의 Granule 갯수 만큼 Outer 테이블의 Row 가 반복해서 제공되어야 하기 때문에 Broadcast 하는쪽의 테이블이 크면 I/O 양이 급격히 늘어난다. |
NONE | BROADCAST | BROADCAST,NONE(바로위) 방법과 같으나 순서가 정반대 이다. 다시말해 Inner 테이블이 Broadcast 된다. Outer Table 의 사이즈와 Inner Table 의 사이즈를 비교하여 Inner 테이블의 사이즈가 훨씬적을때 권장된다. \--> Outer 가 Driving 되는 Hash Join 을 사용시 최악의 Combination 임. |
PARTITION | NONE | Outer 테이블을 조인된 컬럼기준으로 Partition을 하여 Row 를 분배하며 Partition Wise 조인을 한다. |
NONE | PARTITION | Inner 테이블을 조인된 컬럼기준으로 Partition을 하여 Row 를 분배하며 Partition Wise 조인을 한다. |
NONE | NONE | 조인이되는 양측의 테이블이 이미 조인컬럼 기준으로 파티션이 되어 있을때 따로 분배가 필요없으므로 이런 Combination 이 발생한다.(양측 테이블이 파티션 기준으로 분배된다.) |
/\*+ PQ_DISTRIBUTE(inner 테이블명 outer_distribution, inner_distribution) \*/ |
---|
위의 힌트에서 보듯이 Inner 테이블명이나 Alias 를 먼저적고 Row 분배방식의 Combination 을 작성하면 된다.
create table emp_1
as
with a as
(select /*+ materialize */ level + 10000000 as empno,
chr(mod(level,90)) as big_ename, chr(mod(level,90)) as big_addr
from dual
connect by level <= 100) -- 실제 블로그에는 100000이나 너무 오래걸려 줄였음 ㅋ
select empno,
lpad(big_ename, 3000,big_ename) as big_ename ,
lpad(big_addr, 3000,big_addr) as big_addr
from a ;
create table emp_2
as
select * from emp_1 ;
EXEC dbms_stats.gather_table_stats(user,'EMP_1');
EXEC dbms_stats.gather_table_stats(user,'EMP_2');
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| TQ |IN-OUT| PQ Distrib |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 45 | 527K| 8 (13)| | | |
| 1 | PX COORDINATOR | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10002 | 45 | 527K| 8 (13)| Q1,02 | P->S | QC (RAND) |
|* 3 | HASH JOIN BUFFERED | | 45 | 527K| 8 (13)| Q1,02 | PCWP | |
| 4 | PX RECEIVE | | 45 | 263K| 4 (0)| Q1,02 | PCWP | |
| 5 | PX SEND HASH | :TQ10000 | 45 | 263K| 4 (0)| Q1,00 | P->P | HASH |
| 6 | PX BLOCK ITERATOR | | 45 | 263K| 4 (0)| Q1,00 | PCWC | |
|* 7 | TABLE ACCESS FULL| EMP_1 | 45 | 263K| 4 (0)| Q1,00 | PCWP | |
| 8 | PX RECEIVE | | 100 | 586K| 4 (0)| Q1,02 | PCWP | |
| 9 | PX SEND HASH | :TQ10001 | 100 | 586K| 4 (0)| Q1,01 | P->P | HASH |
| 10 | PX BLOCK ITERATOR | | 100 | 586K| 4 (0)| Q1,01 | PCWC | |
| 11 | TABLE ACCESS FULL| EMP_2 | 100 | 586K| 4 (0)| Q1,01 | PCWP | |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("T1"."EMPNO"="T2"."EMPNO")
7 - filter("T1"."BIG_ENAME">'1')
explain plan for
SELECT /*+ full(t1) full(t2) parallel(t1 8) parallel(t2 8) leading(t1) use_hash(t2) PX_JOIN_FILTER(t2) */
*
FROM emp_1 t1,
emp_2 t2
WHERE t1.empno = T2.empno
and t1.BIG_ENAME > '1' -- 이 조건이 없으면 Parallel Join Filter 는 결코 작동하지 않는다.(없으면 트릭 : t1.empno > 0 )
;
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| TQ |IN-OUT| PQ Distrib |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 45 | 527K| 8 (13)| | | |
| 1 | PX COORDINATOR | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10002 | 45 | 527K| 8 (13)| Q1,02 | P->S | QC (RAND) |
|* 3 | HASH JOIN BUFFERED | | 45 | 527K| 8 (13)| Q1,02 | PCWP | |
| 4 | PX JOIN FILTER CREATE| :BF0000 | 45 | 263K| 4 (0)| Q1,02 | PCWP | |
| 5 | PX RECEIVE | | 45 | 263K| 4 (0)| Q1,02 | PCWP | |
| 6 | PX SEND HASH | :TQ10000 | 45 | 263K| 4 (0)| Q1,00 | P->P | HASH |
| 7 | PX BLOCK ITERATOR | | 45 | 263K| 4 (0)| Q1,00 | PCWC | |
|* 8 | TABLE ACCESS FULL| EMP_1 | 45 | 263K| 4 (0)| Q1,00 | PCWP | |
| 9 | PX RECEIVE | | 100 | 586K| 4 (0)| Q1,02 | PCWP | |
| 10 | PX SEND HASH | :TQ10001 | 100 | 586K| 4 (0)| Q1,01 | P->P | HASH |
| 11 | PX JOIN FILTER USE | :BF0000 | 100 | 586K| 4 (0)| Q1,01 | PCWP | |
| 12 | PX BLOCK ITERATOR | | 100 | 586K| 4 (0)| Q1,01 | PCWC | |
| 13 | TABLE ACCESS FULL| EMP_2 | 100 | 586K| 4 (0)| Q1,01 | PCWP | |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("T1"."EMPNO"="T2"."EMPNO")
8 - filter("T1"."BIG_ENAME">'1')
select filtered, probed, proved - filtered as sent
from v$sql_join_filter
where qc_session_id = sys_context('userenv', 'sid');
SELECT /*+ GATHER_PLAN_STATISTICS FULL(A) parallel(A 4) GBY_PUSHDOWN */
prod_id, cust_id, COUNT (*) cnt
FROM sh.sales A
GROUP BY prod_id, cust_id;
GBY_PUSHDOWN 란 힌트를 사용하였으며 아래의 Plan 에서 보는바와 같이 성공적으로 Group By Push Down 이 수행되었다
---------------------------------------------------------------------------
| Id | Operation | Name | E-Rows | Used-Mem | Used-Tmp|
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | |
| 1 | PX COORDINATOR | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10001 | 359K| | |
| 3 | HASH GROUP BY | | 359K| 2197K (0)| |
| 4 | PX RECEIVE | | 359K| | |
| 5 | PX SEND HASH | :TQ10000 | 359K| | |
| 6 | HASH GROUP BY | | 359K| 3284K (1)| 1024 | -- Group By 가 수행. temp tablespace사용
| 7 | PX BLOCK ITERATOR | | 918K| | |
|* 8 | TABLE ACCESS FULL| SALES | 918K| | |
---------------------------------------------------------------------------
\
SELECT /*+ GATHER_PLAN_STATISTICS FULL(A) parallel(A 4) NO_GBY_PUSHDOWN */
prod_id, cust_id, COUNT (*) cnt
FROM sh.sales A
GROUP BY prod_id, cust_id;
----------------------------------------------------------------
| Id | Operation | Name | E-Rows | Used-Mem |
----------------------------------------------------------------
| 0 | SELECT STATEMENT | | | |
| 1 | PX COORDINATOR | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10001 | 359K| |
| 3 | HASH GROUP BY | | 359K| 4492K (0)|
| 4 | PX RECEIVE | | 918K| |
| 5 | PX SEND HASH | :TQ10000 | 918K| |
| 6 | PX BLOCK ITERATOR | | 918K| |
|* 7 | TABLE ACCESS FULL| SALES | 918K| |
----------------------------------------------------------------
*********************************
Number of join permutations tried: 1
*********************************
GROUP BY adjustment factor: 0.707107
GROUP BY cardinality: 359386.000000, TABLE cardinality: 918843.000000
Costing group-by pushdown:
SORT ressource Sort statistics
Sort width: 598 Area size: 552960 Max Area size: 104857600
Degree: 1
Blocks to Sort: 563 Row size: 20 Total Rows: 229711
Initial runs: 2 Merge passes: 1 IO Cost / pass: 306
Total IO sort cost: 869 Total CPU sort cost: 230852464
Total Temp space used: 4629000
Distribution cost: resc_cpu: 91885309 resp_cpu: 22971327
Costing final group-by:
Subtracting no-pushdown group-by:
SORT ressource Sort statistics
... 이후 생략
- 강좌 URL : http://www.gurubee.net/lecture/3470
- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^
- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.