행위

병렬 쿼리 튜닝

DB CAFE

목차

1 병렬 쿼리 튜닝

1.1 병렬처리 핵심은?

1.1.1 그래뉼

android The basic unit of work in parallelism is a called a granule.

Oracle Database divides the operation executed in parallel (for example, a table scan, table update, or index creation) into granules.


  1. 병렬로 처리할때 일의 최소 단위
  2. 병렬 쿼리 granule
1.1.1.1 블록 그래뉼
  1. PX BLOCK ITERATOR 라고 표시
  2. QC는 테이블로부터 읽어야할 범위의 블록 GRANULE로서 각 병렬 서버에게 할당
-------------------------------------------------------------------------------------------------
|Id|      Operation          |  Name  |Rows|Bytes|Cost%CPU|  Time  |Pst|Pst|  TQ |INOUT|PQDistri|
-------------------------------------------------------------------------------------------------
| 0|SELECT STATEMENT         |        |  17| 153 |565(100)|00:00:07|   |   |     |     |        |
| 1| PX COORDINATOR          |        |    |     |        |        |   |   |     |     |        |
| 2|  PX SEND QC(RANDOM)     |:TQ10001|  17| 153 |565(100)|00:00:07|   |   |Q1,01|P->S |QC(RAND)|
| 3|   HASH GROUP BY         |        |  17| 153 |565(100)|00:00:07|   |   |Q1,01|PCWP |        |
| 4|    PX RECEIVE           |        |  17| 153 |565(100)|00:00:07|   |   |Q1,01|PCWP |        |
| 5|     PX SEND HASH        |:TQ10000|  17| 153 |565(100)|00:00:07|   |   |Q1,00|P->P | HASH   |
| 6|      HASH GROUP BY      |        |  17| 153 |565(100)|00:00:07|   |   |Q1,00|PCWP |        |
==========> 블록 이터레이터로 표시됨 
| 7|       PX BLOCK ITERATOR |        | 10M| 85M | 60(97) |00:00:01| 1 | 16|Q1,00|PCWC |        |
|*8|        TABLE ACCESS FULL|  SALES | 10M| 85M | 60(97) |00:00:01| 1 | 16|Q1,00|PCWP |        |
-------------------------------------------------------------------------------------------------
1.1.1.2 파티션 그래뉼
  1. PX PARTITION RANGE ALL 또는 PX PARTITION RANGE ITERATOR 라고 표시
  2. 사용되는 시기
    1. Partition-Wise조인 시
    2. 파티션 인덱스를 병렬로 스캔할 시
    3. 파티션 인덱스를 병렬로 갱신할 때
    4. 파티션 테이블 또는 파티션 인덱스를 병렬로 생성할 때
---------------------------------------------------------------------------------------------------
|Id|      Operation                |  Name  |Rows|Byte|Cost%CPU|  Time  |Ps|Ps|  TQ |INOU|PQDistri|
---------------------------------------------------------------------------------------------------
| 0|SELECT STATEMENT               |        |  17| 153|   2(50)|00:00:01|  |  |     |    |        |
| 1| PX COORDINATOR                |        |    |    |        |        |  |  |     |    |        |
| 2|  PX SEND QC(RANDOM)           |:TQ10001|  17| 153|   2(50)|00:00:01|  |  |Q1,01|P->S|QC(RAND)|
| 3|   HASH GROUP BY               |        |  17| 153|   2(50)|00:00:01|  |  |Q1,01|PCWP|        |
| 4|    PX RECEIVE                 |        |  26| 234|    1(0)|00:00:01|  |  |Q1,01|PCWP|        |
| 5|     PX SEND HASH              |:TQ10000|  26| 234|    1(0)|00:00:01|  |  |Q1,00|P->P| HASH   |
==========> 파티션 RANGE ... 로 표시됨 
| 6|      PX PARTITION RANGE ALL   |        |  26| 234|    1(0)|00:00:01|  |  |Q1,00|PCWP|        |
| 7|       TABLEACCESSLOCAL INDEX ROWID|SALES| 26| 234|    1(0)|00:00:01| 1|16|Q1,00|PCWC|        |
|*8|        INDEX RANGE SCAN       |SALES_CUST|26|    |    1(0)|00:00:01| 1|16|Q1,00|PCWP|        |
---------------------------------------------------------------------------------------------------

1.1.2 생산자 VS 소비자

?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy9Qa0%2FbtqytV7dIAq%2FK95i8dY3X43nzrZhvhkPrk%2Fimg.png

1.1.3 DOP 많을수록 좋을까?

  1. 테스트 예제

1.1.4  병렬퀴리튜닝의 관점은? 병렬의 갯수인가? 분산의 정도인가?

각 병렬 프로세스들이 독립적으로 일 할 수 있도록 데이터 적절하게 분배


  1. 튜닝 사례

1.1.5 병렬 PLAN 해석하는 방법

-- 병렬 쿼리 확인 
select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST ADAPTIVE PARTITION PARALLEL OUTLINE'));
--------------------------------------------------------------------------------------------------------------------
| 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 |            |
--------------------------------------------------------------------------------------------------------------------


  1. S->P 직렬 PARALLEL_FROM_SERIAL QC가 읽은 데이터를 테이블 큐를 통해 병렬서버 프로세스에게 전송
  2. P->S 병렬 PARALLEL_TO_SERIAL 각 병렬서버 프로세스가 처리한 데이터를 QC에게 전송
  3. P->P 병렬 PARALLEL_TO_PARALLEL 데이터를 재분배(redistribution)하는 오퍼레이션 ,데이터를 정렬 또는 그룹핑하거나 조인을 위해 동적으로 파티셔닝할 때 사용
  4. PCWP 병렬 PARALLEL_COMBINED_WITH_PARENT 한 서버집합이 현재스텝과 부모스텝을 모두 처리
  5. PCWC 병렬 PARALLEL_COMBINED_WITH_CHILD 한 서버집합이 현재스텝과 자식스텝을 모두 처리
  6. [공백] 직렬 SERIAL 공백인 경우 SERIAL 방식으로 처리



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




1.1.5.1 SQL 플랜상 튜닝 검토 사항

android

  1. broadcast 는 소량테이블에 적합
  2. hash 는 대량 테이블에 적합
  3. S->P 는 튜닝 대상임. P->P로 바꿀수 있는 방법을 검토.sql수정도 좋다.
  4. round-robin 은 튜닝 대상임
  5. merge 나 insert ,delete문에만 주로 Parallel 힌트 사용. 하위 select 문에서는 가급적 지양 , 1:1 관계를 지향한다
  6. part key 플랜도 튜닝 대상. pq_disiribute(A hash hash) 힌트로 검토


  • 오라클은 내부적 으로 어떤 힌트를 사용하고 있는지 볼까 ?
    • OUTLINE

1.2 병렬처리 올바른 사용법은?

  1. 병렬도를 같게 지정하는 것이 바람직 함.
  2. 테이블별 개별 힌트, PARALLEL(A 8) 보다 글로벌 힌트, PARALLEL(8)로 적용

1.2.1 병렬 힌트를 어디에 어떻게 써야 하나?


1.2.2 DDL, DML 병렬처리

  1. DML
    1. SELECT
SELECT 
-- 튜닝 전
/* FULL(BA) FULL (BB FULL (BC FULL (BD)
PARALLEL(BA 16) PARALLEL (BB 16) PARALLEL (BC 16) PARALLEL (BD 16) */
-- 튜닝 후 
/*+ PARALLEL(16) 
LEADING(BB) USE HASH(BA BB BC BD )
FULL(BA) PO DISTRIBUTE (BA HASH HASH) NO SWAP JOIN INPUTS(BA)
FULLIBB) PO DISTRIBUTE(BB HASH HASH) SWAP_ JOIN_INPUTS(BB)
FULL(BC) PQ_DISTRIBUTE (BC HASH HASH) NO_SWAP_JOIN_INPUTS(BC)
FULL (BD) PQ DISTRIBUTE (BD HASH HASH) NO SWAP JOIN INPUTS (BD)
*/
....
 FROM TB_FMMBR_MANT -- 수급자정보 20G
    , TB_WLFM BB    -- 5G
    , TB_WLFMBR BC  -- 8G
    , TB_IDMB BD    -- 13G
WHERE 1 = 1
AND BA.QLFC_ID IN (
SELECT DISTINCT OLFC_ID
......
    1. INSERT
      1. 병렬 힌트 적용 되는 경우
INSERT /*+ PARALLEL (4) ENABLE_PARALLEL_DML */ -- PQ_DISTRIBUTE(T NONE)
  INTO UWM_WM_DTLS T
      1. 병렬 힌트 적용 안되는 경우
-- insert 앞에 힌트 
/*+ PARALLEL (4) ENABLE_PARALLEL_DML*/ -- PQ_DISTRIBUTE(T NONE)
INSERT INTO
UWM_WM_DTLS T
.... 

-- into 뒤에 힌트
INSERT INTO /*+ PARALLEL (4) ENABLE_PARALLEL_DML */ -- PQ_DISTRIBUTE(T NONE)
UWM_WM_DTLS T
....
    1. UPDATE
    2. DELETE
  1. DDL
     : CREATE TABLE [테이블명] (.......) PARALLEL ( DEGREE [병렬범위] ) ;
     : CREATE TABLE [인덱스명] PARALLEL ( DEGREE [병렬범위] ) ;
     : ALTER TABLE [테이블명] PARALLEL ( 병렬범위 ) ;
     : ALTER INDEX[테이블명] PARALLEL ( 병렬범위 ) ;
    1. CREATE 예시
-- 테이블 생성 
CREATE TABLE name ( column1 [data-type], ... )
Parallel 8;

-- 인덱스 생성 DOP 32 
CREATE INDEX CYKIM.IX_TBL_X01
          ON (MY_TABLE)
TABLESPACE TS_CY PARALLEL 32 NOLOGGING;
ALTER INDEX CYKIM.IX_TBL_X01 NOPARALLEL LOGGING;
    1. ALTER 예시
ALTER TABLE name Parallel 8 ;

1.2.3 DML문 에서도 SELECT 절만 PARALLEL 힌트를 사용하면 될까?

  1. DML에서 CONVENTIONAL / DIRECT PATH 차이점?
    1. Conventional Path
       :버퍼캐시에 적재, Default 방식
      1. 처리 속도 느림
      2. Row-level Lock
    2. Direct Path
       :병렬방식으로 Full Scan 할 때는 버퍼 캐시를 거치지 않고 곧바로 PGA 영역으로 읽어들이는 Direct Path Read 방식을 사용
      1. Table-level lock
      2. /*+ append */

12c 에서 처리 방법

  1. DML 병렬 처리
    1. DML 작업에서는 Paralle 힌트를 주어도 QC 가 작업 담당
    2. 병렬 DML 가능하도록 처리
      1. 12c 이전 에는 세션에 적용
SQL> alter session enable parallel dml;
      1. 12c 부터는 힌트로 적용
/*+ ENABLE_PARALLEL_DML */

  1. DML 병렬 처리시 주의사항

android

  1. DML 병렬 처리시 주의사항
    1. 테이블 전체에 Exclusive 모드로 Lock 획득하므로 주의
    2. 커밋/롤백을 해야 SELECT 가능 함.


  • DML 처리시 플랜에서 항상 QC 아래에 INSERT/UPDATE/DELETE 가 존재 해야 한다. (QC가 아닌 병렬서버에서 처리 토록 해야 한다.)

1.3 진행중인 병렬 처리가 잘되는지 궁금한데 ?

1.3.1 관련 뷰

  1. DISK I/O 확인
- V$SESS_IO
  1. LONG OPS
- V$SESSION_LONGOPS
  1. CURRENT STATMENT
- V$PX_SESSION

1.3.2 토드에서 모니터링 하는 방법

  1. Database - Session Browser
    1. IO 탭
    2. Waits 탭
    3. Current Statsment 탭
    4. Long Ops 탭

1.3.3 REAL MONITOR

  1. 사용법
SELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR('5yfzxpu5593jw') FROM DUAL;
  • html
select dbms_sqltune.report_sql_monitor(sql_id=>''5yfzxpu5593jw'',type=>'html', report_level=>'ALL') from dual;

1.3.4 병렬_쿼리_모니터링

  1. V$PQ_TQSTAT oracle ref.

1.3.5 병렬 처리 WAIT EVENT 확인 방법은?

1.3.5.1 병렬 처리 대기이벤트 종류
  1. PX Deq: Parse Reply
    - PEC 가 PES 에게 파싱 요청을 한 후 응답이 올 때까지 대기하는 이벤트
    - 10G 에서 도입된 PSC(Parallel Single Cursor) 모델에서는 PEC가 생성한 커서를 공유하기 때문에 이러한 과정은 생략된다.
    - 단. RAC 에서는 여전히 PEC 와 다른 노드에 존재하는 PES는 PEC가 생성한 SQL문을 파싱하는 역할을 수행
  2. PX Deq: Execute Reply
    - PEC가 가장 보편적으로 대기하는 이벤트, PES의 실제 작업이 끝나기를 기다리는 대기이벤트이다.
    - 즉 PEC가 PES가 작업을 끝낸 후 데이터를 보내주기를 기다리는 동안 이 이벤트를 대기 한다.
  3. PX Deq Credit : need buffer
    - PEC / PES 간, PES / PES 간의 통신은, 프로세스 간 존재하는 테이블 큐(Table Q)를 통해 이루어진다.
    - 가령 PES 가 테이블 큐에 데이터를 집어넣으면, PEC 가 테이블 큐에서 그 데이터를 빼가는 형식이다.
    - 오라클은 두 프로세스 중 한 순간에 오직 하나의 프로세스만이 테이블 큐에 데이터를 집어넣을 수 있도록 보장한다.
    - 테이블 큐에 데이터를 집어넣을 수 있는 자격을 확보할 때까지 기다리는 이벤트다.
  4. PX Deq: Execution Msg
    - PES 에게 가장 보편적인 대기 이벤트, PES 가 어떤 작업을 수행하기 위한 메시지를 기다리는 이벤트
    - 병렬 실행에 관계 하는 각 PES들은 특정 작업이 자신에게 할당될 때까지 기다려야 하며,
    그 동안 PX Deq: Execution Msg 이벤트를 대기한다.
  5. PX Deq: Table Q Normal
    - PES 가 테이블 큐에 데이터가 들어오기를 기다리는 이벤트
    - PES 가 다른 PES 로부터 데이터를 받아서 작업을 수행해야 하는 경우에 보편적으로 발생하는 이벤트
    - 생산자/소비자(Producer/Consumer)
    - SELECT /*+ PARALLEL ... */ FROM TABLE A ORDER BY NAME 과 같은 형태(정렬작업 필요한)의 병렬작업을
    수행하면 테이블로부터 데이터를 패치 하는 생산자 PES 와 패치된 데이터를 받아서 소비(ORDER BY) 하는 소비자 PES 가 협력하는 방식으로 작동
  6. direct path read
    - 버퍼 캐시를 경유하지 않고 데이터 파일로부터 직접 데이터를 읽는 과정에서 발생하는 이벤트
    - PES 가 테이블로부터 데이터를 페치하는 작업은 대부분 데이터 파일에서 직접 데이터를 읽는 방식을 사용한다.
  7. enq: TC Contention
    - PES 가 Direct Path I/O를 수행하려면, 해당 테이블에 대한 체크 포인트(Checkpoint)작업이 선행 되어야 한다.
    - 버퍼 캐시의 더티 버퍼가 모두 데이타 파일에 기록되어야 버퍼 캐시를 경유하지 않고 데이터 파일에서 직접 데이터를 읽을 수 있기 때문이다.
    - PEC는 PES 에게 작업을 지시하기 전에 체크포인트 요청을 하고 작업이 끝날 때 까지 기다려야 하며 그 동안 enq: TC Contention 이벤트 대기

1.3.5.2 병렬 세션 대기,대기 이벤트,대기 클래스 조회
SELECT DECODE(A.QCSERIAL#, NULL, 'PAREMT', 'CHILD') ST_LVL,
       A.SERVER_SET "SET",
       A.SID,
       A.SERIAL#,
       STATUS,
       EVENT,
       WAIT_CLASS
FROM   V$PX_SESSION   A,
       V$SESSION      B
WHERE  A.SID     = B.SID
AND    A.SERIAL# = B.SERIAL#
ORDER  BY A.QCSID,
          ST_LVL DESC,
          A.SERVER_GROUP,
          A.SERVER_SET
;
1.3.5.3 WAIT EVENT - 상세 대기시간 확인
  • V$SESSION + V$SESSSION_EVENT
SELECT S.INST_ID, S.SID, S.SQL_ID
     , S.MACHINE, S.PROGRAM, S.EVENT
     , S.BLOCKING_SESSION_STATUS
     , SE.EVENT AS EVENT_DTL
     , SE.WAIT_CLASS , SE.TIME_WAITED
     , SE.MAX WAIT   , SE.TOTAL_TIMEOUTS
  FROM GV$SESSION S
     , GV$SESSION_EVENT SE
 WHERE S.SID = SE.SID
   AND S.INST ID = 1
   AND S SID=8281
-- AND SE.EVENT = 'gc current grant busy'
-- AND S.SQL ID = ''
   AND SOL_ID IS NOT NULL
ORDER BY SE.TIME_WAITED DESC
1.3.5.4 대기항목별 WAIT CLASS 확인
  • V$SESSION_WAIT + V$SESSSION_WAIT_CLASS
SELECT A.SID, A.SEQ#, A.EVENT,P1TEXT
     , A.STATE , A.WAIT_TIME_MICRO
     , A.TIME_REMAINING_MICRO
     , B.WAIT_CLASS#, B.WAIT_CLASS
     , B.TOTAL_WAITS, B. TIME_WAITED
  FROM GV$SESSION WAIT A
     , GV$SESSION_WAIT_CLASS B
 WHERE A.INST_ID = B.INST_ID 
   AND A.INST_ID =1
   AND A.SID = B.SID
   AND A.SID = :SID -- 1510
   AND A.SERIAL# = B.SERIAL#

1.4 병렬 처리가 왜 안되지 ?

1.4.1 병렬 처리 갯수 확인

  • DOP 최대 갯수 = parallel_threads_per_cpu * cpu_count
SELECT *
  FROM V$PARAMETER
 WHERE NAME in ('parallel_threads_per_cpu','cpu_count')
  • DOP 최대 제약
SELECT * FROM V$PARAMETER 
 WHERE NAME IN ('parallel_degree_limit') ; -- CPU
  • DOP 최적 갯수
/*+ PARALLEL(auto) */
--- 플랜에서 확인 --
Note
I
automatic DOP: Computed Degree of Parallelism is 266
----------------------------------------------------

1.4.2 병렬 환경 파리미터 상세 조회

SELECT *
  FROM V$PARAMETER
 WHERE NAME LIKE '%parallel%'


  1. PARALLEL_FORCE_LOCAL
    1. 병렬로 실행되는 SQL 문이 Oracle RAC 환경의 단일 인스턴스로 제한되는지 여부를 지정
    2. 이 매개변수를 TRUE로 설정하면 쿼리 코디네이터가 실행 중인 단일 Oracle RAC 인스턴스로 처리되는 병렬 서버의 범위를 제한
    3. PARALLEL_FORCE_LOCAL 매개변수의 권장 값은 FALSE
  2. PARALLEL_MAX_SERVERS
    1. 인스턴스에 대한 최대 병렬 실행 프로세스 및 병렬 복구 프로세스 수를 지정
    2. 수요가 증가함에 따라 Oracle Database는 인스턴스 시작 시 생성된 수에서 이 값까지 프로세스 수를 늘림.
    3. DOP(병렬 처리 수준) 특성이 더 높은 테이블의 병렬 쿼리에는 기본값이 충분하지 않을 수 있습니다.
    4. 더 높은 DOP의 쿼리를 실행하려면 2 x DOP x NUMBER_OF_CONCURRENT_USERS
  3. PARALLEL_MIN_PERCENT
    1. 사용 중인 애플리케이션에 따라 사용자가 허용 가능한 DOP를 기다림.
    2. PARALLEL_MIN_PERCENT 매개변수의 권장 값은 0.
    3. 0 이외의 값으로 설정하면 지정된 시간에 시스템에서 요청된 DOP를 충족할 수 없을 때 Oracle 데이터베이스에서 오류를 반환 함.
    4. 예를 들어 PARALLEL_MIN_PERCENT를 50으로 설정하고 DOP가 적응형 알고리즘이나 리소스 제한으로 인해 50% 이상 감소하면 Oracle Database는 ORA-12827을 반환.
    5. ORA-12827: insufficient parallel query slaves available
  4. PARALLEL_MIN_SERVERS
    1. 병렬 작업을 위해 예약된 단일 인스턴스에서 시작할 프로세스 수를 지정
    2. PARALLEL_MIN_SERVERS를 설정하면 시작 비용과 메모리 사용량의 균형을 맞춤.
    3. PARALLEL_MIN_SERVERS를 사용하여 시작된 프로세스는 데이터베이스가 종료될 때까지 종료되지 않음.
  5. PARALLEL_MIN_TIME_THRESHOLD
    1. 쿼리가 자동 병렬 처리 수준을 고려하여 쿼리가 가져야 하는 최소 실행 시간을 지정.
    2. 자동 병렬 처리 수준은 PARALLEL_DEGREE_POLICY가 AUTO 또는 LIMITED로 설정된 경우에만 활성화 됨.
  6. PARALLEL_SERVERS_TARGET
    1. 명령문 큐잉이 사용되기 전에 병렬 명령문을 실행할 수 있는 병렬 서버 프로세스 수를 지정
    2. PARALLEL_DEGREE_POLICY가 AUTO로 설정된 경우 시스템에서 현재 사용 중인 병렬 프로세스 수가 PARALLEL_SERVERS_TARGET보다 크거나 같으면 병렬 실행이 필요한 명령문이 대기함.
    3. 시스템에서 허용되는 최대 병렬 서버 프로세스 수가 아님(PARALLEL_MAX_SERVERS에 의해 제어됨).
    4. 그러나 PARALLEL_SERVERS_TARGET 및 병렬 명령문 큐잉은 병렬 실행이 필요한 각 명령문에 필요한 병렬 서버 리소스가 할당되고 시스템이 너무 많은 병렬 서버 프로세스로 플러딩되지 않도록 하는 데 사용
  7. SHARED_POOL_SIZE
    1. 병렬 실행에는 직렬 SQL 실행에 필요한 것 외에 메모리 리소스가 필요
    2. 추가 메모리는 쿼리 서버 프로세스와 쿼리 코디네이터 간의 통신 및 데이터 전달에 사용
    3. Oracle Database는 공유 풀에서 쿼리 서버 프로세스를 위한 메모리를 할당. 다음과 같이 공유 풀을 조정.
      1. 공유 커서 및 저장 프로시저와 같은 공유 풀의 다른 클라이언트를 허용.
      2. 값이 크면 다중 사용자 시스템에서 성능이 향상되지만 값이 작으면 메모리 사용량이 줄어듬.
      3. 그런 다음 병렬 실행에 사용되는 버퍼 수를 모니터링하고 shared pool PX msg pool을 V$PX_PROCESS_SYSSTAT 보기의 출력에 보고된 현재 최고 수위 표시와 비교




1.4.3 병렬처리가 안되는경우


1.4.3.1 서버에서 프로세스를 할당 받지 못할때

1.4.3.2 insert ~ select 의 병렬도가 다를때

1.4.3.3 파티션닝 테이블에 1개파티션만 타는경우
  1. 튜닝 실제 예시

1.4.3.4 LOB 컬럼 포함시
  1. lob 컬럼 포함시 => 오라클 19c 부터기능

1.4.3.5 DB 링크

1.5 병렬 힌트사용 방법

1.5.1 PQ_DISTRIBUTE

PQ_DISTRIBUTE.png


  1. 병렬 조인 시, Producer 프로세스와 Consumer 프로세스 간의 데이터 전달 방식을 지정합
  2. 용도
    1. 옵티마이져가 파티션된 테이블을 적절히 활용하지 못하고 동적 재분할을 시도할 때
    2. 기존 파티션 키를 무시하고 다른 키 값으로 동작 재분할하고 싶을 때
    3. 통계정보가 부정확하거나 통계정보를 제공하기 어려운 상황에서 실행계획을 고정시키고자 할 때
    4. 기타 여러 가지 이유로 데이터 분배 방식을 변경하고자 할 때
  3. 구문
  • PQ_DISTRIBUTE(inner, none, none)
    • Full-parition Wise 조인으로 유도할 때 사용.양쪽 테이블 모두 조인 컬럼에 대해 같은 기준으로 파티션되어 있어야 작동
    • 각 병렬 쿼리 서버는 각 테이블에서 하나씩 일치하는 한 쌍의 파티션 간에 조인 작업을 수행
    • 반드시 두 테이블 모두 조인 키에 동등하게 분할되어야 함.
  • PQ_DISTRIBUTE(inner, partition, none)
    • inner 테이블의 파티션을 사용하여 outer 테이블의 행을 매핑
    • inner 테이블은 조인 키로 분할되어야 함
    • 이 힌트는 outer 테이블의 파티션 수가 병렬 쿼리 서버 수의 배수와 같거나 거의 같을 때 권장
  • PQ_DISTRIBUTE(inner, none, partition)
    • outer 테이블의 파티션을 사용하여 inner 테이블의 행을 매핑
    • outer 테이블은 조인 키에 분할되어야 함
    • 이 힌트는 outer 테이블의 파티션 수가 쿼리 서버 수의 배수와 같거나 거의 같을 때 권장
  • PQ_DISTRIBUTE(inner, hash, hash)
    • 조인키의 해시 함수를 사용하여 각 테이블의 행을 소비자 병렬 쿼리 서버에 매핑.
    • 매핑이 완료되면, 각 쿼리 서버는 결과 파티션 쌍 사이에 조인을 수행
    • 테이블의 크기가 비슷하고 조인 작업이 해시 조인 또는 정렬 병합 조인으로 구현될 때 권장
  • PQ_DISTRIBUTE(inner, broadcast, none)
    • outer 테이블의 모든 행이 각 병렬 쿼리 서버로 브로드캐스트되고 inner 테이블 행은 무작위로 분할되도록 함
    • 이 힌트는 outer 테이블이 inner 테이블에 비해 매우 작을 때 권장
  • PQ_DISTRIBUTE(inner, none, broadcast)
    • inner 테이블의 모든 행을 각각의 소비자 병렬 쿼리서버로 브로드캐스트하도록 함
    • outer 테이블 행은 무작위로 분할됩니다.
    • inner 테이블이 outer 테이블에 비해 매우 작을 때 권장



1.5.1.1 가능한 조합
  1. HASH - HASH : OUTER, INNER 크기가 비슷할 때
  2. BROADCAST - NONE : OUTER 테이블이 작을때
  3. NONE - BROADCAST : INNER 테이블이 작을때
  4. PARTITION - NONE : INNER 테이블 파티션 기준으로 OUTER 테이블을 파티션 하여 분배
  5. NONE - PARTITION : OUTER 테이블 파티션 기준으로 INNER 테이블을 파티션 하여 분배
  6. NONE - NONE : 두 테이블이 조인컬럼 기준으로 파티션 되어 있을때

1.5.2 PQ_REPLICATE / NO_PQ_REPLICATE(대량테이블)

  1. 각각 병렬서버에서 테이블 전체를 읽음
  2. BROADCAST 분산 하여 읽지 않음
  3. 로컬 캐시(SGA) 에서 빠르게 읽는 방식
  4. 복제라기보다는 조인처럼 생각
  5. 매우 작은 테이블 처리시 유리
    1. 튜닝 예시

1.5.3 PQ_SKEW/NO_PQ_SKEW

  1. 다수의 로우가 같은 조인키값을 가지고 있어서 조인키의 분산값이 한쪽으로 치우친 경우
  2. 오라클에서 히스토그램을 생성해야 하지만 자동으로 병렬조인시 SKEW를 핸들링함
  3. 제약사항
    1. INNER 조인시
    2. 단일 컬럼 조인시만 가능, 여러개 컬럼은 안됨
    3. 병렬 HASH JOIN만 가능
    4. MERGE JOIN 은 안됨
    5. SKEW테이블은 일반 테이블만 (뷰, 결과셋은 기능제한됨)

1.5.4 BF 블름필터(Bloom Filter)

  1. 어떤값이 어떤 집합에 속해 있는가를 검사하는 필터
  2. 패러럴 조인시 소비자간의 커뮤니케이션 데이터량 과 해시조인시 부하를 감소하기 위해 사용됨
  3. JOIN FILTER PRUNNING , RESULT CACHE
  4. 플랜에서 JOIN FILTER CREATE / USE
     :BF0000
1.5.4.1 PX_JOIN_FILTER / NO_PX_JOIN_FILTER
  1. 사용 조건
    1. 해시/머지 조인시
    2. 파티션 조인시
    3. PARALLEL 쿼리시
    4. 파티션/PARALLEL 둘다 아닌경우, 인라인뷰의 GROUP BY
  2. 선행 테이블의 상수조건이 없는 경우 오라클은 블름필터를 사용하지 않는다.
  3. 블름 필터를 만들때 선행 테이블은 필터집합을 만들때 사용.

1.5.5 PQ_DISTRIBUTE_WINDOW

PQ_DISTRIBUTE_WINDOW(@Query_block N) => N=1 for hash, N=2 for range, N=3 for list(예전 방식 9i)

rem ##################################
rem # Objects                        #
rem ##################################

alter session set optimizer_adaptive_plans = false;
alter system flush shared_pool;

drop table asc_dmy1;
drop table asc_dmy3;

create table asc_dmy1
parallel 8
as 
select 'AAA' f001
  from xmltable('1 to 300');
  
--note: this table has no parallel degree
create table asc_dmy3
as
select 'AAA' f001, 1 acc206
  from dual;

rem #############################################
rem # SORT then distribute by HASH (Bug)        #
rem #############################################  
/*
   leads to a HASH JOIN in Line 7, which imo must be a HASH JOIN BUFFERED (due to 2 active PX SENDs at 9 and 13) 
   This SQL hangs and never finishes 
   
   https://oracle-randolf.blogspot.com/2012/12/hash-join-buffered.html
   "At most one data distribution can be active at the same time"
   
   "Since it doesn't seem to be supported to have two PX SEND operations active at the same time, 
    some artificial blocking operation needs to be introduced, in this case the HASH JOIN BUFFERED, 
	that first consumes the second row source completely before starting the actual probe phase"
*/
select /*+ pq_distribute_window(@"SEL$1" 2) */
       max(v.acc206) over (partition by v.f001) max_bew
  from asc_dmy3 v,
       asc_dmy1 e
 where e.f001 = v.f001
   and v.f001 = e.f001;  

/*   
-----------------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name     | E-Rows |E-Bytes| Cost (%CPU)| E-Time   |    TQ  |IN-OUT| PQ Distrib |
-----------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |          |      1 |   419 |     6  (17)| 00:00:01 |        |      |            |
|   1 |  PX COORDINATOR              |          |        |       |            |          |        |      |            |
|   2 |   PX SEND QC (RANDOM)        | :TQ10003 |      1 |   419 |     6  (17)| 00:00:01 |  Q1,03 | P->S | QC (RAND)  |
|   3 |    WINDOW CONSOLIDATOR BUFFER|          |      1 |   419 |     6  (17)| 00:00:01 |  Q1,03 | PCWP |            |
|   4 |     PX RECEIVE               |          |      1 |   419 |     6  (17)| 00:00:01 |  Q1,03 | PCWP |            |
|   5 |      PX SEND HASH            | :TQ10002 |      1 |   419 |     6  (17)| 00:00:01 |  Q1,02 | P->P | HASH       |
|   6 |       WINDOW SORT            |          |      1 |   419 |     6  (17)| 00:00:01 |  Q1,02 | PCWP |            |
|*  7 |        HASH JOIN             |          |      1 |   419 |     5   (0)| 00:00:01 |  Q1,02 | PCWP |            |
|   8 |         PX RECEIVE           |          |      1 |   415 |     3   (0)| 00:00:01 |  Q1,02 | PCWP |            |
|   9 |          PX SEND HASH        | :TQ10000 |      1 |   415 |     3   (0)| 00:00:01 |  Q1,00 | S->P | HASH       |
|  10 |           PX SELECTOR        |          |        |       |            |          |  Q1,00 | SCWC |            |
|  11 |            TABLE ACCESS FULL | ASC_DMY3 |      1 |   415 |     3   (0)| 00:00:01 |  Q1,00 | SCWP |            |
|  12 |         PX RECEIVE           |          |    300 |  1200 |     2   (0)| 00:00:01 |  Q1,02 | PCWP |            |
|  13 |          PX SEND HASH        | :TQ10001 |    300 |  1200 |     2   (0)| 00:00:01 |  Q1,01 | P->P | HASH       |
|  14 |           PX BLOCK ITERATOR  |          |    300 |  1200 |     2   (0)| 00:00:01 |  Q1,01 | PCWC |            |
|  15 |            TABLE ACCESS FULL | ASC_DMY1 |    300 |  1200 |     2   (0)| 00:00:01 |  Q1,01 | PCWP |            |
-----------------------------------------------------------------------------------------------------------------------   
*/

rem #############################################
rem # distribute by HASH then SORT  (Success)   #
rem #############################################  
/*
   leads to a HASH JOIN *BUFFERED* in Line 6, which is inevitably necessary imo
   This SQL finishes immediately
*/ 
select /*+ pq_distribute_window(@"SEL$1" 1) */
       max(v.acc206) over (partition by v.f001) max_bew
  from asc_dmy3 v,
       asc_dmy1 e
 where e.f001 = v.f001
   and v.f001 = e.f001;    

/*
------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                  | Name     | E-Rows |E-Bytes| Cost (%CPU)| E-Time   |    TQ  |IN-OUT| PQ Distrib |  OMem |  1Mem |  O/1/M   |
------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |          |        |       |     6 (100)|          |        |      |            |       |       |          |
|   1 |  PX COORDINATOR            |          |        |       |            |          |        |      |            | 73728 | 73728 |          |
|   2 |   PX SEND QC (RANDOM)      | :TQ10003 |      1 |   419 |     6  (17)| 00:00:01 |  Q1,03 | P->S | QC (RAND)  |       |       |          |
|   3 |    WINDOW SORT             |          |      1 |   419 |     6  (17)| 00:00:01 |  Q1,03 | PCWP |            | 20480 | 20480 |     8/0/0|
|   4 |     PX RECEIVE             |          |      1 |   419 |     5   (0)| 00:00:01 |  Q1,03 | PCWP |            |       |       |          |
|   5 |      PX SEND HASH          | :TQ10002 |      1 |   419 |     5   (0)| 00:00:01 |  Q1,02 | P->P | HASH       |       |       |          |
|*  6 |       HASH JOIN BUFFERED   |          |      1 |   419 |     5   (0)| 00:00:01 |  Q1,02 | PCWP |            |  3400K|  3091K|     8/0/0| 
|   7 |        PX RECEIVE          |          |      1 |   415 |     3   (0)| 00:00:01 |  Q1,02 | PCWP |            |       |       |          |
|   8 |         PX SEND HASH       | :TQ10000 |      1 |   415 |     3   (0)| 00:00:01 |  Q1,00 | S->P | HASH       |       |       |          |
|   9 |          PX SELECTOR       |          |        |       |            |          |  Q1,00 | SCWC |            |       |       |          |
|  10 |           TABLE ACCESS FULL| ASC_DMY3 |      1 |   415 |     3   (0)| 00:00:01 |  Q1,00 | SCWP |            |       |       |          |
|  11 |        PX RECEIVE          |          |    300 |  1200 |     2   (0)| 00:00:01 |  Q1,02 | PCWP |            |       |       |          |
|  12 |         PX SEND HASH       | :TQ10001 |    300 |  1200 |     2   (0)| 00:00:01 |  Q1,01 | P->P | HASH       |       |       |          |
|  13 |          PX BLOCK ITERATOR |          |    300 |  1200 |     2   (0)| 00:00:01 |  Q1,01 | PCWC |            |       |       |          |
|* 14 |           TABLE ACCESS FULL| ASC_DMY1 |    300 |  1200 |     2   (0)| 00:00:01 |  Q1,01 | PCWP |            |       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------------
*/

1.5.6 PQ_EXPAND_TABLE / NO_PQ_EXPAND_TABLE

  1. PQ_EXPAND_TABLE
    1. 파티션테이블인 경우 한곳에 편중된 파티션이 있을때, 옵티마이저가 UNION 절로 변경하여 편중된 파티션과 다른 파티션을 나누어 병렬처리로 수행함.
    2. (예) 인라인뷰내 파티션테이블을 (GROUP BY하는 경우)발생함
  • 튜닝 사례 1 : 한곳에 치중된 파티션을 UNION ALL 로 분리
SELECT *
  FROM 
(SELECT /*+ PQEXPAND_TABLE(A2) */
        AZ.SRV_INSTLID
      , A2.CYKIM_TIMPL_ID
      , A2.CYKIM_WRT_YMD
      , A2.CYKIM_WRT_DGR
      , MIN(A2.DCRY_LNNO) AS DCRY_LNNO 
  FROM TB_BIG_PART A2 -- 파티션테이블 
 WHERE SUBSTR(A2.CYKIM_TMPL_ID, 0, 2) = 'am'
 GROUP BY A2.SRV_INST_ID
        , A2.CYKIM_TMP_ID 
        , A2.CYKIM_WRT_YMD
        , A2.CYKIM_WRT_DGR
) A
, TB_CYKIM_SPRV B
.....
  • 튜닝 사례 2 : 한곳에 치중된 파티션을 UNION ALL 로 분리 하지 않음.
SELECT *
  FROM 
(SELECT /*+ NO_PQEXPAND_TABLE(A2) */
        AZ.SRV_INSTLID
      , A2.CYKIM_TIMPL_ID
      , A2.CYKIM_WRT_YMD
      , A2.CYKIM_WRT_DGR
      , MIN(A2.DCRY_LNNO) AS DCRY_LNNO 
  FROM TB_BIG_PART A2 -- 파티션테이블의  한곳의 파티션에 치중됨, 예를 들어 2000년 이전 데이터는 PT_2001파티션에 1억건 존재 , 나머지는 1백만건 
 WHERE SUBSTR(A2.CYKIM_TMPL_ID, 0, 2) = 'am'
 GROUP BY A2.SRV_INST_ID
        , A2.CYKIM_TMP_ID 
        , A2.CYKIM_WRT_YMD
        , A2.CYKIM_WRT_DGR
) A
, TB_CYKIM_SPRV B
.....

1.5.7 PQ_CONCURRENT_UNION

  1. UNION ALL 성능향상
  2. UNION은 각각 SQL을 1개씩 SERIAL 하게 처리하는게 기본방식임
  3. 12C 부터 병렬쿼리 실행시 동시(CONCURRENT)에 처리토록 함
  4. 전체데이터 처리시 유리
  5. 부분범위 처리시 비추
  6. DEFAULT

1.5.8 PQ_FILTER

/*+ PQ_FILTER(SERIAL | NONE | HASH | RANDOM) */ -- 4개중 택1
  1. 병렬서버에서 서브쿼리를 필터링할수 있는 기능.
  2. 서브쿼리 필터링은 일반적으로 메인쿼리가 모두 수행된 후 수행함.
  3. HASH방식과 RANDOM방식은 추가적인 버퍼링이 필요하므로 특별한 경우가 아니면 NONE 방식으로 사용하는것이 일반적일것으로 판단함.
  4. 다수의 서브쿼리 수행시 2개의 서브쿼리 모두 병렬서버에서 필터링됨.
=> ORDER_SUBQ 힌트(12c 이후) 로 서브쿼리의 수행순서 조정가능함.
  • PQ_FILTER 사용예시 (with NO_UNNEST 힌트)
SELECT /* + PARALLEL(2) PQ_FILTER(HASH) */
       *
  FROM T1 A
 WHERE EXISTS (SELECT /*+ NO_UNNEST */
                      1
                 FROM T2 B
                WHERE B.C1 = A.C1)
  • 서브 쿼리가 2개일 때 순서 조정 예시
SELECT /*+ PARALLEL(2) PQ_FILTER(NONE) 
           ORDER_SUBQ(@MAIN @SUB2 @SUB1) QB_NAME(MAIN) */
       * 
 FROM T1 A
WHERE EXISTS (SELECT /*+ NO_UNNEST QB_NAME(SUB1) */
                      1
                 FROM T2 B
                WHERE B.C1 = A.C1)
  AND EXISTS (SELECT /*+ NO_UNNEST QB_NAME(SUB2) */
                      1
                 FROM T3 C
                WHERE C.C1 = A.C1)


QC 에서 서브쿼리를 필터링함


FILTER
  PX COORDINATOR
    PX SEND QC(RANDOM)
      PX BLOCK ITERATOR
        TABLE ACCESS FULL T1
  INDEX RANGE SCAN IX_T2


  1. 메인쿼리를 분배하지 않고 병렬서버에서 서브쿼리를 필터링함.
  2. 병렬서버가 T1 테이블을 블록 그래뉼로 조회하고, 조회결과로 서브쿼리를 필터링하여 QC로 결과를 전송하는 방식


PX COORDINATOR
  PX SEND QC(RANDOM)
   FILTER
     PX BLOCK ITERATOR.
        TABLE ACCESS FULL 11
     INDEX RANGE SCAN IX_12
PX COORDINATOR
  PX SEND QC(RANDOM)
    FILTER
      PX BLOCK ITERATOR
         TABLE ACCESS FULL T1
    INDEX RANGE SCAN X_13
    INDEX RANGE SCAN IX_12


  1. 메인 쿼리를 HASH 방식으로 분배 한후, 병렬서버에서 서브쿼리를 필터링함.
  2. HASH방식은 조인조건인 C1 컬럼의 해시값으로 분배 되기 때문에 서브쿼리 캐싱 효율을 높이는 목적으로 활용가능.


PX COORDINATOR
  PX SEND QC(RANDOM)
    BUFFER SORT
      FILTER
        PX RECEIVE
           PX SEND HASH
              PX BLOCK ITERATOR
                TABLE ACCESS FULL T1
           INDEX RANGE SCAN IX_T2


  1. 메인쿼리를 RANDOM 방식으로 분배한후, 병렬서버에서 서브쿼리를 필터링함.
  2. RANDOM 방식은 read by other session등 의 블럭경합을 해소하는 목적으로 활용가능


PX COORDINATOR
  PX SEND QC(RANDOM)
    BUFFER SORT
      FILTER
        PX RECEIVE
           PX SEND ROUND-ROBIN
              PX BLOCK ITERATOR 
                TABLE ACCESS FULL T1
              INDEX RANGE SCAN IX_T2

1.5.9 파티션

  1. USE PARTITION_WISE_DISTINCT
  2. USE PARTITION_WISE_GBY
  3. USE PARTITION_WISE_WIF

1.6 튜닝포인트를 찾아라

1.6.1 테이블 정보를 수집 하라

  1. 테이블 사이즈 조사
  2. 테이블 파티셔닝 여부
  3. 파티션 키
  4. 병렬처리가 가능한 인덱스(index_ffs , 파티션 로컬 인덱스)

1.6.2 스칼라퀴리는 인라인뷰로 변경을 검토하라

  1. 쿼리 결과를 전송하는 단계에서 수행되는 스칼라 서브쿼리는 QC가 담당
  2. lateral 쿼리 활용법

1.6.3 BROADCAST 테이블을 찾아라

  1. 임시테이블에 주의하라
     : 임시테이블은 주로 통계 정보가 생성되어 있지 않아. 통계정보 오류로 인해 브로드캐스트로 처리되는 경우가 많음.

1.6.4 튜닝 대상을 찾아라

  1. rownum => row_number 윈도우 함수로 변경
  2. S->P 분산 프로세스
  3. round - robin
    1. 튜닝 실제 예시

1.6.5 조인이 효율적인지 검토 하라

  1. 튜닝 실제 예시

1.6.6 심플하게 튜닝 하는 방법은 없을까?

  1. opt_param('_parallel_broadcast_enabled','false')
  2. pq_distribute(A hash hash)
  3. 튜닝 실제 예시

1.7 입력/수정 성능저하시 검토 사항


1.7.1 INSERT 처리가 느릴때(SELECT~INSERT시 SELECT는 빠른데 INSERT가 느린경우)

  1. hwm / reorg

1.7.2 시퀀스를 사용하는경우

  1. sequence cache size 증가 검토
  2. default 20 => 2000이상

1.7.3 DB링크 병렬처리 주의사항

dblink로 가져올때는 병렬처리 불가


1.8 데이터 전환시 사용하는 병렬처리

-- 세션에서 병렬 쿼리 작업 절차
ALTER SESSION ENABLE PARALLEL DML;
ALTER SESSION SET HASH_AREA_SIZE          = 1024000000;
ALTER SESSION SET SORT_AREA_SIZE          = 2147483647;
ALTER SESSION SET SORT_AREA_RETAINED_SIZE = 2147483647;
ALTER SESSION SET WORKAREA_SIZE_POLICY    = MANUAL; 
-- 사용자가 지정한 sort_area_size가 모든  병렬 서버에게 적용. 
-- sort_area_size를 크게 설정한 상태에서 지나치게 큰 병렬도를 지정하면
-- OS 레벨에서 페이징이 발생하고 심할 경우 시스템을 마비시킬 수 있음.
ALTER SESSION SET SKIP_UNUSABLE_INDEXES = TRUE;
ALTER SESSION SET DB_FILE_MULTIBLOCK_READ_COUNT=256; -- AIX는 최대 512 

ALTER SESSION SET "_sort_multiblock_read_count"     = 256;
ALTER SESSION SET "_db_file_optimizer_read_count"   = 256;
ALTER SESSION SET "_db_file_exec_read_count"        = 256;
ALTER SESSION SET "_serial_direct_read"             = TRUE;
-- 병렬 실행 메시지 사이즈 32K
alter system set parallel_execution_message_size = 32768; -- 16384 -- 기본

1.9 그외 병렬 처리시 주의 사항

  1. 실행계획에서 P ->P가 나타날 때면 지정한 병렬도의 2배수만큼 병렬 프로세스 필요
  2. 쿼리 블록마다 병렬도를 다르게 지정한 경우, 여러 가지 우선 순위와 규칙에 따라 최종 병렬도가 결정됨, 병렬도를 같게 지정하는 것이 바람직 함.
  3. parallel_index 힌트를 사용할 때는 반드시 index 또는 index_ffs 힌트를 사용
    1. 옵티마이저에 의해 Full table Scan이 선택될 경우 parallel_index 힌트는 무시 됨
  4. 병렬 DML 수행시 Exculsive 모드 테이블 Lock이 걸리므로 업무 트랜잭션이 발생하는 주간에는 삼가
  5. 테이블이나 인덱스를 빠르게 생성하려고 parallel 옵션 을 사용했다면 작업을 완료 후 즉시 noparallel로 변경 할것
  6. 부분범위처리 방식으로 조회하면서 병렬 쿼리를 사용한 때에는 필요한 만큼 데이터를 Fetch 하고 나서 곧바로 커서를 닫아 주어야 함
    1. Toad나 Orange 처럼 부분범위처리를 지원하는 쿼리 툴에서는 EOF에 도달하기 전까지
    2. 커서를 오픈한 채로 유지하기 때문에 오라클은 병렬 서버들을 해제하지 못하고 대기 상태에 머물도록 한다.
    3. 이는 불필요한 리소스를 낭비하는 결과를 초래하므로 조회가 끝나자마자
    4. select * from dual 같은 문장을 수행해 병렬 쿼리의 커서를 닫아 주어야 한다.



<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" 데이터 이름="bmc-버튼" 데이터 슬러그="autoexe" data-color="#FFDD00" data-emoji="" data-font="Lato" data-text="커피 한잔 쏩니다." data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff" ></script>