Skip to content
튜닝 연습조인 튜닝
JOIN-004

읽는 블록 수는 정상인데 연간 리포트가 6초 걸리고 TEMP 경보가 울린다

중급SELECT버거154 / 700

상황 ​

본사 마케팅팀의 「연간 매장 · 상품별 판매 수량」은 올해 처음 만든 리포트다. 2013년 한 해를 조회하면 6초가 걸리고, 돌릴 때마다 DBA에게 TEMP 사용 경보가 간다. 오과장은 "Buffers는 두 테이블을 한 번씩 읽은 만큼이라 정상이다. 서버 메모리를 늘려야 하나?"라고 묻는다.

테이블과 인덱스 ​

  • ORD 주문: 1,000,000행 · 22,422블록 · ORD_DT VARCHAR2(8) · 2013년 500,218행 · SHOP_NO 200개 매장 · 통계 최신 (지난주)
  • ORD_ITEM 주문상품: 2,472,717행 · 55,265블록 · ORD_ITEM_PK (ORD_NO, ITEM_ID) · 주문당 평균 2.47줄
  • ORD_ITEM의 통계는 2012년 1월 오픈 첫 주(NUM_ROWS 24,580 · BLOCKS 545)에 모은 뒤 잠겨 있다 (DBMS_STATS.LOCK_TABLE_STATS, 그때 "야간 통계가 계획을 흔든다"는 민원이 있었다)
  • pga_aggregate_target은 512M이고, 해시 조인 하나가 쓸 수 있는 work area는 그보다 훨씬 작다

원본 SQL ​

sql
SELECT o.SHOP_NO, i.ITEM_ID, SUM(i.ORD_ITEM_QTY) AS qty
  FROM ORD o, ORD_ITEM i
 WHERE o.ORD_DT BETWEEN '20130101' AND '20131231'
   AND i.ORD_NO = o.ORD_NO
 GROUP BY o.SHOP_NO, i.ITEM_ID

실행계획 학습용 예시 ​

xplan
------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation           | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem | Used-Tmp|
------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |          |      1 |        |  18400 |00:00:06.12 |   77687 |       |       |          |         |
|   1 |  HASH GROUP BY      |          |      1 |  12290 |  18400 |00:00:06.12 |   77687 |  2152K|  2152K| 3164K (0)|         |
|*  2 |   HASH JOIN         |          |      1 |  12290 |   1236K|00:00:05.71 |   77687 |   176M|    12M|   98M (1)|      96M|
|   3 |    TABLE ACCESS FULL| ORD_ITEM |      1 |  24580 |   2472K|00:00:00.58 |   55265 |       |       |          |         |
|*  4 |    TABLE ACCESS FULL| ORD      |      1 |    500K|    500K|00:00:00.31 |   22422 |       |       |          |         |
------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("I"."ORD_NO"="O"."ORD_NO")
   4 - filter(("O"."ORD_DT">='20130101' AND "O"."ORD_DT"<='20131231'))

문제 ​

  1. 두 테이블을 한 번씩 읽었으므로 Buffers는 정상이다. 그런데도 6초가 걸리는 까닭을 실행계획의 어느 줄, 어느 열에서 알 수 있는지 설명하시오.
  2. 옵티마이저가 이 build를 고른 이유를 E-Rows와 A-Rows로 설명하고, 근본 해결과 임시 해결(통계 변경 승인 전)을 SQL로 작성하시오. 메모리를 늘리자는 제안에 대한 의견도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자