Skip to content
튜닝 연습인덱스 스캔 효율과 테이블 액세스
ACC-038

병렬 8로 돌리는 연간 상품 판매 집계가 새벽 배치들의 병렬 서버를 다 가져간다

중급INSERT버거113 / 700

상황 ​

버거원 본사는 매주 일요일 03시에 「연간 상품 판매 순위」 마트를 2013년 치로 다시 적재한다. 적재 SQL에는 2019년에 붙인 FULL(oi) PARALLEL(oi 8) 힌트가 있다. 지난 일요일, 같은 시간에 도는 매출 집계 배치 두 개가 병렬 서버를 얻지 못해 직렬로 강등되었고(Parallel operations downgraded to serial), 03시 배치 묶음 전체가 40분 늦게 끝났다. 배치 담당 오과장은 이렇게 물었다. "병렬도를 4로 낮추면 되나?"

테이블과 인덱스 ​

  • ORD_ITEM 주문상품: 2,472,717행 · 55,265블록(문자열 채움 컬럼 때문에 행이 넓어 블록당 약 45행) · ORD_DT VARCHAR2(8) 2012 ~ 2013 · 2013년 약 1,236,000행 · ITEM_ID 92종 · ORD_NO와 ITEM_ID는 PK 컬럼이라 NOT NULL
  • 인덱스 ORD_ITEM_PK (ORD_NO, ITEM_ID) · ORD_ITEM_X01 (ORD_DT, ORD_HMS) · ORD_ITEM_X02 (ITEM_ID, ORD_DT, ORD_ITEM_QTY): X02는 「상품 월간 추이」용으로 2019년 뒤에 만들었다 · BLEVEL 2 · 리프 약 8,500블록(리프 한 블록에 약 290항목) · 통계 최신
  • ITEM_SALE_YEAR 연간 상품 판매 마트(이 문제에서 쓰는 집계 테이블): SALE_YY · ITEM_ID · QTY · LINE_CNT · 해마다 92행 · 적재 전에 그해 행을 지운다
  • 운영 DB는 EE이고 parallel_max_servers = 32 · db_file_multiblock_read_count = 128

원본 SQL ​

sql
DELETE FROM ITEM_SALE_YEAR WHERE SALE_YY = '2013';

INSERT INTO ITEM_SALE_YEAR (SALE_YY, ITEM_ID, QTY, LINE_CNT)
SELECT /*+ FULL(oi) PARALLEL(oi 8) */
       '2013', oi.ITEM_ID, SUM(oi.ORD_ITEM_QTY), COUNT(*)
  FROM ORD_ITEM oi
 WHERE oi.ORD_DT BETWEEN '20130101' AND '20131231'
 GROUP BY oi.ITEM_ID;
COMMIT;

실행계획 학습용 예시 · 병렬 실행은 운영(EE)의 모양(교재 14화 §9) · SQL Monitor 요약으로 적었다 ​

xplan
[SQL Monitor — INSERT · 일요일 03:00 · DOP 8]
Global Stats
  Duration 14s · DB Time 121s · Buffer Gets 55.9K · Read Reqs 470 · Read Bytes 432MB
  PX Servers Requested 16 · Allocated 16 (생산자 8 + 소비자 8)

=========================================================================================
| Id |          Operation           |      Name      | Execs |  Rows    | Read | Read  |
|    |                              |                |       | (Actual) | Reqs | Bytes |
=========================================================================================
|  0 | INSERT STATEMENT             |                |     1 |        1 |      |       |
|  1 |  LOAD TABLE CONVENTIONAL     | ITEM_SALE_YEAR |     1 |          |      |       |
|  2 |   PX COORDINATOR             |                |    17 |       92 |      |       |
|  3 |    PX SEND QC (RANDOM)       | :TQ10001       |     8 |       92 |      |       |
|  4 |     HASH GROUP BY            |                |     8 |       92 |      |       |
|  5 |      PX RECEIVE              |                |     8 |      736 |      |       |
|  6 |       PX SEND HASH           | :TQ10000       |     8 |      736 |      |       |
|  7 |        HASH GROUP BY         |                |     8 |      736 |      |       |
|  8 |         PX BLOCK ITERATOR    |                |     8 |    1236K |      |       |
|  9 |          TABLE ACCESS FULL   | ORD_ITEM       |   104 |    1236K |  470 | 432MB |
=========================================================================================
   9 - filter(("OI"."ORD_DT">='20130101' AND "OI"."ORD_DT"<='20131231'))

문제 ​

  1. 이 집계가 실제로 읽어야 하는 컬럼과 지금 읽고 있는 대상을 비교해, 병렬 8이 무엇을 나눠 읽고 있는지 설명하시오.
  2. 병렬 서버를 쓰지 않고도 빨라지도록 SQL을 고치시오. 고친 SQL이 결과를 바꾸지 않는 근거도 쓰시오.
  3. 오과장의 질문(병렬도를 4로 낮추면 되나)에 답하시오. 그래도 더 줄여야 할 때 쓸 수 있는 병렬 방법도 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자