버퍼 캐시 히트율이 낮은 대용량 데이터를 건건이 버퍼 캐시를 거쳐 읽는다면 오히려 성능이 나빠지게 마련이다.
오라클은 병렬 방식으로 Full Scan 할 때는 버퍼 캐시를 거치지 않고 곧바로 PGA영역으로 읽어 들이는 Direct Path Read방식을 사용한다.
버퍼캐시에 충분히 적재될 크기의 중소형 테이블을 병렬쿼리로 읽을 때 오히려 성능이 나빠지는 경우가 있는데, 버퍼경합이 없는 한 Disk i/o가 메모리 i/o보다 빠를 수 없기 때문이다. 게다가 Direct Path Read를 하려면 메모리와 디스크간 동기화를 맞추기 위해서 Object레벨의 Partitial 체크포인트가 먼저 수행해야 하기 때문이다.
Alter session enable parallel query;
Alter session enable parallel dml;
Alter session enable parallel ddl;
다행히 parallel query와 parallel ddl은 기본적으로 활성화 되어 있지만 parallel dml은 명시적으로 활성화 해주어야 한다.
SQL> explain plan for
2 update /*+ parallel(t 4) */ t
3 set no2 = lpad(no,5,'0');
해석되었습니다.
SQL> select * from table(dbms_xplan.display());
---------------------------------------------------------------------------------------
| Id | Operation | Name | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
---------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | | | | | |
| 1 | UPDATE | T | | | | | |
| 2 | PX COORDINATOR | | | | | | |
| 3 | PX SEND QC (RANDOM)| :TQ10000 | | | Q1,00 | P->S | QC (RAND) |
| 4 | PX BLOCK ITERATOR | | 1 | 4 | Q1,00 | PCWC | |
| 5 | TABLE ACCESS FULL| T | 1 | 4 | Q1,00 | PCWP | |
---------------------------------------------------------------------------------------
SQL> alter session enable parallel dml;
세션이 변경되었습니다.
SQL> explain plan for
2 update /*+ parallel(t 4) */ t
3 set no2 = lpad(no,5,'0');
해석되었습니다.
SQL> select * from table(dbms_xplan.display());
---------------------------------------------------------------------------------------
| Id | Operation | Name | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
---------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | | | | | |
| 1 | PX COORDINATOR | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10000 | | | Q1,00 | P->S | QC (RAND) |
| 3 | UPDATE | T | | | Q1,00 | PCWP | |
| 4 | PX BLOCK ITERATOR | | 1 | 4 | Q1,00 | PCWC | |
| 5 | TABLE ACCESS FULL| T | 1 | 4 | Q1,00 | PCWP | |
---------------------------------------------------------------------------------------
오라클 9iR1까지는 한 세그먼트를 두 개 이상의 프로세스가 동시에 갱신할 수 없었다. 따라서 파티션되지 않은 테이블이라면 병렬로 갱신 할 수 없었고, 파티션 테이블일 때라도 병렬도를 파티션 개수 이하로만 지정할 수 있었다.
주의할 점
병렬 DML을 수행 할 때 Exclusive 모드 테이블 lock이 걸린다는 사실이며, 다른 트랜젝션이 DML을 수행하지 못하므로 운영환경을 고려하여 사용해야 함.
Index Fast Full Scan이 아닌 한 인덱스는 기본적으로 병렬로 스캔 할 수 없다.
파티션된 인덱스일 때 병렬 스캔이 가능하며 파티션 기반 Granule이므로 병렬도는 파티션개수 이하로만 지정가능 함.
병렬조인은 항상 Table Full Scan을 이용한 해쉬 조인 또는 소트머지 조인으로 처리된다고 생각하기 쉽다. 하지만 인덱스 기반의 병렬 NL조인도 가능
SQL> select /*+ ordered use_nl(d) full(e) parallel(e 2) */ *
2 from emp1 e, dept d
3 where d.deptno = d.deptno
4 and e.sal >= 1000;
---------------------------------------------------------------------------------------
| Id | Operation | Name | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | | | |
| 1 | PX COORDINATOR | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10000 | | | Q1,00 | P->S | QC (RAND) |
| 3 | NESTED LOOPS | | | | Q1,00 | PCWP | |
| 4 | PX BLOCK ITERATOR | | 2 | 4 | Q1,00 | PCWC | |
|* 5 | TABLE ACCESS FULL| EMP1 | 2 | 4 | Q1,00 | PCWP | |
| 6 | TABLE ACCESS FULL | DEPT | | | Q1,00 | PCWP | |
---------------------------------------------------------------------------------------
Parallel Full Scan은 블록 기반 Granule이 사용되므로 병렬도는 파티션 개수와 무관하다. 단 너무 크게 병렬도를 잡으면 아무 일도 안 하는 노는 프로세스가 생길 수 있으므로 주의해야 한다.
파티션된 인덱스부터 드라이빙하여 병렬 NL조인을 수행
SQL> select /*+ ordered use_nl(d) index(e emp_sal_idx)
2 parallel_index(e emp_sal_idx 3) */ *
3 from emp1 e, dept d
4 where d.deptno = d.deptno
5 and e.sal >= 1000;
--------------------------------------------------------------------------------------
| Id | Operation | Name | Pstart| Pstop | TQ |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | |
| 1 | PX COORDINATOR | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10000 | | | Q1,00 |
| 3 | NESTED LOOPS | | | | Q1,00 |
| 4 | PX PARTITION RANGE ITERATOR | | 2 | 4 | Q1,00 |
| 5 | TABLE ACCESS BY LOCAL INDEX ROWID| EMP1 | 2 | 4 | Q1,00 |
|* 6 | INDEX RANGE SCAN | EMP_SAL_IDX | 2 | 4 | Q1,00 |
| 7 | TABLE ACCESS FULL | DEPT | | | Q1,00 |
--------------------------------------------------------------------------------------
병렬 NL 조인의 필요한 경우
QC의 트레이스는 'user_dump_dest'에 생성되고
슬레이브의 트레이스는 'Background_dump_dest'에 생성된다.
함수에 parallel_enable 키워드를 사용하든 안하든 병렬로 처리되는 것과는 연관관계가 없다.
SQL> select /*+ parallel(t1 24) */ no, no2
2 from t1
3 order by no2;
----------------------------------------------------------------
| Id | Operation | Name |IN-OUT| PQ Distrib |
----------------------------------------------------------------
| 0 | SELECT STATEMENT | | | |
| 1 | PX COORDINATOR | | | |
| 2 | PX SEND QC (ORDER) | :TQ10001 | P->S | QC (ORDER) |
| 3 | SORT ORDER BY | | PCWP | |
| 4 | PX RECEIVE | | PCWP | |
| 5 | PX SEND RANGE | :TQ10000 | P->P | RANGE |
| 6 | PX BLOCK ITERATOR | | PCWC | |
| 7 | TABLE ACCESS FULL| T1 | PCWP | |
----------------------------------------------------------------
SQL> select /*+ parallel(t1 24) */ no, no2, rownum
2 from t1
3 order by no2;
---------------------------------------------------------------
| Id | Operation | Name |IN-OUT| PQ Distrib |
---------------------------------------------------------------
| 0 | SELECT STATEMENT | | | |
| 1 | SORT ORDER BY | | | |
| 2 | COUNT | | | |
| 3 | PX COORDINATOR | | | |
| 4 | PX SEND QC (RANDOM)| :TQ10000 | P->S | QC (RAND) |
| 5 | PX BLOCK ITERATOR | | PCWC | |
| 6 | TABLE ACCESS FULL| T1 | PCWP | |
---------------------------------------------------------------
병렬처리가 바람직한 경우
병렬쿼리와 관계된 주의사항