Skip to content
튜닝 연습DML과 배치 튜닝
DML-070

POS 장애로 밀린 일주일 치를 05:00 전에 마감해야 하는데, 하루 치 체인을 일곱 번 돌리면 108분이 넘는다

실전PLSQL버거495 / 700

상황 ​

POS 중계 서버 장애로 12월 23일 ~ 29일 일주일 치 주문이 본사에 들어오지 않다가, 12월 30일 03:10에 업체가 한꺼번에 다시 보내 ORD와 ORD_ITEM에 다 들어갔다. 일 마감 체인은 기준일 하루를 처리하도록 짜여 있고 평소에는 04:00에 전날분을 한 번 돈다. 05:00에 매장 발주 자동 생성이 최근 7일의 매장 일 집계와 메뉴 순위를 읽으므로 그 전에 일곱 날을 모두 마감해야 한다. 03:30부터 시작할 수 있다. 당직 개발자는 기준일만 바꿔 체인을 일곱 번 돌리겠다고 한다. 오과장: "한 번 도는 데 15분 30초니까 108분이 넘어요. 날짜 범위로 한 번에 돌리면 안 되나요?" 개발자: "WHERE만 범위로 바꾸면 되긴 하는데요…"

테이블과 인덱스 ​

  • ORD 1,000,000행 · 22,422블록 · ORD_X01 (ORD_DT, ORD_HMS) · ORD_ITEM 2,472,717행 · 55,265블록 · ORD_ITEM_X01 (ORD_DT, ORD_HMS) · 12/23 ~ 12/29 주문 9,604건, 주문상품 24,017행(테이블의 약 1%)
  • SHOP_DAY(매장 일 집계) · SHOP_ITEM_RANK(매장 메뉴 일 순위, 매장마다 날마다 상위 10개) · ITEM_MON(메뉴 월 누계): 모두 이 문제용이며 기준일 단위로 지우고 다시 넣거나 다시 계산해 덮는다(다시 돌려도 같다).
  • P_ITEM_MON_REBUILD(ym): 그 달 1일부터 지금까지를 ORD_ITEM에서 다시 합산해 덮는다. P_RPT_CACHE_REBUILD: 최근 13개월 매장 × 메뉴 × 월 요약을 통째로 다시 만든다. P_DW_DAY_FILE(d): DW가 기다리는 '기준일 하루 = 파일 하나'를 만들어 보낸다.
  • 본사 DB는 공유 스토리지라 큰 테이블 전체 스캔이 느리다(ORD_ITEM 한 번에 약 17초).

원본 SQL ​

sql
PROCEDURE P_DAY_CLOSE(p_d VARCHAR2) IS              -- 매일 04:00 · p_d = 전날 'YYYYMMDD'
BEGIN
  -- S1 매장 일 집계
  DELETE FROM SHOP_DAY WHERE ORD_DT = p_d;
  INSERT INTO SHOP_DAY (ORD_DT, SHOP_NO, ORD_CNT, SALE_QTY)
  SELECT o.ORD_DT, o.SHOP_NO, COUNT(DISTINCT o.ORD_NO), SUM(i.ORD_ITEM_QTY)
    FROM ORD o, ORD_ITEM i
   WHERE o.ORD_DT = p_d AND i.ORD_DT = p_d AND i.ORD_NO = o.ORD_NO
   GROUP BY o.ORD_DT, o.SHOP_NO;
  COMMIT;

  -- S2 매장 메뉴 일 순위 (매장마다 상위 10개)
  DELETE FROM SHOP_ITEM_RANK WHERE ORD_DT = p_d;
  INSERT INTO SHOP_ITEM_RANK (ORD_DT, SHOP_NO, ITEM_ID, SALE_QTY, RNK)
  SELECT p_d, SHOP_NO, ITEM_ID, QTY, RNK
    FROM (SELECT o.SHOP_NO, i.ITEM_ID, SUM(i.ORD_ITEM_QTY) AS QTY,
                 ROW_NUMBER() OVER (PARTITION BY o.SHOP_NO
                                    ORDER BY SUM(i.ORD_ITEM_QTY) DESC, i.ITEM_ID) AS RNK
            FROM ORD o, ORD_ITEM i
           WHERE o.ORD_DT = p_d AND i.ORD_DT = p_d AND i.ORD_NO = o.ORD_NO
           GROUP BY o.SHOP_NO, i.ITEM_ID)
   WHERE RNK <= 10;
  COMMIT;

  P_ITEM_MON_REBUILD(SUBSTR(p_d, 1, 6));                                       -- S3 메뉴 월 누계
  DBMS_STATS.GATHER_TABLE_STATS(USER, 'ORD',      estimate_percent => 100);     -- S4 통계 수집
  DBMS_STATS.GATHER_TABLE_STATS(USER, 'ORD_ITEM', estimate_percent => 100);
  P_RPT_CACHE_REBUILD;                                                         -- S5 본사 리포트 캐시
  P_DW_DAY_FILE(p_d);                                                          -- S6 DW 일 파일
END;

실행계획 학습용 예시 ​

xplan
[12월 22일 평소 실행 — 단계별]
  단계                     경과        하는 일                                      읽는 양
  S1 매장 일 집계           0분 06초    그날 주문 · 주문상품 (인덱스 범위)            약 4,600블록
  S2 매장 메뉴 일 순위       0분 04초    그날 주문 · 주문상품 (인덱스 범위)            약 4,600블록
  S3 메뉴 월 누계           1분 50초    그 달 1일 ~ 그날 ORD_ITEM 전체 스캔 · 해시 집계   55,265블록
  S4 통계 수집              6분 40초    ORD · ORD_ITEM 전체 · 100% 표본               77,687블록 + 정렬
  S5 본사 리포트 캐시        5분 10초    13개월 ORD · ORD_ITEM 전체 스캔 · TEMP 사용     77,687블록
  S6 DW 일 파일             1분 40초    그날 집계로 파일 생성 0분 40초 + 전송 연결 1분
  합계                     15분 30초

[리허설 — S1, S2의 WHERE만 7일 범위로 바꿔 한 번 돌려 봄]
  S1   0분 26초 · SHOP_DAY 1,400행 (200매장 × 7일) — 날마다 돌린 결과와 같다
  S2   0분 24초 · SHOP_ITEM_RANK 2,000행 (200매장 × 10개) — 날마다 돌리면 14,000행이어야 한다

문제 ​

  1. 체인을 기준일마다 일곱 번 돌리면 몇 분이 걸리고 몇 시에 끝나는지 계산하시오. 단계마다 비용이 '처리하는 날 수'에 비례하는지 '실행 횟수'에 비례하는지 가르시오.
  2. 일곱 날을 한 번에 처리하도록 체인을 고치시오. 창을 넓혀도 결과가 같은 단계와 그대로 넓히면 결과가 틀어지는 단계(리허설의 S2 2,000행)를 가르고, 고친 체인이 몇 분에 끝나는지 계산하시오.
  3. 이번에 S4(통계 수집)를 어떻게 할지 근거와 함께 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자