01. 기본개념

병렬처리

  • SQL문이 수행해야 할 작업범위를 여러개의 작은 단위로 나누어 여러 프로세스(또는 쓰레드)가 동시에 처리하는 것을 말한다.
  • 여러 프로세스가 동시에 작업하므로 대용량 데이터를 처리할 때 수행속도를 극적으로 단축시킬 수 잇다.

(1) Query Coordinator와 병렬 서버 프로세스

  • Query Coordinator(QC) : 병렬 SQL문을 발생한 세션
  • 병렬 서버 프로세스 : 실제 작업을 수행하는 개별 세션

병렬처리 수행 과정 및 QC의 역할

1. 병렬 SQL이 시작되면 QC는 사용자가 지정한 병렬도(DOP, Degree Of Parallelism)와 오퍼레이션 종류에 따라 하나 또는 두개의 병렬 서버집합(Server Set)을 할당한다. 우선 서버풀(Parallel Execution Server Pool)로부터 필요한 만큼 서버 프로세스를 확보하고, 부족분은 새로 생성한다.

  • parallel_min_servers 파라미터로 설정된 수만큼의 병렬프로세스를 오라클이 기본적으로 생성해 서버풀에 담아 둔다.
  • parallel_max_servers 파라미터에 의해 생성가능한 최대 병렬서버 갯수가 결정된다.

2. QC는 각 병렬서버에게 작업을 할당한다. 작업을 지시하고 일이 잘 진행되는지 관리,감독하는 작업반장의 역할이다.

3. 병렬로 처리하도록 사용자가 지시하지 않은 테이블은 QC가 직접 처리한다. 예를 들어, 아래와 같은 실행계획에서 dept테이블을 직렬로 읽어 병렬서버에 전송하는 8~9번 오퍼레이션은 QC의 몫이다.

4. QC는 각 병렬서버로부터의 산출물을 통합하는 작업을 수행한다. 예를 들어 집계함수(sum, avg, min, max 등)가 사용된 아래와 같은 병렬쿼리를 수행할 때, 각 병렬서버가 자신의 처리범위 내에서 집계(4번 단계)한 값을 QC에게 전송(3번 단계)하면 QC가 최종 집계 작업을 수행(1번 단계)한다.

5. QC는 쿼리의 최종 결과집합을 사용자에게 전송하며, DML일때는 갱신건수를 집계해서 전송해 준다. 쿼리결과를 전송하는 단계에서 수행되는 스칼라서브쿼리도 QC가 수행한다.

병렬처리 실행 예제


SET AUTOT ON

SELECT /*+ ordered use_hash(d) full(d) full(e) noparallel(d) parallel(e 4) */
       COUNT(*)
     , MIN(sal)
     , MAX(sal)
     , AVG(sal)
     , SUM(sal)
  FROM dept d, emp e
 WHERE d.loc = 'CHICAGO'
   AND e.deptno = d.deptno
;

Execution Plan
----------------------------------------------------------
Plan hash value: 321505112

-------------------------------------------------------------------------------------------------------------------
| Id  | Operation                 | Name     | Rows  | Bytes | Cost (%CPU)| Time     |    TQ  |IN-OUT| PQ Distrib |
-------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |          |     1 |    47 |     6  (17)| 00:00:01 |        |      |            |
|   1 |  SORT AGGREGATE           |          |     1 |    47 |            |          |        |      |            |
|   2 |   PX COORDINATOR          |          |       |       |            |          |        |      |            |
|   3 |    PX SEND QC (RANDOM)    | :TQ10002 |     1 |    47 |            |          |  Q1,02 | P->S | QC (RAND)  |
|   4 |     SORT AGGREGATE        |          |     1 |    47 |            |          |  Q1,02 | PCWP |            |
|*  5 |      HASH JOIN            |          |     5 |   235 |     6  (17)| 00:00:01 |  Q1,02 | PCWP |            |
|   6 |       BUFFER SORT         |          |       |       |            |          |  Q1,02 | PCWC |            |
|   7 |        PX RECEIVE         |          |     1 |    21 |     3   (0)| 00:00:01 |  Q1,02 | PCWP |            |
|   8 |         PX SEND HASH      | :TQ10000 |     1 |    21 |     3   (0)| 00:00:01 |        | S->P | HASH       |
|*  9 |          TABLE ACCESS FULL| DEPT     |     1 |    21 |     3   (0)| 00:00:01 |        |      |            |
|  10 |       PX RECEIVE          |          |    14 |   364 |     2   (0)| 00:00:01 |  Q1,02 | PCWP |            |
|  11 |        PX SEND HASH       | :TQ10001 |    14 |   364 |     2   (0)| 00:00:01 |  Q1,01 | P->P | HASH       |
|  12 |         PX BLOCK ITERATOR |          |    14 |   364 |     2   (0)| 00:00:01 |  Q1,01 | PCWC |            |
|  13 |          TABLE ACCESS FULL| EMP      |    14 |   364 |     2   (0)| 00:00:01 |  Q1,01 | PCWP |            |
-------------------------------------------------------------------------------------------------------------------

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

   5 - access("E"."DEPTNO"="D"."DEPTNO")
   9 - filter("D"."LOC"='CHICAGO')

Note
-----
   - dynamic sampling used for this statement


Statistics
----------------------------------------------------------
         24  recursive calls
          0  db block gets
          8  consistent gets
          0  physical reads
          0  redo size
        390  bytes sent via SQL*Net to client
        239  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          5  sorts (memory)
          0  sorts (disk)
          1  rows processed

(2) Intra-Operation Parallelism 과 Inter-Operation Parallelism

Order By를 포함하는 병렬처리 실행 예제


SELECT /*+ full(고객) parallel(고객 4) */ *
  FROM 고객
 ORDER BY 고객명
;

  • 병렬처리 방법은 아래 두가지 경우로 생각할 수 있는데.
    • 첫번째. 8명이 각각 정렬한후 머지하는 방법
    • 두번째. 2개조로 나누어 1조(4명)은 분배 2조(4명)은 정렬한후 취합하는 방법
  • 첫번째 방법은 작업자가 많을수록 나중에 QC가 머지하는 단계에서 부하가 걸려 병렬처리를 극대화하기 어렵다.
  • 반면에, 두번째 방법은 이미 각각 정렬된 결과를 머지 작업 없이 그대로 합치기만 하면 된다.
  • 오라클은 두번째 방식으로 동작함
  • Intra-Operation Parallelism
    • 서로 배타적인 범위를 동시에 처리하는 것
    • 서버집합 내부에서는 서로 데이터를 주고받을 일이 없다.
  • Inter-Operation Parallelism
    • 데이터를 분배해서 서버집합2에 데이터 전송
    • 데이터를 정렬하여 QC에 데이터 전송
    • 서버집합간에 항상 통신이 발생한다.

(3) 테이블 큐

  • 테이블 큐(Table Queue) : 쿼리 서버집합 간(P->P) 또는 QC와 서버집합 간(P->S, S->P) 데이터 전송을 위해 연결된 파이프라인(Pipeline).
  • 테이블 큐 식별자(TQ Identifier) - :TQ10000, :TQ10001, :TQ10002
  • 서리 서버집합 간(P->P) Inter-Operation Parallelism이 발생될때 사용자가 지정한 병렬도(=2)의 배수(4개)만큼 서버프로세스가 필요하다.
  • 그림 7-2에서는 병렬도=4이므로 8개의 프로세스가 필요했었다.
그림DOP(병렬도)p(병렬프로세스):TQ10000(S->P):TQ10001(P->P):TQ10002(P->S)
7-144404
7-248(4*2)416(4^2)4
7-324(2*2)24(2^2)2

Order By 병렬처리 실행 예제


SELECT /*+ ordered use_hash(e) full(d) noparallel(d) full(e) parallel(e 2) pq_distribute(e broadcast none) */ *
  FROM dept d, emp e
 WHERE e.deptno = d.deptno
 ORDER BY e.ename
;

Execution Plan
----------------------------------------------------------
Plan hash value: 709482007

--------------------------------------------------------------------------------------------------------------------
| Id  | Operation                  | Name     | Rows  | Bytes | Cost (%CPU)| Time     |    TQ  |IN-OUT| PQ Distrib |
--------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |          |    14 |  1638 |     7  (29)| 00:00:01 |        |      |            |
|   1 |  PX COORDINATOR            |          |       |       |            |          |        |      |            |
|   2 |   PX SEND QC (ORDER)       | :TQ10002 |    14 |  1638 |     7  (29)| 00:00:01 |  Q1,02 | P->S | QC (ORDER) |
|   3 |    SORT ORDER BY           |          |    14 |  1638 |     7  (29)| 00:00:01 |  Q1,02 | PCWP |            |
|   4 |     PX RECEIVE             |          |    14 |  1638 |     6  (17)| 00:00:01 |  Q1,02 | PCWP |            |
|   5 |      PX SEND RANGE         | :TQ10001 |    14 |  1638 |     6  (17)| 00:00:01 |  Q1,01 | P->P | RANGE      |
|*  6 |       HASH JOIN            |          |    14 |  1638 |     6  (17)| 00:00:01 |  Q1,01 | PCWP |            |
|   7 |        BUFFER SORT         |          |       |       |            |          |  Q1,01 | PCWC |            |
|   8 |         PX RECEIVE         |          |     4 |   120 |     3   (0)| 00:00:01 |  Q1,01 | PCWP |            |
|   9 |          PX SEND BROADCAST | :TQ10000 |     4 |   120 |     3   (0)| 00:00:01 |        | S->P | BROADCAST  |
|  10 |           TABLE ACCESS FULL| DEPT     |     4 |   120 |     3   (0)| 00:00:01 |        |      |            |
|  11 |        PX BLOCK ITERATOR   |          |    14 |  1218 |     2   (0)| 00:00:01 |  Q1,01 | PCWC |            |
|  12 |         TABLE ACCESS FULL  | EMP      |    14 |  1218 |     2   (0)| 00:00:01 |  Q1,01 | PCWP |            |
--------------------------------------------------------------------------------------------------------------------

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

   6 - access("E"."DEPTNO"="D"."DEPTNO")

Note
-----
   - dynamic sampling used for this statement


Statistics
----------------------------------------------------------
        191  recursive calls
          3  db block gets
         40  consistent gets
          0  physical reads
        632  redo size
       1295  bytes sent via SQL*Net to client
        239  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          8  sorts (memory)
          0  sorts (disk)
         14  rows processed

생산자 / 소비자 모델
  • 테이블 큐에는 항상 생산자(Producer)와 소비자(Customer)가 존재한다.
  • 그림 7-3에서 보듯 Inter-Operation Parallelism이 나타날 때, 소비자 서버집합은 From절에 테이블 큐를 참조하는 서브쿼리를 가지고 작업을 수행한다.
병렬 실행계획에서 생산자와 소비자 식별
  • 10g 이후 버전부터는 실행계획에 'PX SEND' 및 'PX RECEIVE'가 표시됨.

    ① QC가 dept 테이블을 읽어 첫번째 서버집합(Q1,01)에게 전송한다
    ② 서버집합(Q1,01)은 emp테이블을 병렬로 읽으면서 QC에게서 받아둔 dept 테이블과 조인한다. 조인에 성공한 레코드는 바로바로 두번째 서버집합(Q1,02)에게 전송한다.
    ③ 서버집합(Q1,02)은 전송받은 레코드를 정렬하고 나서 QC에게 전송한다.

(4) IN-OUT 오퍼레이션

구분명칭설명(Intra/Inter)-Operation Parallelism오퍼레이션
S->PPARALLEL_FROM_SERIALQC가 읽은 데이터를 테이블 큐를 통해 병렬서버 프로세스에게 전송X직렬
P->SPARALLEL_TO_SERIAL각 병렬서버 프로세스가 처리한 데이터를 QC에게 전송Inter병렬
P->PPARALLEL_TO_PARALLEL데이터를 재분배(redistribution)하는 오퍼레이션
데이터를 정렬 또는 그룹핑하거나 조인을 위해 동적으로 파티셔닝할 때 사용
Inter병렬
PCWPPARALLEL_COMBINED_WITH_PARENT한 서버집합이 현재스텝과 부모스텝을 모두 처리Intra병렬
PCWCPARALLEL_COMBINED_WITH_CHILD한 서버집합이 현재스텝과 자식스텝을 모두 처리Intra병렬
SERIAL공백인 경우 SERIAL 방식으로 처리X직렬

(5) 데이터 재분배

구분내용
RANGEOrder By 또는 Group By 를 병렬로 처리할 때 사용
정렬작업을 맡은 두번째 서버집합의 프로세스마다 처리범위를 지정하고 나서
데이터를 읽는 첫번째 서버집합이 정렬키값에 따라 정해진 범위에 해당하는 두번째 프로세스에게 분배하는 방식
QC는 작업범위를 할당하며, 정렬작업에는 참여하지 않는다.
정렬결과를 순서대로 받아서 사용자에게 전송하는 역할만 담당
HASH조인이나 Hash Group By 를 병렬로 처리할 때 사용
조인 키나 Group By 키값을 해시함수에 적용하여 리턴되는 값에 따라 데이터를 분배
P->P 뿐만 아니라 S->P 방식으로 이루어 질수도 있다.
BROADCASTQC 또는 첫번째 서버집합의 프로세스들이 각각 읽은 데이터를 두번째 서버집합의 "모든" 병렬프로세스에게 전송하는 방식
병렬 조인에서 크기가 매우 작은 테이블이 있을 때 사용되며
P->P 뿐만 아니라 S->P 방식으로 이루어 진다.
작은 테이블은 병렬로 읽지 않을 때가 많으므로 오히려 S->P가 일반적임
KEY특정 컬럼(들)을 기준으로 테이블 또는 인덱스를 파티셔닝할때 사용하는 분배 방식
실행계획에는 'PARTITION (KEY)'로 표시된다.(줄여서 'PART (KEY)').
ROUND-ROBIN파티션키, 정렬키, 해시함수에 의존하지 않고 반대편 정렬 서버에 무작위로 데이터 분배
골고루 분배되도록 ROUND-ROBIN 방식 사용

(6) Granule

  • Granule : 데이터를 병렬로 처리할 때 일의 최소 단위
  • 블록 기반 Granule
    • 대부분의 병렬 오퍼레이션에 적용되는 기본 작업단위
    • 실행계획상의 PX BLOCK ITERATOR
    • Granule의 크기와 총갯수는 실행시점에서 오브?트 사이즈와 병렬도에 따라 QC가 동적으로 정한다.
  • 파티션 기반 Granule(= 파티션 Granule)
    • 각 병렬 프로세스는 하나의 파티션 전체를 책임진다.(프로세스 하나가 파티션 두개 처리 불가)
    • 실행계획상의 PX PARTITION RANGE ALL 또는 PX PARTITION RANGE ITERATOR
    • Granule의 갯수가 테이블과 인덱스의 파티션 구조에 의해 정적으로 결정되므로 유영성이 적다.
    • 병렬도보다 파티션 갯수가 상당히(대략 병렬도늬 3배 이상) 많을 때라야 유용하다
    • 파티션 수보다 많은 병렬도를 지정할 수 없다.

(7) 병렬 처리 과정에서 발생하는 대기 이벤트