pq_distribute 힌트의 용도 

 - pq_distribute 힌트를 사용함으로써 옵티마이저의 선택을 무시하고 사용자가 직접 조인을 위한 

   데이터 분배 방식을 결정할 수 있다. 

    ● 옵티마이져가 파티션된 테이블을 적절히 활용하지 못하고 동적 재분할을 시도할 때 

    ● 기존 파티션 키를 무시하고 다른 키 값으로 동적 재분할하고 싶을 때 

    ● 통계정보가 부정확하거나 통계정보를 제공하기 어려운 상황에서 실행계획을 

        고정시키고자 할 때 

    ● 기타 여러 가지 이유로 데이터 분배 방식을 변경하고자 할 때

 - 병렬 쿼리는 '분할&정복 원리'에 기초한다. 그 중에서도 병렬 조인을 위해서는 

    '분배&조인 원리'가 작동함을 이해하는 것이 매우 중요하다. 이때, pq_distribute 힌트는 

    조인에 앞서 데이터를 분배하는 과정에만 관여하는 힌트임을 반드시 기억할 필요가 있다. 

 - pq_distribute 힌트는 병렬 조인에 앞선 사전 정지 작업으로서 데이터를 어떻게 분배할지를 

   결정하는 힌트지, 조인 방식을 결정하는 힌트가 아니다. 


구문 이해하기 

 - pq_distribute 힌트의 사용법 

   /*+PQ_DISTRIBUTE ( table, outer_distribution, inner_distribution) */ 

                          1. inner 테이블명  2.outer 테이블의   3. inner 테이블의 

                                또는 alias        distribution 방식    distribution 방식                                                                                    


분배방식 지정 

 - 분배 방식의 종류 

    ● pq_distribute (inner, none, none )

         - Full-Partition Wise 조인으로 유도할 때 사용한다. 당연히, 양쪽 테이블 모두 조인 컬럼에 

           대해 같은 기준으로 파티셔닝 돼 있을 때만 작동한다. 

    ● pq_distribute (inner, partition, none )

         - Partial-Partition Wise 조인으로 유도할 때 사용하며, outer 테이블을 inner 테이블 파티션 

           기준에 따라 파티셔닝하라는 뜻이다.

           당연히, inner 테이블이 조인 키 컬럼에 대해 파티셔닝 돼 있을 때만 작동한다. 

    ● pq_distribute (inner, none, partition )

         - Partial-Partition Wise 조인으로 유도할 때 사용하며, inner 테이블을 outer 테이블 

           파티션 기준에 따라 파티셔닝하라는 뜻이다. 당연히, outer 테이블이 조인 키 컬럼에 

           대해 파티셔닝 돼 있을 때만 작동한다. 

    ● pq_distribute (inner, hash, hash )

         - 조인 키 컬럼을 해시 함수에 적용하고 거기서 반환된 값을 기준으로 양쪽 테이블을 

           동적으로 파티셔닝하라는 뜻이다. 

    ● pq_distribute (inner, broadcast, none )

         - outer 테이블을 Broadcast 하라는 뜻이다. 

    ● pq_distribute (inner, none, broadcast )  

         - inner 테이블을 Broadcast 하라는 뜻이다. 


pq_distribute 힌트를 이용한 튜닝 사례 

 - 통계 정보가 없는 상태에서 병렬 조인하면 옵티마이저가 아주 큰 테이블을 Broadcast 하는 

   경우를 종종 보게 된다. 임시 테이블을 많이 사용하는 야간 배치나 데이터 이해 프로그램에서 

   그런 문제가 자주 발생하는 이유가 여기에 있다. 

 - 10g부터는 통계정보가 없을 때 동적 샘플링이 일어나므로 그럴 가능성이 매우 낮아졌다. 

   하지만 테이블 간 조인을 여러 번 거치면 옵티마이저가 예상한 조인 카디널리티가 점점 

   부정확해지게 마련이다. 

 - 데이터 분포가 고르지 않은 컬럼이 조건절에 많이 사용되거나, 다른 테이블과 조인되기 전

   인라인 뷰 내에서 많은 가공이 이루어져 정확한 카디널리티 계산이 어려울 때 이런 오류 

   발생 가능성은 더욱 커진다. 

병렬 조인 

 - 병렬 조인 매커니즘을 이해하는 핵심 원리는, 병렬 프로세스들이 서로 독립적으로 조인을 

   수행할 수 있도록 데이터를 분배하는 데에 있다. 분배작업이 완료되고 나면 프로세스 간에 

   서로 방해 받지 않고 각자 할당받은 범위 내에서 조인을 완료한다. 

 - 병렬 조인의 큰 2가지 방식 

     1. 파티션 방식 : Partition-Pair 끼리 조인 수행 

     2. Broadcast 방식 : 한쪽 테이블을 Broadcast하고 나서 조인 수행 

                                (파티셔닝 불필요) 

 - 1번 파티션 방식은 조인되는 두 테이블의 파티션 상태에 따라 아래 세 가지 경우로 나뉜다. 

     1-1. 둘 다 같은 기준으로 파티셔닝된 경우 

     1-2. 둘 중 하나만 파티셔닝된 경우 

           ( 둘 다 파티셔닝되었더라도 파티션 기준이 서로 다른 경우는 여기에 해당 )

     1-3. 둘 다 파티셔닝되지 않은 경우 


둘 다 같은 기준으로 파티셔닝 된 경우 - Full Partition Wise 조인 

 - 조인에 참여하는 두 테이블이 조인 컬럼에 대해 같은 기준으로 파티셔닝 돼 있다면 병렬 조인은 

   매우 간단하다. 

 - HASH JOIN 바로 위쪽에 'PX PARTITION RANGE ALL' 또는 'PX PARTITION RANGE ITERATOR'

   라고 표시되는 것을 통해 Full Partition Wise 조인인 것을 확인할 수 있다. 

    ● 다른 병렬 조인은 두 개의 서버집합이 필요한 반면, 여기서는 하나의 서버집합만 필요하다. 

    ● Full Partition Wise 조인은 파티션 기반 Granule이므로 서버 프로세스 개수는 

        파티션 개수 이하로 제한된다. 

    ● 파티션 방식은 어떤 것이든 상관없다. Range이든 리스트이든 해시이든 두 테이블이 

        조인 컬러에 대해 같은 방식, 같은 기준으로 파티셔닝 돼 있다면 서로 방해받지 않고 

        Partition Pair끼리 독립적인 병렬 조인이 가능하기 때문이다. 

    ● 조인 방식도 어떤 것이든 선택 가능하다. NL 조인, 소트 머지 조인, 해시 조인 등


둘 중 하나만 파티셔닝된 경우 - Partial Partition Wise 조인    

 - 둘 중 한 테이블만 조인 컬럼에 대해 파티셔닝된 경우, 다른 한쪽 테이블을 같은 기준으로 

   동적으로 파티셔닝하고 나서 각 Partition-Pair를 독립적으로 병렬 조인하는 것을 

    'Partial Partition Wise 조인'이라고 한다. 둘 다 파티셔닝되었지만 파티션 기준이 서로 다른 

   경우도 이 방식으로 조인될 수 있다. 

 - 중요한 것은, 데이터를 동적으로 파티셔닝하기 위해선 데이터 재분배가 선행되어야 한다는 

   사실이다. 즉, Inter-operation parallelism을 위해 두 개의 서버 집합이 필요해진다.

 - HASH JOIN 아래쪽에 있는 두 테이블 중 어느 한쪽에 'PARTITION (KEY)' 또는 'PART(KEY)' 

   라고 표시되는 것을 통해 Partial Partition Wise 조인인 것을 확인할 수 있다. 

 

둘 다 파티셔닝되지 않은 경우 - 동적 파티셔닝 

 - 조인 컬럼에 대해 어느 한 쪽도 파티션이되지 않은 상황이라면 오라클은  두 가지 방식 중 

   하나를 사용한다. 

    ● 양쪽 테이블을 동적으로 파티셔닝하고서 Full Partition Wise 조인 

    ● 한쪽 테이블을 Broadcast하고 나서 조인 

 - 동적으로 파티셔닝하는 방식 

    ● 1단계 : 첫 번째 서버 집합이 첫번째 테이블을 읽어 두 번째 서버 집합에 전송한다. 

    ● 2단계 : 첫 번째 서버 집합이 두번째 테이블을 읽어 두 번째 서버 집합에 전송한다. 

        - 첫 번째 서버 집합은 데이터를 분배하는 역할을 하고, 두 번째 서버 집합은 받은 데이터를 

          파티셔닝하는 역할을 한다. 가능하다면 메모리 내에서 파티셔닝하겠지만 

          공간이 부족할 때는 Temp 테이블스페이스를 활용할 것이다.

        - 이렇게 2단계까지 완료하고 나면 이제 Partition-Pair가 수성되었으므로 

           Full Partition Wise 조인을 수행할 수 있게 되었다. 

    ● 3단계 : 양쪽 테이블 모두의 파티셔닝을 담당한 두 번째 서버 집합이 

                  각 Partition-Pair에 대해 독립적으로 병렬 조인을 수행한다.  

 - HASH JOIN 아래쪽에 있는 두 테이블 모두 PQ Distrib 컬럼에 'HASH'라고 표시되는 것을 

   통해 동적 파티셔닝이 이루어지는 것을 확인할 수 있다. 

 - 이 방식의 특징은, 조인을 본격적으로 수행하기 전 사전 정지 작업 단계에서 메모리 자원과 

   Temp 테이블스페이스 공간을 많이 사용한다는 데에 있다. 

 - 그리고 양쪽 모두 파티셔닝해야 하므로 기본적으로 양쪽 테이블 모두에 대한 전체범위처리가 

   불가피하다. 

 - 또한 조인 컬럼의 데이터 분포가 균일하지 않을 때는 프로세스 간 일량 차이 때문에 

   병렬 처리 효과가 크게 반감될 수 있다. 예를 들어, 상품권 업무를 담당하는 사원이 

   몇몇 사람에게 집중된 상황에서  일부 프로세스만 열심히 일하고 나머지는 Idle 상태로 

   대기하는 현상이 발생할 것이다. 

 - 동적 파티셔닝 방식이 유용한 상황  

    ● 어느 한 쪽도 조인 커럶 기준으로 파티셔닝되지 않은 상황에서 

    ● 두 테이블 모두 대용량 테이블이고 

    ● 조인 컬럼의 데이터 분포가 균일할 때 


둘 다 파티셔닝되지 않은 경우 - Broadcast 방식 

 - 조인 컬럼에 대해 어느 한 쪽도 파티셔닝되지 않은 상황에서 오라클이 선택할 수 있는 

   두 번째 방식은 Broadcast 방식으로서, 두 테이블 중 작은 쪽을 반대편 서버 집합의 '모든' 

   프로세스에 Broadcast하고 나서 조인을 수행하는 방식이다. 

 - 양쪽 테이블 모두 파티션되지 않았을 때는 1차적으로 Broadcast 방식이 고려되어야 한다. 

   양쪽 테이블을 동적으로 파티셔닝하는 방식은 앞서 설명한 것처럼 메모리 자원과 

   Temp 테이블스페이스 공간을 많이 사용하는 반면 이 방식은 리소스 사용량이 매우 적기 

   때문이다. 

 - 이런 특징은 그러나 Broadcast 되는 테이블이 아주 작을 때만 적용된다. 만약 Broadcast되는 

   테이블이 중대형 이상일 때는 과도한 프로세스 간 통신 때문에 성능이 매우 느려질 수 있다. 

 - 뿐만 아니라 두 번째 서버 집합이 메모리 내에서 감당하기 어려울 정도로 큰 테이블을 

   Broadcast 한다면 Temp 테이블 스페이스 공간을 사용하게 되면서 그 성능은 심각하게 

   저하될 것이다.    

 - 이 외에의 Broadcast 방식의 특징 

    ● Broadcast는 작은 테이블임이 전제되어야 하므로 Serial 스캔으로 처리할 때도 많다. 

        따라서 P->P이 아닌 S->P 형태가 오히려 일반적이고, 이는 두 테이블 중 한쪽 테이블만 

        병렬로 처리함을 뜻한다. 

    ● Broadcast가 이루어지고 나서의 조인 방식은 어떤 것이든 선택 가능하다. 

        NL 조인, 소트 머지 조인, 해시 조인 등 

    ● Broadcast되는 작은 쪽 테이블은 전체범위처리가 불가피하지만 큰 테이블은 

        부분범위처리가 가능하다. 

 

 * 지금까지의 4가지 병려 조인 방식의 특징을 요약한 표가 P.717에 있음 

병렬 order by 와 Group by 

 - P -> P 데이터 재분배는 주로 병렬 order by, 병렬 group by, 병렬 조인을 포함한 SQL에서 

   나타난다. 아주 단순한 SQL이 아니고서야 대부분 이들 오퍼레시연을 포함하므로 

   거의 모든 병렬 SQL에서 Inter-Operation Parallelism이 일어난다고 보면 틀림 없다. 


병렬 Order by 

 - order by를 병렬로 수행하려면 테이블 큐를 통한 테이터 재분배가 필요한데, 쿼리 수행이 

   완료된 직후에 같은 세션에서 v$pq_tqstat를 쿼리해 보면 아래와 같이 테이블 큐를 통한 

   데이터 전송 통계를 확인해 볼 수 있다. 

 - 병렬 쿼리 수행 속도가 예상만큼 빠르지 않다면 테이블 큐를 통한 데이터 전송량에 편차가 

   크지 않은지 확인해 볼 필요가 있는데, 그럴 때 v$pq_tqstat 뷰가 유용하게 쓰인다. 


병렬 Group by 

 - order by 와 group by를 병렬로 처리하는 내부 수행원리는 기본적으로 같다 

  

Group by가 두 번 나타날 때의 처리 과정    

 - 병렬 Group by 실행계획에 아래와 같이 group by가 두 번 나타나는 경우가 있다. 

   비밀은 group 기준 컬럼의 선택도에 있다. 

 - group by절에서 있는 컬럼은 일반적으로 선택도가 pk 보다 낮기 때문에 해당 컬럼을 기준으로 

   첫 번째 서버 집합이 읽은 데이터를 먼저 group by 하고 나서 두 번째 서버 집합에 전송한다면 

   프로세스간 통신량이 줄어 그만큼 병렬 처리 과정에서 생기는 병목을 줄일 수 있다. 

 - 참고로, 선택도가 낮은 컬럼으로 group by 할 때도 강제로 이 방식을 사용하도록 하려면 

    ' _groupby_nopushdown_cut_ration ' 파라미터를 0으로 세팅하면 된다.  

   이 파라미터의 기본 값인 3은 group by 기준 컬럼의 선택도에 따라 옵티마이저가 방식을 

   결정하도록 하는 것이다. 

 - 참고로, 11g에서는 gby_pushdown, no_gby_pushdown 힌트가 추가돼 파라미터 변경 없이도 

   사용자가 group by 방식을 조정할 수 있게 되었다. 

기본 개념 

 - 병렬처리란, SQL문이 수행해야 할 작업 범위를 여러 개의 작은 단위로 나누어 

   여러 프로세스가 동시에 처리하는 것을 말한다. 


Query Coordinator와 병렬 서버 프로세스 

 - Query Coordinator는 병렬 SQL문을 발생한 세션을 말하고, 병렬 서버 프로세스는 

   실제 작업을 수행하는 개별 세션들을 말한다. 

 - QC의 역할은 다음과 같다. 

    1. 병렬 SQL이 시작되면 QC는 사용자가 지정한 병렬도와 오퍼레이션 종류에 따라 

       하나 또는 두개의 병렬 서버 집합을 할당한다. 우선 서버 풀로부터 필요한 만큼 

       서버 프로세스를 확복하고 부족분은 새로 생성한다. 

    2. QC는 각 병렬 서버에게 작업을 할당한다. 작업을 지시히고 일이 잘 진행되는지 관리, 

       감독하는 작업반장 역할이다. 

    3. 병렬로 처리하도록 사용자가 지시하지 않은 테이블은 QC가 직접 처리한다. 

    4. QC는 각 병렬 서버로부터의 산출물을 통합하는 작업을 수행한다. 예를 들어 집계함수가 

       사용된 병렬 쿼리를 수행할 때, 각 병렬 서버가 자신의 처리 범위 내에서 집계 값을 

       QC에게 전송하면 QC가 최종 집계 작업을 수행한다. 

    5. QC는 쿼리의 최종 결과집합을 사용자에게 전송하며, DML일 때는 갱신 건수를 집계해서 

       전송해준다. 쿼리 결과를 전송하는 단계에서 수행되는 스칼라 서브 쿼리도 QC가 수행한다. 

 - 병렬 처리에서 실제 QC 역할을 담당하는 프로세스는 SQL문을 발행한 사요자 세션 자신이다. 


Intra-Operation Parallelism 과 Inter-Operation Parallelism 

 - Intra-Operation Parallelism : 서로 배타적인 범위를 독립적으로 동시에 처리하는 것을 

                                           Intra-Operation Parallelism 이라고 한다. 이 과정에서 

                                           같은 서버 집합 끼리는 서로 데이터를 주고 받을 일이 없다. 

 - Inter-Operation Parallelism : 자신에게 할당된 일이 끝난 뒤 다른 서버 집합에 분배하거나 

                                           정렬된 결과를 QC에게 전송하는작업을 병렬로 동시에 진행하는 

                                           것을 'Inter-Opertaion Parallelism'이라고 하며, 

                                           이때는 항상 프로세스 간 통신이 발생한다.


테이블 큐 

 - 쿼리 서버 집합 간 (P -> P) 또는 QC와 쿼리 서버 집합 간 (P->S, S->P) 데이터 전송을 

   위해 연결도니 파이프 라인을 '테이블 큐'라고 한다. 

 - 쿼리 서버 집합 간 (P ->P) Inter-Operation Parallelism 이 발생할대는 사용자가 지정한 

   병렬도의 배수만큼 서버 프로레스가 필요하다. 

 - 또한 테이블 뮤에는 병렬도의 제곱만큼 파이프 라인이 필요하다. 


생산자/소비자 모델 

 - 테이블 큐에는 항상 생산자와 소비자가 존재한다. Inter-Operation Parallelism이 나타날 때, 

   소비자 서버 집합은 from 절에 테이블 큐를 참조하는 서브(sub) SQL을 가지고 작업을 수행 


병렬 실행계획에서 생산자와 소비자 식별 

 - 10g 이후부터는 생산자에 'PX SEND', 소비자에 'PX RECEIVE'가 표시되므로 테이블 큐를 통한 

   데이터 분배 과정을 좀 더 쉽게 확인할 수 있게 되었다. 

 - 생산자로부터 소비자로 데이터 재분배가 일어날 때마다 실행계획에 'Name' 컬럼에 

   테이블 큐가 표시된다. 


IN-OUT 오퍼레이션 

 - 아래 병렬 쿼리 실행계획(9i)을 보면, 뒤에서 두 번째 컬럼에 테이블 큐를 통한 

   in-out 오퍼레이션 정보가 출력되는 것을 볼 수 있다. 

 - In-out 오퍼레이션에 나타나는 내용의 의미 

     ● S -> P : PARALLEL_FROM_SERIAL

        - QC가 읽은 데이터를 테이블 큐를 통해 병렬 서버 프로세스에게 전공하는 것 

     ● P -> S : PARALLEL_TO_SERIAL 

        - 각 병렬 서버 프로세스가 처리한 데이터를 QC에게 전공하는 것을 의미한다. 

          병렬 프로세스로부터 QC로 통신이 발생하므로 Inter-operation Parallelism에 속한다. 

          참고로, S -> P도 통신이 발생하지만 이는 병렬 오퍼레이션이 아니므로 

           Inter-Operation Parallelism에 속하지 않는다. 

     ● P -> P : PARALLEL_TO_PARALLEL 

         - 데이터를 재분배하는 오퍼레이션으로서, 실행계획에 P->P가 나타날 때면 

           해당 오퍼레이션을 두 개의 서버 집합이 처리한다는 사실이다. 따라서 사용자가 지정한 

           병렬도의 2배수만큼 병렬 프로세스가 필요하다. 

         - 데이터를 정렬 또는 그룹핑하거나 조인을 위해 동적으로 파티셔닝할 때 사용되며, 

           첫 번째 병렬 서버 집합이 읽거나 가공한 데이터를 두 번째 병렬 서버 집합에 전송하는 

           과정에서 병렬 프로세스 간 통신이 발생하므로 Inter-Operation Parallelism에 속한다. 

     ● PCWP : PARALLEL_COMBINED_WITH_PARENT 

         - 이는 한 서버 집합이 현재 스텝과 그 부모 스텝을 모두 처리함을 의미한다. 

         - PCWP도 분명히 병렬 오퍼렝션이지만 한 서버 집합 내에서 프로세스 간 통신이 

           발생하지 않으므로 Intra-operation Parallelism에 속한다. 

         - 즉, 한 서버 집합에 속한 서버 프로세스들이 각자 맡은 범위 내에세 두 스텝 이상의 

           오퍼레이션을 처리하는 것이며, 자식 스텝의 처리 결과를 부모 스텝에서 사용할 뿐 

           프로세스 간  통신은 필요치 않다. 

     ● PCWC: PARALLEL_COMBINED_WITH_CHILD 

         - 한 서버 집합이 현재 스텝과 그 자식 스텝을 모두 처리함을 의미한다. 

           PCWC도 병렬 오퍼레이션이지만 한 서버 집합 내에서는 프로세스간 통신이 

           절대 발생하지 않으므로 Intra-Operation Parellelism에 속한다. 

           자식 스텝의 처리 결과를 받아 현재 스텝의 입력 값으로 사용할 뿐이며, 

           프로세스 간 통신은 필요치 않다. 

 - In-Out 오퍼레이션에 대한 정리 

    ● S -> P, P -> S, P -> P는 프로세스 간 통신이 발생한다. 

    ● PCWP와 PCWC는 프로세스 간 통신이 발생하지 않으며, 각 병렬 서버가 독립적으로 여러 

         스텝을 처리할 때 나타난다. 하위 스텝의 출력 값이 상위 스텝의 입력 값으로 사용된다. 

    ● P -> P, P -> S, PCWP, PCWC는 병렬 오퍼레이션인 반면 S -> P는 직렬 오퍼레이션이다. 

 - 병렬 쿼리 실행계획에 S->P가 나타난다면 해당 오퍼레이션이 병목 지점인지 의심해볼 필요가 

   있다. 만약 처리할 데이터량이 수백 MB이상 된다면 병렬 오퍼레이션으로 바꾸는 것을 

   고려해야 한다. 


데이터 재분배 

 - 일반적인 5가지 데이터 재분배 방식 

    ● RANGE 

          - order by 또는 sort group by를 병렬로 처리할 때 사용된다. 정렬 작업을 맡은 두 번째 

            서버 집합의 프로세스마다 처리 범위를 지정하고 나서, 데이터를 읽는 

            첫 번째 서버 집합이 두 번째 서버 집합의 정해진 프로세스에게 '정렬 키 값에 따라 '  

            분배하는 방식이다. 

          - QC는 각 서버 프로세스에게 작업 범위를 할당하고 정렬 작업에는 직접 참여하지 않으며, 

            정렬이 완료되고 나면 순서대로 결과를 받아서 사용자에게 전송하는 역할만 한다. 

        HASH 

          - 조인이나 hash group by를 병렬로 처리할 때 사용된다. 조인 키나 group by 키 값을 

            해시 함수에 적용ㅎ고 리턴된 값에 따라 데이터를 분배하는 방식이며, P->P 뿐만 아니라 

            S -> P 방식으로 이루어질 수도 있다. 

        BROADCAST 

          - QC 또는 첫 번째 서버 집합에 속한 프로세스들의 각각 읽은 데이터를 

            두 번째 서버 집합에 속한 '모든' 병렬 프로세스에 전송하는 방식이다. 

            병렬 조인에서 크기가 매우 작은 테이블이 있을 때 사용되며, P->P뿐만 아니라 

            S->P방식으로도 이루어진다.  

            ( 작은 테이블은 병렬로 읽지 않을 때가 많으므로 오히려 S->P가 일반적이다. ) 

        KEY

          - 특정 컬럼(들)을 기준으로 테이블 또는 인덱스를 파티셔닝할 때 사용하는 분배 방식으로,

            실행계획에는 'PARTITION (KEY)'로 표시된다. 

             ● Partial Partiton - Wise 조인  

             ● CTAS 문장으로 파티션 테이블을 만들 때 ( -> 9i ) 

             ● 병렬로 글로벌 파티션 인덱스를 만들 때 

                  ( 참고로, 비파티션 인덱스를 만들 때 RANGE 방식 사용 ) 

          - Partial Parttion-Wise 조인은, 이미 파티션된 테이블과 조인하기 위해 다른 한 쪽 

            테이블을 동적으로 파티셔닝하고 나서 각 Partiton-Pair에 대해 독립적으로 병렬 조인을 

            수행하는 것을 말한다.  

        ROUND-ROBIN

          - 파티션 키, 정렬 키, 해시 함수 등에 의존하지 않고 반대편 병렬 서버에 무작위로 

            데이터를 분배할 때 사용된다. 무작위라고는 하지만 골고루 분배되도록 round-robin 

            방식이 사용된다. 


Granule 

 - 데이터를 병렬로 처리할 때 일의 최소 단위를 'Granule'이라고 하며, 병렬 서버는 한 번에 

   하나의 Granule씩만 처리한다. Granule 개수와 크기는 병렬도와 관련 있으며, 이는 병렬 서버

   사이에 일을 고르게 분배하는 데에 큰 영향을 미친다. 

 - Granule에는 크게 '블록 기반 Granule'과 '파티션 기반 Granule 이 있는데, 

   이는 오라클 데이터베이스의 내부적인 결정사항이며 사용자가 그 크기나 종류를 직접 

   선택할 수는 없다. 


블록 기반 Granule (=블록 범위 Granule ) 

 - 블록 기반 Graule은, 파티션 테이블인지 여부와 상관없이 대부분의 병렬 오퍼레이션에 

   적용되는 기분 작업 단위이다. 

 - 병렬 쿼리는 물론, 9iR2부터 병렬 DML에도 블록 기반 Granule이 사용되므로 파티션 여부, 

   파티션 개수와 무관하게 병렬도를 지정할 수 있다. 

 - 블록 기반 Granule 단위로 데이터를 읽을 때는 실행계획상에 'PX BLOCK ITERATOR'라고 

   표시된다. 이 오퍼레이션이 나타날 때면, QC는 테이블로부터 읽어야 할 일정 범위의 블록을 

   Granule로서 각 병렬 서버에게 할당한다. 

 - 그리고 ITERATOR가 의미하는 바와 같이 병렬 서버가 한 Graunle에 대한 일을 끝마치면 

   이어서 다른 Granule을 할당한다. 

 - Granule 크기와 총 개수는 실행 시점에 오브젝트 사이즈와 병렬도에 따라 QC가 동적으로 

   결정한다.  물론 목표는 모든 병렬 서버에게 일을 골고루 분배하는 데에 있으며, 아주 작은 

   테이블이 아니라면 Granule 개수는 사용자가 지정한 병렬도보다 많을 것이다. 

 - 또한 Granule을 계산할 때는 각 병렬 서버에게 가능한 한 서로 다른 데이터 파일에 놓인 

   블록들을 할당함으로써 경합을 회피하려고 노력한다. 


파티션 기반 Granule(=파티션 Granule) 

 - 파티션 기반 Granule이 사용될 때, 각 병렬 서버 프로세스는 할당받은 테이블(또는 인덱스) 

   파티션 전체를 처리할 책을 진다. 이 방식에선 한 파티션을 두 개 프로세스가 함께 처리할 수 

   없으므로 병렬도는 당연히 파티션 개수 이하로만 지정할 수 있다. 

 - 파티션 기반 Graunle일 때는 실행계획에 'PX PARTITION RANGE ALL' 또는 

    'PX PARTITION RANGE ITERATOR'라고 표시된다. 전자는 파티션 전체를 읽어야 할 때 

    나타나고, 후자는 일부 파티션만 읽을 때 나타난다. 

 - 블록 기반 Granule과 마찬가지로 여기서도 병렬 서버가 한 파티션 처리르 끝마치면 이어서  

   다른 파티션을 할당받는 식으로 진행한다. 물론 병렬도가 파티션 개수보다 적을 때 그렇다. 

 - 파티션 기반 Granule을 사용하는 작업 

    ● Partition_Wise 조인 시 

    ● 파티션 인덱스를 병렬로 스캔할 때 

    ● 파티션 인덱스를 병렬로 갱신할 때 

    ● 9iR1 이전에서의 병렬 DML 

    ● 파티션 테이블 또는 파티션 인덱스를 병렬로 생성할 때 

 - 파티션 기반일 때는 Granule 개수가 테이블과 인덱스의 파티션 구조에 의해 정적으로 

   결정되므로 블록 기반 Granule처럼 유연하지가 못하다. 

 - 파티션 기반 Granule은 병렬도보다 파티션 개수가 상당히 많을 때라야 유용하다. 

   ( 대략 병렬도의 3배 이상 )

 - 시스템 리소스를 최대한 사용함으로써 병렬 효과를 극대화하려 할 때, 

   파티션 개수보다 많은 병렬도를 지정할 수 없다는 것도 단점이다. 


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

 - QC와 병렬 서버 간, 병렬 서버와 병렬 서버 간 테이블 큐를 통해 메시지를 주고받기 위해 

   내부적으로 메시지 버퍼를 사용하는데, 생산자 프로세스가 버퍼에 데이터를 넣으면

   소비자 프로세스가 그것을 꺼내 가는 식이다. 

 - 병렬 처리 과정에서 자주 발생하는 대기 이벤트 

    ● PX Deq : Execute Reply 

    ● PX Deq : Execution Msg 

    ● PX Deq : Table Q Nomal 

    ● PX Deq Credit: send blkd 

    ● PX Deq Credit: need buffe 


대기 이벤트 해소 

 -  v$event_name 뷰를 조회해  보면 병렬처리와 관련된 대기 이벤트가 대부분 Idle로 분류돼 

    있다. 이유는, 이들 이벤트를 회피하기 위해 사용자가 할 수 있는 일들이 거의 없기 때문이다. 

 - 큰 테이블을 S -> P 방식으로 분배하던 것을 P -> P 방식으로 바꿔 주거나, 

    P -> P 분배 과정에서 프로세스 간 데이터 전송량이 많은 SQL을 튜닝해서 그 양을 줄일 수 

   있는 경우가 종종 있기는 하다. 

 - 하지만 그런 조치들이 가능하지 않은 상황에서 병렬 쿼리 관련 Idle 대기 이벤트가 많이 

   발생한다면 병렬 메커니즘상 자연스럽게 나타나는 현상이라고 이해하는 수밖에 없다. 

 - Idle 이벤트가 아닌 경우에는 종종 튜닝이 가능할 수 있는데, 앞선 테스트 사례의 경우 

   PX Deq Credit : send blkd 이벤트가 많이 발생하는 이유는 클라이언트가 

   천천히 스크롤 하면서 데이터를 관찰하거나 Fetch Call 과 Fetch Call 사이에 많은 애플리케이션

   로직을 수행하기 때문일 것이다. 전자의 경우라면 CPU 리소스를 낭비하는 것이므로 

   가급적 병렬 쿼리를 사용하지 않는 편이 낫고, 후자의 경우는 애플리케이션 로직을 

   튜닝할 수 있는지 검토해 봐야 한다. 

인덱스 파티션 유형 

 - 인덱스 파티션 종류 

    ● 비파티션 인덱스 

    ● 글로벌 파티션 인덱스 

    ● 로컬 파티션 인덱스 

 - 참고로, 비파티션 테이블에 대한 비트맵 인덱스는 파티셔닝이 허용되지 않고, 파티션 테이블에

   대한 비트맵 인덱스는 로컬 파티셔닝만 허용된다. 


로컬 파티션 인덱스 

 - 로컬 파티션 인덱스는 항상 테이블 파티션과 1:1 관계를 형성하므로 만약 테이블이 

   결합 파티셔닝 돼 있다면 인덱스도 같은 단위로 파티셔닝 된다.  

 - 로컬 파티션 인덱스가 갖는 장점은 무엇보다 관리적 편의성에 있다. 테이블 파티션 구성에 

   변경이 생기더라도 인덱스를 재생성할 필요가 없어 관리 비용이 아주 적다 .


비파티션 인덱스 

 - 비파티션 인덱스는 말 그래도 파티셔닝하지 않은 인덱스를 말한다. 테이블이 파티셔닝 돼 

   있다면 1:M 관계에 놓인다. 


글로벌 파티션 인덱스 

 - 글로벌 파티션 인덱스는 테이블 파티션과 독립적인 구성을 갖도록 파티셔닝하는 것을 말한다. 

   테이블은 파티셔닝돼 있지 않을 수도 있다. 

 - 독립적 구성이라는 점에서 효용성이 높을 것처럼 보이지만 몇몇 제약사항 때문에 오히려 

   효용성이 낮은 편이다. 

 - 글로벌 파티션 인덱스 사용을 불편하게 만드는 가장 큰 제약은, 기준 테이블의 파티션 구성에 

   변경이 생길 때마다 인덱스가 unusable 상태로 바뀌고 그때마다 인덱스를 재생성해야 한다는 

   것이다. 이 제약은 비파티션 인덱스 일 때도 똑같이 나타난다. 

 - 9i부터 아래와 같이 update global indexes 옵션을 주면 파티션 DDL 작업에 의해 영향 받는 

   인덱스 레코드를 자동으로 갱신해 주므로 인덱스가 unusable 상태로 빠지지 않는다. 

 - 하지만, 파티션 DDL로 인해 영향 받는 레코드가 전체의 5% 미만일 때만 유용하다. 

   다시 말해, 5% 이상일 때는 인덱스를 재생성하는 것보다 오히려 늦다는 뜻이다. 

   ( 항상 들어맞는 수치는 아니며, 평균적으로 그렇다는 것이다. 

     손익분기점과 같은 개념으로 이해하면 된다. ) 


테이블 파티션과의 관계 

 - 오라클이 자동으로 관리해 주는 1:1 관계가 아닌 파티션 인덱스는 

   모두 글로벌 파티션 인덱스라고 설명했다. 

 - 참고로, 로컬 파티션 인덱스처럼 테이블과 1:1 관계가 되도록 수동으로 구성하더라도 

   여느 글로벌 파티션과 마찬가지로 기준 테이블 구성에 변경이 발생할 때마다 

   인덱스를 재생성해야 한다.  


글로벌 해시 파티션 인덱스 

 - 글로벌 파티션 인덱스의 경우, 9i까지는 글로벌 Range 파티션만 가능했지만 10g부터는 글로벌 

   해시 파티션도 가능해졌다. 즉, 테이블과 독립적으로 인덱스만 해시 키 값에 따라 

   파티셔닝할 수 있게 되었다.     

- 글로벌 해시 파티션 인덱스는 Right Growing 인덱스 처럼 Hot 블록이 발생하는 인덱스의 

  경합을 분산할 목적으로 주로 사용된다. 

- 글로벌 결합 인덱스 파티셔닝은 여전히 불가능하다. 


Prefixed vs Nonprefixed 

 - prefixed 와 Nonprefixed의 구분은 인덱스 파티션 키 컬럼이 인덱스 구성상 왼쪽 선두 컬럼에 

   위치하는지에 따른 구분이다. 

    ● Prefixed : 파티션 인덱스를 생성할 때, 파티션 키 컬럼을 인덱스 키 컬럼 왼쪽 선두에 

                     두는 것을 말한다. 

    ● Nonprefixed : 파티션 인덱스를 생성할 때, 파티션 키 컬럼을 인덱스 컬럼 왼쪽 선두에 

                           두지 않는 것을 말한다. 파티션 키가 인덱스 커럶에 아예 속하지 않을 때도 

                           여기에 속한다. 

 - 로컬과 글로벌, Prefixed 와 Nonprefixed 조합 

    ● 비파티션 인덱스 

    ● 글로벌 Prefixed 파티션 인덱스 

    ● 로컬 Prefixed 파티션 인덱스 

    ● 로컬 Nonprefixed 파티션 인덱스 


파티션 인덱스 구성 예시 

 - 로컬 파티션 인덱스에서 Unique 파티션 인덱스를 만들 때는 파티션 키 컬럼이 인덱스 컬럼에 

   포함돼 있어야 한다. ( 그렇지 않을 경우 ORA-14039 error 발생 ) 

 - 비파티션 인덱스를 만들 때는 그런 제약이 없으므로 위와 같이 에러 없이 Unique 인덱스가 

   만들어진다. 

 - 로컬 파티션 인덱스에는 Nonprefixed가 허용되지만 

   글로벌 파티션 인덱스에는 허용되지 않는다.  

 - 참고로, 비파티션 인덱스에는 이런 제약이 없다. 

 - user_indexes 와 user_part_indexes  테이블을 통해 파티션 인덱스의 종류를 확인할 수 있다. 


글로벌 파티션 인덱스의 효용성 

 - 결론은 글로벌 파티션 인덱스는 경합을 분산시키려고 글로벌 해시 파티셔닝 하는 경우외에는 

   거의 사용되지 않는 실정 

 - 파티션 테이블에 대한 글로벌 파티션 인덱스의 효용성 

   ● 테이블과 같은 컬럼으로 파티셔닝하는 경우 

      - 이 부분을 이해 하는데 좀 생각이 필요했는데 여기서 예시는 테이블 파티션 값은 월별 

        인덱스 파티션은 분기별로 나누었다. 같은 컬럼을 사용했다고 하니 범위를 다른게 했던지 

        했을 것이다. 그런데 여기서는 날짜는 between으로 자주 사용하기 때문에 글로벌 인덱스가 

        파티션 테이블 키 값보다 넓은 값을 가지고 있으면 상대적으로 많은 값이 있는 파티션들을 

        읽어야 하기 때문에 비효율이 있을 것이라고 말하고 있다. 

      - 그래서 결론은 NL 조인에서 넓은 범위 조건을 가지고 Inner 테이블 엑세를 위해 자주 

        사용된다면 비파티션 인덱스가 가장 좋은 선택이라고 한다. 

      - 그런데 이부분은 사실상 왜 이렇게 글로벌 파티션 인덱스를 구성했는지가 궁금하다 

        이유는 처음 부터 비효율적으로 생성하기 때문이라서 실제로 현업에서 

        이런 케이스가 있는지 설명을 위해서 만들어 낸것인지 궁금하다. 

   ● 테이블과 다른 컬럼으로 파티셔닝 하는 경우 

      - 테이블 파티션 기준인 날짜 이외 컬럼으로 인덱스를 글로벌 파티셔닝 할 수 있는데, 

        그런 구성은 대게 인덱스를 적정 크기로 유지하려는 데에 목적이 있다. 

        인덱스가 너무 커지면 관리하기 힘들고 인덱스 높이가 증가해 엑세스 효율도 나빠지기 

         때문이다. 

      - 하지만 그런 장점도 로컬 파티션 인덱스 때문에 무색해진다. 

        글로벌 파티션이 비파티션 보다 관리상 이점이 있다고는 하나 로컬 파티션만 못하고, 

        인덱스 높이 조절 측면에서도 그렇다. 


로컬 Nonprefixed 파티션 인덱스의 효용성 

 - 결국은 쿼리에서 where 절에 다른 컬럼이 등치 조건일 때 유리할 수 있다. 


글로벌 Prefixed 파티션 인덱스와 비교 

 - 글로벌 파티션 인덱스는 prefixed 파티션만 허용되므로 로컬 prefixed 파티션과 마찬가지로 

   인덱스 스캔 효율이 나쁘다. 

 - 더욱이 과거 파티션을 제거하고 신규 파티션을 추가하는 등의 파티션 단위 작업 시 

   매번 인덱스를 재생성해야 하므로 관리적 부담이 크다. 


비파티션 인덱스와 비교 

 - 비파티션 인덱스를 이용하더라도 관리적 부담은 글로벌 파티션과 동일하게 발생한다. 

   관리적 비용 측면에서는 로컬 Nonprefixed 파티션 인덱스가 훨씬 낫다. 

 - 조회 측면에서 로컬 Nonprefixed 파티션 인덱스는, 파티션 키 값 2개 이상의 넓은 범위의 

   조건으로 조회할 때 여러 인덱스를 (수직적) 탐색해야 하는 비효율이 있다. 

 - 반면 등치 조건을 선두에 둔 비파티션 인덱스는 파티션 키 값이 여러개 걸친 조건으로 

   조회하더라도 인덱스 스캔 상 비효율은 없다. 

 - 하지만 아주 넓은 범위의 파티션 키 컬럼을 조회하거나 등치 조건만으로 조회할 때는 

   테이블 Random 엑세스 부하 때무에 비파티션 인덱스도 제 성능을 내기 어렵다. 

   이럴 때는 병렬 쿼리가 필요할 수 있는데, 아쉽게도 비파티션 인덱스에는 병렬 쿼리가 

   허용되지 않는다. 

 - 로컬 Nonprefixed 파티션 인덱스라면 여러 병렬 프로세스가 각각 하나의 인덱스 세그먼트를 

   스캔하도록 함으로써 위 쿼리의 응답 속도를 크게 향상시킬 수 있다. 


엑세스 효율을 고려한 인덱스 파티셔닝 선택 기준 

 - DW성 애플리케이션 과 OLTP성 애플리케이션 환경 비교 

    ● DW성 애플리케이션 환경 

         - DW/DSS 애플리케이션에는 날짜 컬럼 기준으로 파티셔닝된 이력성 대용량 테이블이 

           많다. 따라서 관리적 측면뿐만 아니라 병렬 쿼리 활용 측면에서도 로컬 파티션 인덱스가 

           좋은 선택이다. 

           ( 비파티션 인덱스는 index fast full scan이 아닌 한 병렬 쿼리에 활용할 수 없다. ) 

         - 로컬 인덱스 중에서는 Nonprefixed 파티션 인덱스가 성능 면에서 유리할 때가 많다.  

    ● OLTP성 애플리케이션 환경 

         - OLTP성 애플리케이션 환경에서는 비파티션 인덱스가 대게 좋은 선택이다.  

         - Right Growing 인덱스에 대한 동시 Insert 경합을 분산할 목적으로 해시 파티셔닝하는 

           경우가 아니라면 글로벌 파티션 인덱스 효용성이 낮다. 

         - OLTP성이더라도 테이블이 파티셔닝돼 있다면 인덱스 파티셔닝을 고려할 수 있는데, 

           특히 로컬 파티션 인덱스는 테이블 파티션에 대한 DDL 작업 후 인덱스를 재생성하지 

           않아도 되므로 가용성 측면에서 유리하다. 

         - 파티션 키에 대한 조건절이 없을 때 로컬 Prefixed 인덱스는 아예 사용이 안 되는 반면 

           Nonprefixed 인덱스는 비효율이 있을지언정 잘 사용된다. 

         - 정리하면, OLTP 환경에서 로컬 인덱스를 선택했다면 Prefixed 파티션이든 

           Nonprefixed 파티션이든 검색 조건에 항상 사용되는 컬럼을 파티션 키로 선정하려고 

           노력해야 한다. 파티션 키가 범위검색 조건으로 자주 사용된다면 Nonprefixed 인덱스가 

           유리하고, 될 수 있으면 좁은 범위검색이어야 한다. 특히, NL 조인에서 파티션 키에 대한 

           넓은 범위검색 조건을 가지고 Inner 테이블 엑세스 용도로 사용된다면 비파티션 인덱스를

           사용해야 한다.


인덱스 파티셔닝 제약을 고려한 데이터베이스 설계 

 - 인덱스 파티셔닝에 대한 반드시 기억해야 하는 2가지 제약 

    ● Unique 파티션 인덱스를 정의할 때는 인덱스 파티션 키가 모두 인덱스 구성 컬럼에 

        포함돼야 한다. 이 제약이 없다면 인덱스 키 값을 변경하거나 새로운 값을 입력할 때마다

        중복 값 체크를 위해 매번 많은 인덱스 파티션을 탐색해야 하므로 DML 성능이 저하된다. 

        따라서 당연히 필요한 제약이라고 할 수 있다. 

    ● 글로벌 파티션 인덱스는 Prefixed 파티션이어야 한다. 

- 파티셔닝은 인덱스 전략 수립과 병행해야 한다. 자주 사용되는 엑세스 패턴과 데이터 분포를 

  고려해 인덱스 전략을 수립하고, 인덱스만으로 빠른 성능을 내기 어려운 엑세스 경로를 파악해 

  테이블 파티셔닝과 인덱스 파티셔닝 전략을 수립해야 한다. 

파티션 Pruning 이란 

 - 파티션 Pruning은 하드파싱이나 실행 시점에 SQL 조건절을 분석하여 읽지 않아도 되는 

   파티션 세그먼트를 엑세스 대상에서 제외시키는 기능이다. 파티션 테이블에 대한 쿼리나 

   DML을 수행할 때 극적인 성능 개선을 가져다주는 핵심 원리가 파티션 Pruning에 있다


기본 파티션 Pruning 

 - 기본 파티션 Pruning 기법 

    ● 정적 파티션 Pruning : 파티션 키 컬럼을 상수 조건으로 조회하는 경우에 작동하며, 

                                       엑세스할 파티션이 쿼리 최적화 시점에 미리 결정되는 것이 

                                       특징이다. 실행계획의 Pstart 와 Pstop 컬럼에 엑세스할 

                                       파티션 번호가 출력된다. 

    ● 동적 파티션 Pruning : 파티션 키 컬럼을 바인드 변수로 조회하면 쿼리 최적화 시점에는

                                       엑세스할 파티션을 미리 결정할 수 없다. 실행 시점이 돼서야 

                                       사용자가 입력한 값에 따라 결정되며, 실행계획의 Pstart와 

                                       Pstop 컬럼에는 'KEY'라고 표시된다. NL 조인할 때도 Inner 테이블이 

                                       조인 컬럼 기준으로 파티셔닝 돼 있다면 동적 Pruning이 작동한다. 

 - 파티션 컬럼에 IN-List 조건을 사용하면 상수 값이더라도 아래처럼 'KEY(I)'라고 표시 된다. 

 - NL조인에서도 inner 테이블일 때 동적 Pruning이 일어난다. 


파티션 Pruning 기능에 따른 I/O 수행량 비교 

 - 주 파티션과 서브 파티션이 있을 때 서브 파티션 키 컬럼을 함수로 가공하면 서브 파티션 

   Pruning을 사용할 수 없다. 

 - 주 파티션 키 컬럼을 가공함으로써 서브 파티션 키 컬럼에 묵시적 형변환이 일어난다. 


동적 파티션 Pruning 시 테이블 레벨 통계 사용 

 - 동적 파티션 Pruning이 일어나면 쿼리 최적화에 테이블 레벨 통계가 사용된다. 

   반면, 정적 파티션 Pruning일 때는 파티션 레벨 통계가 사용된다. 

 - 테이블 레벨 통계는 파티션 레벨 통계보다 다소 부정확하기 때문에 옵티마이저가 가끔 

   잘못된 실행계획을 수립하는 경우가 생기며, 이는 바인드 변수 때문에 생기는 대표적인 

   부작용 중 하나다. 


서브쿼리 Pruning 

 - 조인에 사용되는 고급 파티션 Pruning 기법으로는 아래 두 가지가 있다. 

    ● 서브 쿼리 Pruning (8i ~ ) 

    ● 조인 필터 Pruning (11g ~ )

 - 오라클은 Recursive 서브쿼리를 이용한 동적 파티션 Pruning을 고려한다. 

   이른바 '서브쿼리 Pruning'이라고 불리는 메커니즘으로서, 엑세스해야할 파티션 번호 목록을 

   특별한 쿼리를 통해 구해 이를 이용해 필요한 파티션만 스캔할 수 있다. 

 - KEY(SQ)에서 SQ는 SubQuery를 뜻한다. 이 방식으로 파티션 Pruning 하려면 

   드라이빙 테이블을 한 번 더 읽게 되므로 경우에 따라 총 비요이 오히려 증가할 수 있다. 

   따라서 서브쿼리 Pruning 적용 여부는 옵티마이저가 비용을 고려해 내부적으로 결정한다. 

 - 옵티마이저 결정에 영향을 미치는 Hidden 파라미터는 3가지가 있다. 

    ● _subquery_pruning_cost_factor 

    ● _subquery_pruning_reduction

    ● _subquery_pruning_enabled 

        (위 파라미터의 자세한 설명은 p.649 참조) 

 - 제거될 것으로 예상되는 파티션 개수가 상당히 (기본 값에 의하면 50%) 많고, where 조건절을 

   가진 드라이빙 테이블이 파티션 테이블에 비해 상당히 작을 때만 서브쿼리 Pruning이 작동 

 - 참고로, 아래와 같이 설정하면 옵티마이저에 의해 계산된 비용과 상관없이 항상 서브쿼리 

   Pruning을 실시한다. 

    ● _subquery_pruning_cost_factor =1 

    ● _subquery_pruning_reduction = 100


조인 필터 Pruning 

 - 서브쿼리 Pruning은 드라이빙 테이블을 한 번 더 엑세스하는 추가 비용이 발생한다. 

   그래서 11g부터 오라클은 블룸 필터 알고리즘을 기반으로 한 조인 필터 Pruning 방식을 

   도입하였다. 

 - 블룸 필터는 교집합이 작을 때 큰효과를 발휘한다. 

   * 해당 알고리즘은 p.648을 참고한다. 

 - 블룸 필터 알고리즘에서 false positive를 줄이는 방법은 더 많은 비트를 할당하거나 

   더 많은 해시 함수를 사용하면 false positive 발생 가능성은 줄어든다. 

   공간/시간의 효율성과 false positive 발생 가능성은 서로 트레이드 오프 관계이므로 

   적정한 개수의 비트와 해시 함수를 사용하는 것이 과제다. 


조인 필터 (=블룸 필터) Pruning 

 - '조인 필터 Pruning' 또는 '블룸 필터 Pruning'이라고 부르는 이 기능은 11g부터 적용되기 

   시작했으며 파티션 테이블과 조인할 때 , 읽지 않아도 되는 파티션을 제거하는 것이다. 

 - 실행계획에서 part join filter create 는 블룸 필터를 생성하는 단계 , 

    partition range join-filter는 블룸 필터를 이용해 파티션 Pruning 하는 단계를 나타낸다. 

 - 블룸필터의 역활은, 교집합 아님이 확실한 원소를 찾는 데에 있다. 이 알고리즘을 사용하는 

   조인 필터 Pruning도 조인 대상 집합을 확실히 포함하는 파티션을 찾는 게 아니라, 확실히 

   포함하지 않는 파티션을 찾는다. 

 - 서브쿼리 Pruning 과 비교해 보면, 드라이빙 테이블이 클수록 조인 필터 Pruning이 유리하다.


SQL 조건절 작성 시 주의사항     

 - 파티션 Pruning을 위해서라도 like 보다는 가급적 between 연산자를 이용해 정확한 검색구산을

   지정하는 것이 바람직하다. 

테이블 파티셔닝 

 - 파티셔닝은 테이블과 인덱스 데이터를 파티션 단위로 나누어 저장하는 것을 말한다. 

   테이블을 파티셔닝하면 하나의 테이블일지라도 파티션 키에 따라 물리적으로는 별도의 

   세그먼트에 데이터가 저장되며, 인덱스도 마찬가지다. 

 - 파티셔닝이 필요한 관리적 측면과 성능적 측면 

     ● 관리적 측면 : 파티션 단위 백업, 추가, 삭제, 변경 

     ● 성능적 측면 : 파티션 단위 조회 및 DML 수행 

 - 파티셔닝도 클러스터, IOT와 마찬가지로 관련 있는 데이터가 흩어지지 않고 물리적으로 

   인접하도록 저장하는 클러스터링 기술에 속한다. 

   클러스터와 다른 점은 세그먼트 단위로 모아서 저장한다는 것이다. 클러스터는 블록 단위로 

   데이터를 모아 저장한다. IOT는 정렬된 순서로 데이터를 저장하는 구조인데, IOT와 파티셔닝을 

   조합함으로써 놀라운 성능 효과를 얻을 수 있다. 


파티션 기본 구조 

 - 수동 파티셔닝 

   ● 파티션 테이블이 제공되기 전(7.3), 파티션 뷰를 통해 파티션 기능을 구현

        이를 '수동 파티셔닝'이라고 부른다. 

   ● 파티션 뷰의 핵심 기능은 뷰 쿼리에 사용된 조건절에 부합하는 테이블만 읽는다는 데에 

       있으며, 이를 '파티션 Pruning이라고 한다. 

   ● 10g 나 11g에서는 파티션 뷰에서 바인드 변수 사용 시 파티션 Pruning이 제대로 작동하지

       않는 것으로 확인되었다고 하는데, 이 부분은 개인적인 테스트를 해보아야 할 것 같다. 


  - 파티션 테이블 

   ● 오라클 8에서 도입된 파티션 테이블 기능을 이용하면 훨씬 간편하게 파티션을 저의할 수 

       있을 뿐 아니라 기능적으로도 더 낫다.

   ● partition by 절은 파티션 뷰의 Base 테이블에 체크 제약을 설정하는 것과 같은 역할을 한다. 

   ● 인덱스를 만들 때도 local 옵션을 지정했으므로 각 파티션별로 개별적인 인덱스가 만들어져, 

       파티션 뷰 Base 테이블에 각각 인덱스를 만든 것과 같은 모습이다. 

   ● 버전별 테이블 파티션 유형

        - Range 파티셔닝 (8 이상) 

        - 해시 파티셔닝 (8i 이상) 

        - Range-해시 파티셔닝 (8i 이상)

        - 리스트 파티셔닝 (9i 이상)          

        - Range-리스트 파티셔닝 (9i 이상) 

        - Range-Range 파티셔닝 (11g 이상) 

        - 리스트-해시 파티셔닝 (11g 이상) 

        - 리스트-리스트 파티셔닝 (11g 이상) 

        - 리스트-Range 파티셔닝 (11g 이상)  


 - Range 파티셔닝 

   ● 오라클 8 버전부터 제공된 가장 기초적인 파티셔닝 방식으로서, 

       주로 날짜 컬럼을 기준으로 한다. 

   ● 파티션 키로는 하나 이상의 컬럼을 지정할 수 있고, 최대 16개까지 허용된다. 

   ● DB 관리자의 실수로 신규 파티션 생성을 빠뜨리면 월초 또는 연초에 데이터가 입력되지 

       않는 에러가 발생하므로, maxvalue 파티션을 반드시 생성해 두는 것이 좋다.

   ● 참고로 11g 부터는 Range 파티션을 생성할 때 interval 기준으로 정의함으로써 

       정해진 간격으로 파티션이 자동 추가 되도록 할 수 있다.


 - 해시 파티셔닝 

     ● 해시 파티셔닝은 Range 파티셔닝에 이어 오라클 8i 버전부터 제공되기 시작했다. 

         파티션 키에 해시 함수를 적요한 결과 값이 같은 레코드를 같은 파티션 세그먼트에 

         저장해 두는 방식이며, 주로 변별력이 좋고 데이터 분포가 고른 컬럼을 

         파티션 기준 컬럼으로 선정해야 효과적이다. 

     ● 검색할 때는 조건절 비교 값에 해시 함수를 적용해 읽어야 할 피타션을 결정하며, 

         해시 알고리즘 특성상 등치(=) 조건 또는 IN-List 조건으로 검색할 때만 

         파티션 Pruning이 작동한다. 

     ● 오라클은, 특정 파티션에 데이터가 몰리지 않도록 하려면 파티션 개수를 2의 제곱으로 

        설정할 것을 권고한다. 이 규칙을 따르더라도 파티션 키 컬럼의 Distinct Value 개수가 

        적다면 데이터가 고르게 분산되지 않을 가능성이 높으므로, 이때는 리스트 파티션을 

        이용해 파티션 기준을 사용자가 수동으로 결정해 주는 것이 낫다. 

     ● 데이터가 모든 파티션에 고르게 분산돼 있다면, 더구나 각 파티션이 서로 다른 디바이스에

         저장 돼 있다면 병렬 I/O 성능을 극대화할 수 있다. 반대로 말하면, 데이터가 고르게 

         분산되지 않을 때는 병렬 쿼리 효과가 반감된다. 

     ● 해시 파티셔닝의 DML 경합 분산 

         - 동시에 입력이 많은 대용량 테이블이나 인덱스에 발생하는 경합을 줄일 목적으로도 

           해시 파티셔닝을 사용한다. 대용량 거래 테이블일수록 DML 발생량이 많아 경합 발생 

           가능성도 그만큼 크다.

         - 데이터가 입력되는 테이블 블록에도 경합이 발생할 수 있지만, 그보다는 입력할 블록을 

           할당받기 위한 Freelist 조회 때문에 세그먼트 헤더 블록에 대한 경합이 더 자주 발생한다. 

           그럴때 테이블 해시 파티셔닝하면 세그먼트 헤더 블록에 대한 경합을 줄일 수 있다. 

         - Right Growing 인덱스도 자주 경합 지점이 되곤 하는데, 

           이때도 인덱스를 해시 파티셔닝함으로써 경합 발생 가능서을 낮출 수 있다. 

         - 경합 분산이나 병렬 쿼리 성능 향상, 두 가지 모두 트랜잭션이 많이 발생하는 대용량 거래

           테이블일 대라야 효과가 있다. 단일 해시 파티셔닝보다는 Range와 해시를 조합한 

           결합 파티셔닝을 주로 사용하게 되는 이유가 여기에 있다. 


리스트 파티셔닝 

  - 오라클 9i 버전부터 제공되기 시작한 파티셔닝은, 사용자에 의해 미리 정해진 그룹핑 

    기준에 따라 데이터를 분할 저장하는 방식이다. 

  - Range 파티션에선 값의 순서에 따라 저장할 파티션이 결정되지만, 리스트 파티션에서는 

    순서와 상관없이 불연속적인 값의 목록으로써 결정된다. 

  - Range, 해시 파티셔닝과 달리 리스트 파티셔닝에는 단일 컬럼으로만 파티션 키를 지정할 수 

    있다. 그리고 Range 파티션에 maxvalue 파티션을 반드시 생성하라고 권고한 것과 같은 

    이유로, 리스트 파티션에도 default 파티션을 생성해 두어야 안전하다. 

   * range 와 구분할 때 

     range : 범위 

    리스트 : in과 같은 개념 

    으로 생각하면 편할 듯 하다. 


결합 파티셔닝 

 - 결합 파티셔닝을 구성하면 서브 파티션마다 세그먼트를 하나씩 할당하고, 

   서브 파티션 단위로 데이터를 저장한다. 즉, 주 파티션 키에 따라 1차적으로 데이터를 분배하고, 

   서브 파티션 키에 따라 최종적으로 저장할 위치를 결정한다. 

 - 11g부터는 주 파티션이 해시 방식이 아닌 모든 조합을 지원한다. 

    ● Range + 해시 결합 파티셔닝 

        - 대용량 거래 테이블을 이와 같이 파티셔닝한다면 Range 파티셔닝과 해시 파티셔닝의 

          장점을 둘 다 누릴 수 있으며, 각각의 장점에 대해서는 이미 설명하였다. 

    ● Range + 리스트 결합 파티셔닝   

        - Rnage + 리스트 결합 파티셔닝은 논리적으로 하나의 테이블이지만 물리적으로 

          별도 세그먼트에 나눠 저장하기 때문에 성능을 떨어뜨리지 않으면서 통합의 이점을 

          누릴 수 있는 것이다.


11g에 추가된 파티션 유형

 - Reference 파티셔닝 

    ● 11g에서 부모 테이블 파티션 키를 이용해 자식 테이블을 파티셔닝하는 기능이 

        도입되었는데, 이를 'Reference 파티션'이라고 부른다. 

    ● 이 기능을 사용하려면 자식 테이블의 컬럼에 not null과 FK 제약이 설정돼 있어야 한다. 

 - Interval 파티셔닝 

    ● 11g 부터는 Range 파티션을 생성할 때 아래와 같이 interval 기준을 저의함으로써 

        정해진 간격으로 파티션이 자동 추가되도록 할 수 있다. 

  - 이 외에도 시스템 파티셔닝, 가상(virtual) 컬럼 파티셔닝 등이 11g에 추가되었다. 

Sort Area 크기 조정 

 - Sort Area 크기 조정을 통한 튜닝의 핵심은, 디스크 소트가 발생하지 않도록 하는 것을 

   1차 목표로 삼고 불가피할 때는 Onepass 소트로 처리되도록 하는 데에 있다. 


PGA 메모리 관리 방식의 선택 

 - 데이터 정렬, 해시 조인, 비트맵 머지, 비트맵 생성 등을 위해 사용하는 메모리 공간을 

   'Work Area'라고 부르며, sort_area_size, hash_area_size, bitmap_merge_area_size, 

   create_bitmap_area_size 같은 파라미터를 통해 조정한다. 


자동 PGA 메모리 관리 방식 하에서 크기 결정 공식 

 - auto 모드에서 단일 프로세스가 사용할 수 있는 최대 Work Area 크기는 인스턴스 기동 시 

   오라클에 의해 내부적으로 결정되며, _smm_max_size 파라미터를 통해 확인 가능하다. 

 - SGA는 sga_max_size 파라미터로 설정된 크기만큼 공간을 미리 할당한다. 이와 대조적으로, 

   PGA는 자동 PGA 메모리 관리 기능을 사용한다고 해서 pga_aggregate_target 크기 만큼의 

   메모리를 미리 할당해 두지는 않는다. 이 파라미터는 workarea_size_policy를 auto로 설정한 

   모든 프로세스들이 할당받을 수 있는 Work Area의 총량을 제한하는 용도로 사용된다. 


수동 PGA 메모리 관리 방식으로 변경 시 주의사항 

 - manual 모드로 설정한 프로세스는 pga_aggregate_target 파라미터의 제약을 받지 않는다. 

   따라서 manual 모드로 설정한 많은 세션에서 Sort Area와 Hash Area를 아주 큰 값으로 

   설정하고 실제 매우 큰 작업을 동시에 수행한다면 가용한 물리적 메모리가 고갈돼 

   페이징이 발생하면서 시스템 전체 성능을 크게 떨어뜨릴 수 있다. 

 - 특히, workarea_size_policy 파라미터를 manual로 설정한 상태에서 병렬 쿼리를 사용하면 

   각 병렬 슬레이브 별로 sort_area_size 크기만큼 Sort Area를 사용할 수 있다는 사실을 

   바드시 기억해야 한다.  

 - sort order by나 해시 조인등을 수행할 때는 사용자가 지정한 DOP의 2배수만큼의 병렬 Slave가

   떠서 작업을 수행하므로 위 쿼리가 수행되는 동안 128개의 프로세스가 각각 최대 2GB의 

   Sort Area를 사용할 수 있다. 

 - 따라서 manual 모드에서 병렬 Degree를 크게 설정할 때는 sort_area_size와 hash_area_size를 

   반드시 확인해야 한다. 


PGA_AGGREGATE_TARGET의 적정 크기 

 - 오라클의 권고 값 

     OLTP 시스템 : ( Total Physical Memory x 80% ) x 20 %  

     DSS 시스템 : ( Total Physical Memory x 80% ) x 50 % 

 - 이것은 일반적인 권고사하일 뿐이며 애플리케이션 특성에 따라 모니터링 결괄르 바탕으로 

   세밀한 조정이 필요하다. 대부분 Optimal 소트 방식으로 수행되고 나머지 일부만 

   Onepass 소트 방식으로 수행되는 것을 목표로 삼아야 한다. 

소트를 완료하고 나서 데이터 가공하기 

 - select 절에 여러 컬럼들을 붙여서 사용하면서 order by를 사용할 경우 

   차라리 order by 를 먼저 실행한 후 여러 컬럼들을 붙이는 것이 sort area 영역을 적게 사용한다.


Top-N 쿼리 

 - Top-N 쿼리 형태로 작성하면 소트 연산 횟수를 최소화함은 물론 Sort Area 사용량을 줄일 수 

   있다. ( order by 와 where 절 rownum 사용 )

    * 만약 rownum 이 아니라 일반 순번 컬럼을 사용 시 해당 효과를 얻을 수느 ㄴ없다. 


분석함수에서 Top-N 쿼리 

 - window sort 시에도 rank()나 row_number( )를 쓰면 Top-N 쿼리 알고리즘이 작동해 

   max( )등 함수를 쓸 때보다 소트 부하를 경감시켜 준다. 

Sort Order by 대체 

 - where A=값 order by B 일 때 A+B 인덱스를 사용한다면 sort order by 연산을 대체할 수 있다. 

   ( A 만 있거다 A와 B 사이에 다른 컬럼이 있으면 sort order by 연산을 대체 할 수 없다. ) 

 - 물론, 소트해야 할 대상 레코드가 무수히 많고 그 중 일부만 읽고 멈출 수 있는 업무에서만 

   이 방식이 유리하다. 인덱스를 스캔하면서 결과집합을 끝까지 Fetch 한다면 오히려 I/O 및 

   리소스 사용 측면에서 손해다. 대상 레코드가 소량일 때는 소트가 발생하더라도 

   부하가 크지 않아 개선 효과도 미미하다. 


Sort Group By 대체 

 - group 절에 사용 된 컬럼이 선두인 결합 인덱스나 단일 컬럼 인덱스를 사용한다면 

   sort group by 연산을 대체할 수 있다. 

   ( 'sort group by nosort' 가 실행계획으로 표시 된다. ) 

 - 이처럼 인덱스를 이용한 nosort 방식으로 수행될 때는 group by  오퍼레이션에도 불구하고 

   부분범위처리가 가능해져 OLTP환경에서 매우 극적인 성능 개선 효과를 얻을 수 있다. 


인덱스가 소트 연산을 대체하지 못하는 경우 

 - 인덱스 사용보다 full table scan이 효과적일 때 

 - 인덱스를 사용할 해당 컬럼에 not null 제약이 추가 되지 않았을 때 

    ( group by도 마찬가지다. group by nosort를 우해 사용하려는 인덱스가 단일 컬럼 

      인덱스일 때는 해당 컬럼에 not null 제약이 설정돼 있ㅇ야 제대로 작동한다. ) 

 - order by 절에 nulls first 구문을 사용하는 경우 

   ( 마찬가지 원리로 인덱스를 뒤쪽부터 읽으려고 index_desc 힌트를 쓰면 null 값들이 

    먼저 출력 될텐대, null 값들을나중에 출력하려고 nulls last 구문을 사용하면 소트가 발생한다.)

+ Recent posts