Skip to content
튜닝 연습Lock과 동시성 제어
LOCK-036

본사 반기 리포트가 새벽에는 31분에 끝나는데 낮에 돌리면 ORA-01555로 죽는다

중급SELECT버거531 / 700

상황 ​

버거원 본사 영업기획팀의 「매장별 상품 판매」 반기 리포트(2013년 하반기)는 원래 새벽 02:00에 돌고 31분 걸린다. 지난주 화요일 급한 보고 때문에 14:00에 돌렸더니 23분 만에 ORA-01555: snapshot too old로 죽었다. 두 번 더 돌렸지만 두 번 다 20분을 넘기고 죽었다. DBA는 언두 테이블스페이스를 두 배로 늘리자고 했다. 리포트 SQL 맨 위에는 "NL 고정. 매장별로 읽어야 빠름(2012.03, 매장 30곳)"이라는 주석과 힌트가 있다.

테이블과 인덱스 ​

  • ORD 주문: 1,000,000행 · 22,422블록 · ORD_DT VARCHAR2(8) · 2013년 하반기 약 252,000건 · 인덱스 ORD_X01 (ORD_DT, ORD_HMS), 이 문제에서 더한 ORD_X02 (SHOP_NO, ORD_DT)
  • ORD_ITEM 주문상품: 2,472,717행 · 55,265블록 · ORD_ITEM_PK (ORD_NO, ITEM_ID) · ORD_DT는 주문의 ORD_DT를 그대로 복사한 칸이다 · 2013년 하반기 약 623,000행
  • SHOP 매장: 200행 · 5블록
  • 영업 중에는 반품과 수량 정정 화면이 지난 주문의 ORD, ORD_ITEM 행을 분당 수십 건 고치고 COMMIT한다. 새벽에는 거의 없다.
  • 언두 테이블스페이스 4GB 고정(AUTOEXTEND OFF) · undo_retention 900 · 버퍼 캐시 1GB

원본 SQL ​

sql
-- 「매장별 상품 판매」 반기 리포트
-- NL 고정. 매장별로 읽어야 빠름 (2012.03, 매장 30곳)
SELECT /*+ LEADING(s o i) USE_NL(o) USE_NL(i) INDEX(o ORD_X02) */
       s.SHOP_NO, s.SHOP_NM, i.ITEM_ID,
       SUM(i.ORD_ITEM_QTY) AS QTY
  FROM SHOP s, ORD o, ORD_ITEM i
 WHERE o.SHOP_NO = s.SHOP_NO
   AND o.ORD_DT BETWEEN '20130701' AND '20131231'
   AND i.ORD_NO = o.ORD_NO
 GROUP BY s.SHOP_NO, s.SHOP_NM, i.ITEM_ID;

실행계획 학습용 예시 ​

xplan
[새벽 02:00 — 끝까지 돈 실행 · ALLSTATS LAST]
--------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                               | Name        | Starts | E-Rows | A-Rows |    A-Time   | Buffers | Reads |
--------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                        |             |      1 |        |  18210 | 00:31:04.12 |   1265K |  412K |
|   1 |  HASH GROUP BY                          |             |      1 |  18400 |  18210 | 00:31:04.12 |   1265K |  412K |
|   2 |   NESTED LOOPS                          |             |      1 |   618K |   623K | 00:30:58.40 |   1265K |  412K |
|   3 |    NESTED LOOPS                         |             |      1 |   250K |   252K | 00:09:12.77 |    253K |  121K |
|   4 |     TABLE ACCESS FULL                   | SHOP        |      1 |    200 |    200 | 00:00:00.01 |       5 |     0 |
|   5 |     TABLE ACCESS BY INDEX ROWID BATCHED | ORD         |    200 |   1250 |   252K | 00:09:12.70 |    253K |  121K |
|*  6 |      INDEX RANGE SCAN                   | ORD_X02     |    200 |   1250 |   252K | 00:00:01.90 |    1420 |   410 |
|   7 |    TABLE ACCESS BY INDEX ROWID          | ORD_ITEM    |   252K |      2 |   623K | 00:21:40.10 |   1012K |  291K |
|*  8 |     INDEX RANGE SCAN                    | ORD_ITEM_PK |   252K |      2 |   623K | 00:06:02.30 |    758K |   97K |
--------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   6 - access("O"."SHOP_NO"="S"."SHOP_NO" AND "O"."ORD_DT">='20130701' AND "O"."ORD_DT"<='20131231')
   8 - access("I"."ORD_NO"="O"."ORD_NO")

[14:00 — 알림 로그]
ORA-01555 caused by SQL statement below (SQL ID: 4hq7d2w9n3c8k, Query Duration=1380 sec, SCN: 0x00000000b71c04e2):
SELECT /*+ LEADING(s o i) USE_NL(o) USE_NL(i) INDEX(o ORD_X02) */ S.SHOP_NO, S.SHOP_NM, I.ITEM_ID, SUM(I.ORD_ITEM_QTY) …
  같은 날 세 번: 14:00 → 14:23 · 14:25 → 14:47 · 14:50 → 15:12

[V$UNDOSTAT — 10분 구간]
구간            UNDOBLKS   TXNCOUNT   MAXQUERYLEN   TUNED_UNDORETENTION   UNXPSTEALCNT   SSOLDERRCNT
02:00 ~ 02:40      3,120        410         1,864                21,400              0             0
14:00 ~ 15:10    418,200     61,400         1,380                 1,040              0             3

문제 ​

  1. 같은 SQL이 새벽에는 끝나고 낮에는 ORA-01555로 죽는 이유를 실행계획과 알림 로그, V$UNDOSTAT으로 설명하시오. 이 리포트는 아무것도 고치지 않는데 왜 언두가 필요한지도 쓰시오.
  2. 낮에 돌려도 죽지 않도록 고치시오. DBA의 "언두 두 배" 안에도 답하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자