기본 개념
- 병렬처리란, 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 리소스를 낭비하는 것이므로
가급적 병렬 쿼리를 사용하지 않는 편이 낫고, 후자의 경우는 애플리케이션 로직을
튜닝할 수 있는지 검토해 봐야 한다.