# 요약
  ■ 병렬처리 
# Serial Full Table Scan
# Parallel Full Table Scan, Direct Path Read 방식


■ IN-OUT 오퍼레이션
# order by
# group by push down
# no_gby_pushdown

v$pq_tqstat

■ Full Partition Wise Join
# pq_distribute(e,none,none)

■ Partial Partition Wise Join
# pq_distribute(e, none, partition)
# pq_distribute(e,partition,none)

■ 동적 파티셔닝
# pq_distribute(e,hash,hash)

■ Broadcast
# pq_distribute(e,broadcast,none)

 

 


 

 

■ 병렬처리
- SQL문이 수행해야할 작업 범위를 여러개의 작은 단위로 나누어 여러 서버 프로세스가 동시에 처리하는 작업을 의미한다.

[ Sample Table : hr.emp ]

HR@ora11g> drop table hr.emp purge;

Table dropped.

HR@ora11g> create table hr.emp nologging
as select rownum as employee_id, last_name, first_name, hire_date, job_id, salary, manager_id, department_id
from hr.employees e, (select level as id from dual connect by level <= 1000);

Table created.

--- 대용량 자료는 degree 숫자를 더 높여 통계 수집을 빨리 끝내버려도 좋다.
HR@ora11g> exec dbms_stats.gather_table_stats('hr', 'emp', degree => 2)

PL/SQL procedure successfully completed.

HR@ora11g> select num_rows, blocks, avg_row_len, logging from user_tables where table_name = 'EMP';

  NUM_ROWS     BLOCKS AVG_ROW_LEN LOGGING
---------- ---------- ----------- -------
    107000        760          45 NO

--- Set Logging Mode
HR@ora11g> alter table hr.emp logging;

Table altered.

HR@ora11g> select num_rows, blocks, avg_row_len, logging from user_tables where table_name = 'EMP';

  NUM_ROWS     BLOCKS AVG_ROW_LEN LOGGING
---------- ---------- ----------- -------
    107000        760          45 YES

--- 행 운반단위
HR@ora11g> show arraysize
arraysize 15

HR@ora11g> set arraysize 1000

--- 통계 정보 모두 수집 
HR@ora11g> alter session set statistics_level = all;

Session altered.

 

 

 

# Serial Full Table Scan

HR@ora11g> select /*+ full(e) */ count(*) from emp e;

  COUNT(*)
----------
    107000

HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      1 |00:00:00.01 |     744 |
|   1 |  SORT AGGREGATE    |      |      1 |      1 |      1 |00:00:00.01 |     744 |
|   2 |   TABLE ACCESS FULL| EMP  |      1 |    107K|    107K|00:00:00.01 |     744 |
-------------------------------------------------------------------------------------

 

 

# Parallel Full Table Scan, Direct Path Read 방식

HR@ora11g> select /*+ full(e) parallel(e 2) */ count(*) from emp e;

> parallel(e 2) = ('hr', 'emp', degree => 2)
> count(*) 그룹함수

--- allstats last parallel
HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last parallel'));

--------------------------------------------------------------------------------------------------------------------------
| Id  | Operation              | Name     | Starts | E-Rows |    TQ  |IN-OUT| PQ Distrib | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |          |      1 |        |        |      |            |      1 |00:00:00.11 |       5 |
|   1 |  SORT AGGREGATE        |          |      1 |      1 |        |      |            |      1 |00:00:00.11 |       5 |
|   2 |   PX COORDINATOR       |          |      1 |        |        |      |            |      2 |00:00:00.11 |       5 |
|   3 |    PX SEND QC (RANDOM) | :TQ10000 |      0 |      1 |  Q1,00 | P->S | QC (RAND)  |      0 |00:00:00.01 |       0 |
|   4 |     SORT AGGREGATE     |          |      0 |      1 |  Q1,00 | PCWP |            |      0 |00:00:00.01 |       0 |
|   5 |      PX BLOCK ITERATOR |          |      0 |    107K|  Q1,00 | PCWC |            |      0 |00:00:00.01 |       0 |
|*  6 |       TABLE ACCESS FULL| EMP      |      0 |    107K|  Q1,00 | PCWP |            |      0 |00:00:00.01 |       0 |
--------------------------------------------------------------------------------------------------------------------------

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

   6 - access(:Z>=:Z AND :Z<=:Z)

> +0.11 속도에 실망하지 말자. 서버에서는 빠르다.

 

 

- QC : Query Coordinator
    - 병렬 SQL문을 발생한 세션
    - 작업을 지시하고 일이 잘 진행되는지 관리, 감독하는 역할

- 병렬 서버 프로세스
    - 실제 작업을 수행하는 개별 세션
    - 병렬 서버 = 병렬 프로세스 = 병렬 슬레이브

- 병렬 서버 풀 (Parallel Execution Server Pool)
    - 병렬 처리 시 서버풀에 있는 프로세스부터 사용
    - 부족분은 오라클이 추가 생성
    - 생성할 수 있는 최대 병렬 서버 개수 : parallel_max_servers
    - 병렬 처리가 발생하지 않더라도 parallel_min_servers 파라미터에 지정된 개수만큰 병렬 프로세스를 유지한다.
        - 이유 : 병렬 처리 시 서버 프로세스를 띄우는 부하를 줄이기 위해서

SYS@ora11g> show parameter Parallel_%_servers

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
parallel_max_servers                 integer     80
parallel_min_servers                 integer     0

 

- 병렬 서버 집합(server set) 할당
    - 병렬도(DOP(Degree Of Parallelism))와 오퍼레이션 종류에 따라 한개 또는 두개의 병렬서버 집합 할당
    - 서버풀(Parallel Execution Server Pool)로부터 필요한 만큼 서버프로스세스 확보
    - 부족분은 새로 생성

 

> 병렬 서버 생성을 위해 작업 > 메모리 필요 > Large Pool에서 빌려쓴다.

 

> parallel 병렬처리는 대용량 작업을 많이해서 메모리를 거치면 더 느려진다.
> 최적화 Parallel + Join + Full + Hash

 

 




■ IN-OUT 오퍼레이션

- Blank     : serial, 직렬 작업

- P->S      : Parallel_To_Serial
              각 병렬 서버 프로세스가 처리한 데이터를 QC에게 전송
              
- QC (RAND) : Order by가 없을 때에 표시되며 병렬 프로세스들이 무순위로 QC에게 데이터를 전송

- PCWP      : Parallel_Combinded_With_Parent
              한 서버집합이 현재 스텝과 그 부모 스텝을 모두 처리
              
- PCWC      : Parallel_Combinded_With_Child
              한 서버집합이 현재 스텝과 그 자식 스텝을 모두 처리
              
- S->P      : Parallel_From_Serial
              QC가 읽은 데이터를 데이터 큐를 통해 병렬 서버프로세스에게 전송
              
- P->P      : Parallel_To_Parallel
              데이터 정렬, 그룹핑하거나 조인을 위해 동적으로 파티셔닝할 때 사용
              첫번째 병렬 서버 집합이 읽거나 가고한 데이터를 두번째 병렬 서버 집합에 전송

> PCWP, PCWC 보다 P <-> S 간의 작업이 더 중요하다.


# 병렬도(degree) = 2
# 서버 프로세스 = 병렬도 * 2 = 2 * 2 = 4 = p000 ~ p003
# 통신 채널 수 = 병렬도 ** 2 = 2 ** 2 = 4


# order by 절 사용

HR@ora11g> select /*+ full(e) parallel(e 2) */ * from emp e order by last_name;

HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last parallel'));

------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation               | Name     | Starts | E-Rows |    TQ  |IN-OUT| PQ Distrib | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |          |      1 |        |        |      |            |    107K|00:00:00.16 |       5 |       |       |          |
|   1 |  PX COORDINATOR         |          |      1 |        |        |      |            |    107K|00:00:00.16 |       5 |       |       |          |
|   2 |   PX SEND QC (ORDER)    | :TQ10001 |      0 |    107K|  Q1,01 | P->S | QC (ORDER) |      0 |00:00:00.01 |       0 |       |       |          |
|   3 |    SORT ORDER BY        |          |      0 |    107K|  Q1,01 | PCWP |            |      0 |00:00:00.01 |       0 |  7902K|  1110K| 3511K (0)|
|   4 |     PX RECEIVE          |          |      0 |    107K|  Q1,01 | PCWP |            |      0 |00:00:00.01 |       0 |       |       |          |
|   5 |      PX SEND RANGE      | :TQ10000 |      0 |    107K|  Q1,00 | P->P | RANGE      |      0 |00:00:00.01 |       0 |       |       |          |
|   6 |       PX BLOCK ITERATOR |          |      0 |    107K|  Q1,00 | PCWC |            |      0 |00:00:00.01 |       0 |       |       |          |
|*  7 |        TABLE ACCESS FULL| EMP      |      0 |    107K|  Q1,00 | PCWP |            |      0 |00:00:00.01 |       0 |       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------------------

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

   7 - access(:Z>=:Z AND :Z<=:Z)


# 병렬 작업 정보
- parallel + [order by|group by|join] 시 작동

HR@ora11g> select tq_id, server_type, process, num_rows, bytes, waits
           from v$pq_tqstat
           order by tq_id, decode(substr(server_type,1,4),'Rang',1,'Prod',2,'Cons',3),process;

     TQ_ID SERVER_TYPE                              PROCESS                    NUM_ROWS      BYTES      WAITS
---------- ---------------------------------------- ------------------------ ---------- ---------- ----------
         0 Ranger                                   QC                              182      19244          1
         0 Producer                                 P002                          52504    2777440         18
         0 Producer                                 P003                          54496    2884494         21
         0 Consumer                                 P000                          53000    2793946         12
         0 Consumer                                 P001                          54000    2867942         12
         1 Producer                                 P000                          53000    2793923          0
         1 Producer                                 P001                          54000    2867919          0
         1 Consumer                                 QC                           107000    5661842        341

                       P -> P                                               P -> S
생산자(Producer)                    소비자(Consumer)     생산자(Producer)                  소비자(Consumer)
P002 (52504 ROWS)                  P000 (A-M)          P000 (A-M)                       QC
P003 (54496 ROWS)                  P001 (N-Z)          P001 (N-Z)                       
-----------------    ----------    ----------------    ----------------    ---------    ---------------------
데이터 추출            테이블큐       정렬                정렬결과셋           테이블큐      생산자로부터 받은 각각의
병렬서버집합 1         : TQ10000     병렬서버집합 2                           : TQ10001    정렬 결과를 Merge


- 생산자(Producer) : 작업을 생성, 데이터 추출, 데이터를 추출해서 버퍼에 저장
- 소비자(Consumer) : 작업을 처리, 버퍼에 있는 데이터를 꺼내 소비(정렬, 그룹작업수행)
- 테이블큐(TQ) : 프로세스 간 통신 즉 메시지 또는 데이터를 전송하기 위한 통신, 파이프라인(pipeline), 작업반장



# group by push down 적용

HR@ora11g> select /*+ full(e) parallel(e 2) */ department_id, count(*) 
           from emp e group by department_id;

12 rows selected.

HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last parallel'));

-------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                | Name     | Starts | E-Rows |    TQ  |IN-OUT| PQ Distrib | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |          |      1 |        |        |      |            |     12 |00:00:00.07 |       5 |       |       |          |
|   1 |  PX COORDINATOR          |          |      1 |        |        |      |            |     12 |00:00:00.07 |       5 |       |       |          |
|   2 |   PX SEND QC (RANDOM)    | :TQ10001 |      0 |     11 |  Q1,01 | P->S | QC (RAND)  |      0 |00:00:00.01 |       0 |       |       |          |
|   3 |    HASH GROUP BY         |          |      0 |     11 |  Q1,01 | PCWP |            |      0 |00:00:00.01 |       0 |  1115K|  1115K| 1643K (0)|
|   4 |     PX RECEIVE           |          |      0 |     11 |  Q1,01 | PCWP |            |      0 |00:00:00.01 |       0 |       |       |          |
|   5 |      PX SEND HASH        | :TQ10000 |      0 |     11 |  Q1,00 | P->P | HASH       |      0 |00:00:00.01 |       0 |       |       |          |
|   6 |       HASH GROUP BY      |          |      0 |     11 |  Q1,00 | PCWP |            |      0 |00:00:00.01 |       0 |  4455K|  1486K| 1647K (0)|
|   7 |        PX BLOCK ITERATOR |          |      0 |    107K|  Q1,00 | PCWC |            |      0 |00:00:00.01 |       0 |       |       |          |
|*  8 |         TABLE ACCESS FULL| EMP      |      0 |    107K|  Q1,00 | PCWP |            |      0 |00:00:00.01 |       0 |       |       |          |
-------------------------------------------------------------------------------------------------------------------------------------------------------

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

   8 - access(:Z>=:Z AND :Z<=:Z)


HR@ora11g> select tq_id, server_type, process, num_rows, bytes, waits
           from v$pq_tqstat
           order by tq_id, decode(substr(server_type,1,4),'Rang',1,'Prod',2,'Cons',3),process;

     TQ_ID SERVER_TYPE                              PROCESS                    NUM_ROWS      BYTES      WAITS
---------- ---------------------------------------- ------------------------ ---------- ---------- ----------
         0 Producer                                 P002                             12        231          0
         0 Producer                                 P003                             12        231          0
         0 Consumer                                 P000                             12        232          3
         0 Consumer                                 P001                             12        230          3
         1 Producer                                 P000                              6         70          2
         1 Producer                                 P001                              6         67          2
         1 Consumer                                 QC                               12        137          2

- gby_pushdown 힌트 작동
- 원래 수행되어야하는 group by는 ID기준으로 3번이지만 id 6번 먼저 group by 가 수행되었다.
- 그 이유는 id 5번의 : TQ10000에게 데이터를 전달하기전에 데이터를 줄여서 성능을 향상시키기 위함이다.

                             P->P         P->S
Producer      Consumer           Producer     Consumer
P002(12)  —  P000(ID 10~50)  —  P000(6)  →  QC(12)
          ⨉                  ⨉           ↗
P003(12)  —  P001(ID 60~)    —  P001(6)    
group by      group by
+ 추출





# no_gby_pushdown
HR@ora11g> select /*+ full(e) parallel(e 2) no_gby_pushdown */ department_id, count(*) 
           from emp e group by department_id;

12 rows selected.

HR@ora11g> select * from table(dbms_xplan.display_cursor(null,null,'allstats last parallel'));

------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation               | Name     | Starts | E-Rows |    TQ  |IN-OUT| PQ Distrib | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |          |      1 |        |        |      |            |     12 |00:00:00.05 |       5 |       |       |          |
|   1 |  PX COORDINATOR         |          |      1 |        |        |      |            |     12 |00:00:00.05 |       5 |       |       |          |
|   2 |   PX SEND QC (RANDOM)   | :TQ10001 |      0 |     11 |  Q1,01 | P->S | QC (RAND)  |      0 |00:00:00.01 |       0 |       |       |          |
|   3 |    HASH GROUP BY        |          |      0 |     11 |  Q1,01 | PCWP |            |      0 |00:00:00.01 |       0 |  8417K|  3028K| 1393K (0)|
|   4 |     PX RECEIVE          |          |      0 |    107K|  Q1,01 | PCWP |            |      0 |00:00:00.01 |       0 |       |       |          |
|   5 |      PX SEND HASH       | :TQ10000 |      0 |    107K|  Q1,00 | P->P | HASH       |      0 |00:00:00.01 |       0 |       |       |          |
|   6 |       PX BLOCK ITERATOR |          |      0 |    107K|  Q1,00 | PCWC |            |      0 |00:00:00.01 |       0 |       |       |          |
|*  7 |        TABLE ACCESS FULL| EMP      |      0 |    107K|  Q1,00 | PCWP |            |      0 |00:00:00.01 |       0 |       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------------------

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

   7 - access(:Z>=:Z AND :Z<=:Z)


HR@ora11g> select tq_id, server_type, process, num_rows, bytes, waits
           from v$pq_tqstat
           order by tq_id, decode(substr(server_type,1,4),'Rang',1,'Prod',2,'Cons',3),process;

     TQ_ID SERVER_TYPE                              PROCESS                    NUM_ROWS      BYTES      WAITS
---------- ---------------------------------------- ------------------------ ---------- ---------- ----------
         0 Producer                                 P002                          52411     209926         14
         0 Producer                                 P003                          54589     218654         14
         0 Consumer                                 P000                          97000     388500          8
         0 Consumer                                 P001                          10000      40080          8
         1 Producer                                 P000                              6         70         25
         1 Producer                                 P001                              6         67          5
         1 Consumer                                 QC                               12        137          3

                             P->P         P->S
Producer         Consumer        Producer     Consumer
P002(52411)  —  P000(97000)  —  P000(6)  —  QC(12)
             ⨉               ⨉            /
P003(54589)  —  P001(10000)  —  P001(6)
추출                              group by




 

 



[ Sample Table : hr.emp_part, hr.dept_part ]

HR@ora11g> drop table hr.emp_part purge;

HR@ora11g> drop table hr.dept_part purge;

HR@ora11g> create table hr.emp_part
           partition by list(department_id)(
                partition p_dept_1 values (10,20,30,40),
                partition p_dept_2 values (50),
                partition p_dept_3 values (60,70,80,90,100,110),
                partition p_dept_4 values (default))
           as select * from hr.employees;

Table created.

HR@ora11g> create table hr.dept_part
           partition by list(department_id)(
                partition p_dept_1 values (10,20,30,40),
                partition p_dept_2 values (50),
                partition p_dept_3 values (60,70,80,90,100,110),
                partition p_dept_4 values (default))
           as select * from hr.departments;

HR@ora11g> exec dbms_stats.gather_table_stats('hr','emp_part', granularity => 'auto')

PL/SQL procedure successfully completed.

HR@ora11g> exec dbms_stats.gather_table_stats('hr','dept_part', granularity => 'auto')

PL/SQL procedure successfully completed.


HR@ora11g> select partition_name, partition_position, high_value, tablespace_name, num_rows, blocks, avg_row_len
           from user_tab_partitions
           where table_name = 'EMP_PART';

PARTITION_NAME  PARTITION_POSITION HIGH_VALUE                TABLESPACE_NAME   NUM_ROWS     BLOCKS AVG_ROW_LEN
--------------- ------------------ ------------------------- --------------- ---------- ---------- -----------
P_DEPT_1                         1 10, 20, 30, 40            USERS                   10         19          64
P_DEPT_2                         2 50                        USERS                   45         19          66
P_DEPT_3                         3 60, 70, 80, 90, 100, 110  USERS                   51         19          71
P_DEPT_4                         4 default                   USERS                    1         13          71

HR@ora11g> HR@ora11g> select * from user_part_key_columns where name = 'EMP_PART';

NAME                                          OBJECT_TYPE   COLUMN_NAME          COLUMN_POSITION
--------------------------------------------- ------------- -------------------- ---------------
EMP_PART                                      TABLE         DEPARTMENT_ID                      1


HR@ora11g> select partition_name, partition_position, high_value, tablespace_name, num_rows, blocks, avg_row_len
           from user_tab_partitions
           where table_name = 'DEPT_PART';

PARTITION_NAME  PARTITION_POSITION HIGH_VALUE                TABLESPACE_NAME   NUM_ROWS     BLOCKS AVG_ROW_LEN
--------------- ------------------ ------------------------- --------------- ---------- ---------- -----------
P_DEPT_1                         1 10, 20, 30, 40            USERS                    4         19          23
P_DEPT_2                         2 50                        USERS                    1         19          19
P_DEPT_3                         3 60, 70, 80, 90, 100, 110  USERS                    6         19          19
P_DEPT_4                         4 default                   USERS                   16         19          19

HR@ora11g> select * from user_part_key_columns where name = 'DEPT_PART';

NAME                                          OBJECT_TYPE   COLUMN_NAME          COLUMN_POSITION
--------------------------------------------- ------------- -------------------- ---------------
DEPT_PART                                     TABLE         DEPARTMENT_ID                      1




■ Full Partition Wise Join
- 조인하려는 두 테이블에 조인키 컬럼을 기준으로 파티션 된 경우
- 데이터 재분배가 필요없다.
- pq_distribute(Inner Table(별칭), Outer Table Distribution 방식, Inner Table Distribution 방식)
                                                (재분배)                       (재분배)

# pq_distribute(e,none,none) : Full Partition Wise Join으로 유도할 때 사용
                               양쪽 테이블 모두 조인 컬럼에 대해 같은 기준으로 파티셔닝되어 있을 경우 사용
                               = 재분배 할 필요 없다. 밤시간 작업

HR@ora11g> select /*+ leading(d,e) use_hash(e) full(d) full(e) parallel(d 2) parallel(e 2) 
                  pq_distribute(e, none, none) */
                  e.employee_id, e.last_name, e.salary, d.department_id, d.department_name
           from hr.dept_part d, hr.emp_part e
           where e.department_id = d.department_id;

106 rows selected.

> e.department_id = d.department_id;
> leading(d,e)  outer : hr.dept_part
                inner : hr.emp_part
> 부서정보가 있는 사원정보 출력


HR@ora11g> select * from table(dbms_xplan.display_cursor(null, null, 'allstats last partition'));

------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation               | Name      | Starts | E-Rows | Pstart| Pstop | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |           |      1 |        |       |       |    106 |00:00:00.01 |      24 |       |       |          |
|   1 |  PX COORDINATOR         |           |      1 |        |       |       |    106 |00:00:00.01 |      24 |       |       |          |
|   2 |   PX SEND QC (RANDOM)   | :TQ10000  |      0 |    106 |       |       |      0 |00:00:00.01 |       0 |       |       |          |
|   3 |    PX PARTITION LIST ALL|           |      0 |    106 |     1 |     4 |      0 |00:00:00.01 |       0 |       |       |          |
|*  4 |     HASH JOIN           |           |      0 |    106 |       |       |      0 |00:00:00.01 |       0 |  1135K|  1135K|  753K (0)|
|   5 |      TABLE ACCESS FULL  | DEPT_PART |      0 |     27 |     1 |     4 |      0 |00:00:00.01 |       0 |       |       |          |
|   6 |      TABLE ACCESS FULL  | EMP_PART  |      0 |    107 |     1 |     4 |      0 |00:00:00.01 |       0 |       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------

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

   4 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")


HR@ora11g> select tq_id, server_type, process, num_rows, bytes, waits
           from v$pq_tqstat
           order by tq_id, decode(substr(server_type,1,4),'Rang',1,'Prod',2,'Cons',3),process;

     TQ_ID SERVER_TYPE                              PROCESS                    NUM_ROWS      BYTES      WAITS
---------- ---------------------------------------- ------------------------ ---------- ---------- ----------
         0 Producer                                 P000                             51       1492          2
         0 Producer                                 P001                             55       1757          2
         0 Consumer                                 QC                              106       3249          6

Producer                          Consumer
hr.emp_part(inner)
P000 P_DEPT_3 + P_DEPT_4 = 51  —  QC 106
                                /
P001 P_DEPT_1 + P_DEPT_2 = 55  

> 파티션 키컬럼끼지 내부적으로 조인, 어떻게 엮일지는 모름



 

[ Sample Table : hr.emp_non ]


HR@ora11g> drop table hr.emp_non purge;

Table dropped.

HR@ora11g> create table hr.emp_non as select * from hr.employees;

Table created.

 

 


■ Partial Partition Wise Join
- 조인테이블에 한쪽만 파티셔닝된 경우 > 파티션 없는 테이블에 다이나믹 파티션으로 유도
- 파티셔닝이 되어있지 않은 다른쪽 테이블을 같은 기준으로 파티셔닝하고나서 Full Partition Wise Join을 수행한다.
- 동적 파티셔닝을 위한 데이터 재분배가 필요하다.

# pq_distribute(e, none, partition) : inner 테이블은 outer 테이블 파티션 기준에 따라 파티셔닝하라는 의미
                                        당연히 outer 테이블 조회 키 컬럼에 대해 파티셔닝되 있을때 작동된다.

 

 


HR@ora11g> select /*+ leading(d,e) use_hash(e) full(d) full(e) parallel(d 2) parallel(e 2)
                  pq_distribute(e, none, partition) */
                  e.employee_id, e.last_name, e.salary, d.department_id, d.department_name
           from hr.dept_part d, hr.emp_non e
           where e.department_id = d.department_id;

106 rows selected.

> outer : dept_part
> inner : emp_non    > 다이나믹 파티션 적용(힌트사용)


HR@ora11g> select * from table(dbms_xplan.display_cursor(null, null, 'allstats last partition'));

----------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name      | Starts | E-Rows | Pstart| Pstop | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
----------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |           |      1 |        |       |       |    106 |00:00:00.01 |      15 |       |       |          |
|   1 |  PX COORDINATOR             |           |      1 |        |       |       |    106 |00:00:00.01 |      15 |       |       |          |
|   2 |   PX SEND QC (RANDOM)       | :TQ10001  |      0 |    106 |       |       |      0 |00:00:00.01 |       0 |       |       |          |
|*  3 |    HASH JOIN BUFFERED       |           |      0 |    106 |       |       |      0 |00:00:00.01 |       0 |  1300K|  1300K| 2089K (0)|
|   4 |     PX PARTITION LIST ALL   |           |      0 |     27 |     1 |     4 |      0 |00:00:00.01 |       0 |       |       |          |
|   5 |      TABLE ACCESS FULL      | DEPT_PART |      0 |     27 |     1 |     4 |      0 |00:00:00.01 |       0 |       |       |          |
|   6 |     PX RECEIVE              |           |      0 |    107 |       |       |      0 |00:00:00.01 |       0 |       |       |          |
|   7 |      PX SEND PARTITION (KEY)| :TQ10000  |      0 |    107 |       |       |      0 |00:00:00.01 |       0 |       |       |          |
|   8 |       PX BLOCK ITERATOR     |           |      0 |    107 |       |       |      0 |00:00:00.01 |       0 |       |       |          |
|*  9 |        TABLE ACCESS FULL    | EMP_NON   |      0 |    107 |       |       |      0 |00:00:00.01 |       0 |       |       |          |
----------------------------------------------------------------------------------------------------------------------------------------------

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

   3 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")
   9 - access(:Z>=:Z AND :Z<=:Z)

Note
-----
   - dynamic sampling used for this statement (level=2)

- PX SEND PARTITION (KEY) : 동적으로 파티션을 해서 probe에 있는 테이블이 build 테이블과 동일하게 파티션해서 올린다는 의미.


HR@ora11g> select tq_id, server_type, process, num_rows, bytes, waits
           from v$pq_tqstat
           order by tq_id, decode(substr(server_type,1,4),'Rang',1,'Prod',2,'Cons',3),process;

     TQ_ID SERVER_TYPE                              PROCESS                    NUM_ROWS      BYTES      WAITS
---------- ---------------------------------------- ------------------------ ---------- ---------- ----------
         0 Producer                                 P002                             98       2118          3
         0 Producer                                 P003                              9        225          0
         0 Consumer                                 P000                             46       1004          8
         0 Consumer                                 P001                             61       1339          8
         1 Producer                                 P000                             45       1416          0
         1 Producer                                 P001                             61       1833          0
         1 Consumer                                 QC                              106       3249          1



[ Sample Table : hr.dept_non ]

HR@ora11g> drop table hr.dept_non purge;

Table dropped.

HR@ora11g> create table hr.dept_non as select * from hr.departments;

Table created.


# pq_distribute(e,partition,none)
- Partial Partition Wise Join 일 때 Full Partition Wise Join으로 수행하기 위해서 사용된다.
- Outer Table을 Inner Table 파티션 기준에 따라 파티셔닝하라는 의미

HR@ora11g> select /*+ leading(d,e) use_hash(e) full(d) full(e) parallel(d 2) parallel(e 2)
                  pq_distribute(e,partition,none) */
                  e.employee_id, e.last_name, e.salary, d.department_id, d.department_name
           from hr.dept_non d, hr.emp_part e
           where e.department_id = d.department_id;


HR@ora11g> select * from table(dbms_xplan.display_cursor(null, null, 'allstats last partition'));

--------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name     | Starts | E-Rows | Pstart| Pstop | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
--------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |          |      1 |        |       |       |    106 |00:00:00.01 |      15 |       |       |          |
|   1 |  PX COORDINATOR                  |          |      1 |        |       |       |    106 |00:00:00.01 |      15 |       |       |          |
|   2 |   PX SEND QC (RANDOM)            | :TQ10001 |      0 |    106 |       |       |      0 |00:00:00.01 |       0 |       |       |          |
|*  3 |    HASH JOIN                     |          |      0 |    106 |       |       |      0 |00:00:00.01 |       0 |  1061K|  1061K| 1118K (0)|
|   4 |     PART JOIN FILTER CREATE      | :BF0000  |      0 |     27 |       |       |      0 |00:00:00.01 |       0 |       |       |          |
|   5 |      PX RECEIVE                  |          |      0 |     27 |       |       |      0 |00:00:00.01 |       0 |       |       |          |
|   6 |       PX SEND PARTITION (KEY)    | :TQ10000 |      0 |     27 |       |       |      0 |00:00:00.01 |       0 |       |       |          |
|   7 |        PX BLOCK ITERATOR         |          |      0 |     27 |       |       |      0 |00:00:00.01 |       0 |       |       |          |
|*  8 |         TABLE ACCESS FULL        | DEPT_NON |      0 |     27 |       |       |      0 |00:00:00.01 |       0 |       |       |          |
|   9 |     PX PARTITION LIST JOIN-FILTER|          |      0 |    107 |:BF0000|:BF0000|      0 |00:00:00.01 |       0 |       |       |          |
|  10 |      TABLE ACCESS FULL           | EMP_PART |      0 |    107 |:BF0000|:BF0000|      0 |00:00:00.01 |       0 |       |       |          |
--------------------------------------------------------------------------------------------------------------------------------------------------

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

   3 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")
   8 - access(:Z>=:Z AND :Z<=:Z)

Note
-----
   - dynamic sampling used for this statement (level=2)

- PX SEND PARTITION (KEY) : dept_non에 맞춰 emp_part에 다이나믹 파티션 진행되었구나.


HR@ora11g> select tq_id, server_type, process, num_rows, bytes, waits
           from v$pq_tqstat
           order by tq_id, decode(substr(server_type,1,4),'Rang',1,'Prod',2,'Cons',3),process;

     TQ_ID SERVER_TYPE                              PROCESS                    NUM_ROWS      BYTES      WAITS
---------- ---------------------------------------- ------------------------ ---------- ---------- ----------
         0 Producer                                 P002                             27        505          1
         0 Producer                                 P003                              0         40          0
         0 Consumer                                 P000                             17        347          5
         0 Consumer                                 P001                             10        198          5
         1 Producer                                 P000                             45       1416          0
         1 Producer                                 P001                             61       1833          0
         1 Consumer                                 QC                              106       3249          1





■ 동적 파티셔닝 : 둘다 파티셔닝 되지 않은 경우
- 어느 한쪽도 조인 컬럼에 대해 파티셔닝이 되지 않은 상황
- 양쪽 테이블이 모두 대용량

# pq_distribute(e,hash,hash)
: 조인 키 컬럼을 해시 함수에 적용하고 거기서 반환된 값을 기준으로 양쪽 테이블을 동적으로 파티셔닝 하라는 의미

> 일반적으로는 대용량 조인 시 파티션을 만들어 사용한다.
> 대용량 테이블을 동적 파티셔닝 하느니 테이블구조를 바꾸기도 한다.

HR@ora11g> select /*+ leading(d,e) use_hash(e) full(d) full(e) parallel(d 2) parallel(e 2)
                  pq_distribute(e, hash, hash) */
                  e.employee_id, e.last_name, e.salary, d.department_id, d.department_name
           from hr.dept_non d, hr.emp_non e
           where e.department_id = d.department_id;

HR@ora11g> select * from table(dbms_xplan.display_cursor(null, null, 'allstats last partition'));

-----------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation               | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem | Used-Tmp|
-----------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |          |      1 |        |    106K|00:00:00.41 |       8 |       |       |          |         |
|   1 |  PX COORDINATOR         |          |      1 |        |    106K|00:00:00.41 |       8 |       |       |          |         |
|   2 |   PX SEND QC (RANDOM)   | :TQ10002 |      0 |    106K|      0 |00:00:00.01 |       0 |       |       |          |         |
|*  3 |    HASH JOIN BUFFERED   |          |      0 |    106K|      0 |00:00:00.01 |       0 |  1278K|  1278K| 4251K (0)|    3072 |
|   4 |     PX RECEIVE          |          |      0 |     27 |      0 |00:00:00.01 |       0 |       |       |          |         |
|   5 |      PX SEND HASH       | :TQ10000 |      0 |     27 |      0 |00:00:00.01 |       0 |       |       |          |         |
|   6 |       PX BLOCK ITERATOR |          |      0 |     27 |      0 |00:00:00.01 |       0 |       |       |          |         |
|*  7 |        TABLE ACCESS FULL| DEPT_NON |      0 |     27 |      0 |00:00:00.01 |       0 |       |       |          |         |
|   8 |     PX RECEIVE          |          |      0 |    107K|      0 |00:00:00.01 |       0 |       |       |          |         |
|   9 |      PX SEND HASH       | :TQ10001 |      0 |    107K|      0 |00:00:00.01 |       0 |       |       |          |         |
|  10 |       PX BLOCK ITERATOR |          |      0 |    107K|      0 |00:00:00.01 |       0 |       |       |          |         |
|* 11 |        TABLE ACCESS FULL| EMP_NON  |      0 |    107K|      0 |00:00:00.01 |       0 |       |       |          |         |
-----------------------------------------------------------------------------------------------------------------------------------

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

   3 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")
   7 - access(:Z>=:Z AND :Z<=:Z)
  11 - access(:Z>=:Z AND :Z<=:Z)

Note
-----
   - dynamic sampling used for this statement (level=2)


HR@ora11g> select tq_id, server_type, process, num_rows, bytes, waits
           from v$pq_tqstat
           order by tq_id, decode(substr(server_type,1,4),'Rang',1,'Prod',2,'Cons',3),process;

     TQ_ID SERVER_TYPE                              PROCESS                    NUM_ROWS      BYTES      WAITS
---------- ---------------------------------------- ------------------------ ---------- ---------- ----------
         0 Producer                                 P002                             27        505          2
         0 Producer                                 P003                              0         40          0
         0 Consumer                                 P000                             13        251        137
         0 Consumer                                 P001                             14        294        139
         1 Producer                                 P002                             97       2100          1
         1 Producer                                 P003                              9        225          1
         1 Consumer                                 P000                             97       2091        138
         1 Consumer                                 P001                              9        234        139
         2 Producer                                 P000                             97       2923          0
         2 Producer                                 P001                              9        326          0
         2 Consumer                                 QC                              106       3249          1





■ Broadcast : 둘다 파티셔닝 되지 않은 경우
- 어느 한쪽도 조인 컬럼에 대해 파티셔닝이 되지 않은 상황
- 둘 중 하나의 데이터 집합이 매우 작을 때

# pq_distribute(e,broadcast,none) : 작은 테이블을 큰 테이블쪽으로 브로드캐스트한다.
- broadcast 되는 데이터 집합이 아주 작을 때 유용하다.

> 대용량 테이블을 파티션을 하면 Temp 공간을 많이 사용할 수 있으니 Full Table Scan을 하자.


HR@ora11g> select /*+ leading(d,e) use_hash(e) full(d) full(e) parallel(d 2) parallel(e 2)
                  pq_distribute(e, broadcast, none) */
                  e.employee_id, e.last_name, e.salary, d.department_id, d.department_name
           from hr.dept_non d, hr.emp_non e
           where e.department_id = d.department_id;

HR@ora11g> select * from table(dbms_xplan.display_cursor(null, null, 'allstats last partition'));

-------------------------------------------------------------------------------------------------------------------------
| Id  | Operation               | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |          |      1 |        |    106 |00:00:00.01 |       6 |       |       |          |
|   1 |  PX COORDINATOR         |          |      1 |        |    106 |00:00:00.01 |       6 |       |       |          |
|   2 |   PX SEND QC (RANDOM)   | :TQ10001 |      0 |    106 |      0 |00:00:00.01 |       0 |       |       |          |
|*  3 |    HASH JOIN            |          |      0 |    106 |      0 |00:00:00.01 |       0 |  1077K|  1077K| 1244K (0)|
|   4 |     PX RECEIVE          |          |      0 |     27 |      0 |00:00:00.01 |       0 |       |       |          |
|   5 |      PX SEND BROADCAST  | :TQ10000 |      0 |     27 |      0 |00:00:00.01 |       0 |       |       |          |
|   6 |       PX BLOCK ITERATOR |          |      0 |     27 |      0 |00:00:00.01 |       0 |       |       |          |
|*  7 |        TABLE ACCESS FULL| DEPT_NON |      0 |     27 |      0 |00:00:00.01 |       0 |       |       |          |
|   8 |     PX BLOCK ITERATOR   |          |      0 |    107 |      0 |00:00:00.01 |       0 |       |       |          |
|*  9 |      TABLE ACCESS FULL  | EMP_NON  |      0 |    107 |      0 |00:00:00.01 |       0 |       |       |          |
-------------------------------------------------------------------------------------------------------------------------

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

   3 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")
   7 - access(:Z>=:Z AND :Z<=:Z)
   9 - access(:Z>=:Z AND :Z<=:Z)

Note
-----
   - dynamic sampling used for this statement (level=2)

> HASH JOIN > build : dept_non 총 27행
              probe : emp_non 총 107행


HR@ora11g> select tq_id, server_type, process, num_rows, bytes, waits
           from v$pq_tqstat
           order by tq_id, decode(substr(server_type,1,4),'Rang',1,'Prod',2,'Cons',3),process;

     TQ_ID SERVER_TYPE                              PROCESS                    NUM_ROWS      BYTES      WAITS
---------- ---------------------------------------- ------------------------ ---------- ---------- ----------
         0 Producer                                 P002                             54        970          1
         0 Producer                                 P003                              0         40          0
         0 Consumer                                 P000                             27        505         37
         0 Consumer                                 P001                             27        505         39
         1 Producer                                 P000                             97       2954          1
         1 Producer                                 P001                              9        295          1
         1 Consumer                                 QC                              106       3249          4

> P003 : 0 row -> 데이터가 작아서 P002만 일하고 있다.
> dept_non 작은 테이블을 브로드캐스트한다.
    
Producer      Consumer          Producer           Consumer
dept_non      hash Table  JOIN  emp_non   TQ10001
P002(54)  —  P000(27)     —    P000(97)    —     QC(106)
          \               Ⅹ                 /
P003(0)       P001(27)     —    P001(9)

> P002는 P000, P001에 각각 dept_non의 Hash Table 생성한 후 emp_non에서 P000(97), P001(9)를 가져와 QC에게 보낸다.

'Oracle 11g > Tuning' 카테고리의 다른 글

95-1 DML작업 병렬처리  (0) 2024.02.29
93 Arraysize, Partition Pruning, Local/Global Partitioned Indexes  (1) 2024.02.27
92 Partition  (1) 2024.02.26
91 통계정보  (0) 2024.02.23
90 Pushdown, Pullup, Use_concat, Optimazer  (0) 2024.02.22