Skip to content
튜닝 연습인덱스 사용 조건
IDX-065

점주 앱을 빠르게 하려고 인덱스를 고친 다음 날부터 본사 시간대 상황판이 수십 배 무거워졌다

중급DDL버거65 / 700

상황 ​

버거원 점주 앱의 「오늘 시간대별 주문」(IDX-022의 SQL)은 가맹점 200곳이 하루 4만 번쯤 연다. 화면이 무겁다는 민원에 지난 주말 외주 DBA가 인덱스를 손봤다. 작업 메모는 이렇다. "인덱스 개수는 늘리지 않는다. ORD_X01 맨 앞에 SHOP_NO를 넣어 다시 만들었다. 점주 앱 4블록." 월요일부터 본사 상황판 「전 매장 시간대별 주문」과 운영팀의 「점심 피크 주문 목록」이 느려졌다. 박팀장은 인덱스 개수를 늘리지 말고 셋을 다 받치는 안을 가져오라고 했다.

테이블과 인덱스 ​

  • ORD: 1,000,000행 · 22,422블록 · 주문 일시 순으로 쌓였다 · ORD_DT VARCHAR2(8) 2012-01-01 ~ 2013-12-31 · ORD_HMS VARCHAR2(6) · SHOP_NO VARCHAR2(10) 200곳
  • 2013-03-15 주문 1,344건(매장마다 평균 약 7건) · 그중 12시대(12:00:00 ~ 12:59:59) 186건 · 하루치 주문은 테이블 약 30블록 안에 모여 있다
  • 인덱스 ORD_PK (ORD_NO) · ORD_X01 (SHOP_NO, ORD_DT, ORD_HMS): 지난 주말 다시 만든 것 · BLEVEL 2 · 리프 약 4,050블록(리프 한 블록에 약 250항목) · 통계 최신
  • 고치기 전 ORD_X01 (ORD_DT, ORD_HMS): 리프 3,774블록 · CF 22,223 · S2는 8블록, S3는 13블록이었다
  • POS가 주문마다 INSERT한다(하루 약 1,370건)

원본 SQL ​

sql
-- S1 점주 앱 「오늘 시간대별 주문」 (하루 약 4만 번)
SELECT SUBSTR(ORD_HMS, 1, 2) AS hh, COUNT(*) AS cnt
  FROM ORD
 WHERE ORD_DT  = :v_dt
   AND SHOP_NO = :v_shop
 GROUP BY SUBSTR(ORD_HMS, 1, 2)
 ORDER BY hh;

-- S2 본사 상황판 「전 매장 시간대별 주문」 (본사 PC 10대가 5분마다 · 하루 약 2,900번)
SELECT SUBSTR(ORD_HMS, 1, 2) AS hh, COUNT(*) AS cnt
  FROM ORD
 WHERE ORD_DT = :v_dt
 GROUP BY SUBSTR(ORD_HMS, 1, 2)
 ORDER BY hh;

-- S3 운영팀 「점심 피크 주문 목록」 (하루 수백 번)
SELECT ORD_NO, SHOP_NO, ORD_HMS
  FROM ORD
 WHERE ORD_DT = :v_dt
   AND ORD_HMS BETWEEN '120000' AND '125959';

-- 지난 주말 작업
DROP INDEX ORD_X01;
CREATE INDEX ORD_X01 ON ORD (SHOP_NO, ORD_DT, ORD_HMS);

실행계획 학습용 예시 ​

xplan
[S2 · 2013-03-15]
-----------------------------------------------------------------------------------------
| Id  | Operation         | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |         |      1 |        |     14 |00:00:00.01 |     612 |
|   1 |  SORT GROUP BY    |         |      1 |     14 |     14 |00:00:00.01 |     612 |
|*  2 |   INDEX SKIP SCAN | ORD_X01 |      1 |   1370 |   1344 |00:00:00.01 |     612 |
-----------------------------------------------------------------------------------------
   2 - access("ORD_DT"=:V_DT)
       filter("ORD_DT"=:V_DT)

[S3 · 2013-03-15]
----------------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |         |      1 |        |    186 |00:00:00.01 |     618 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| ORD     |      1 |    163 |    186 |00:00:00.01 |     618 |
|*  2 |   INDEX SKIP SCAN                   | ORD_X01 |      1 |    163 |    186 |00:00:00.01 |     612 |
----------------------------------------------------------------------------------------------------------
   2 - access("ORD_DT"=:V_DT AND "ORD_HMS">='120000' AND "ORD_HMS"<='125959')
       filter(("ORD_DT"=:V_DT AND "ORD_HMS">='120000' AND "ORD_HMS"<='125959'))

[S1 · SH0042]  INDEX RANGE SCAN ORD_X01 · access("SHOP_NO"=:V_SHOP AND "ORD_DT"=:V_DT) · Buffers 4

문제 ​

  1. S2와 S3이 고치기 전보다 수십 배 읽게 된 이유를 실행계획의 INDEX SKIP SCAN과 선두 컬럼 SHOP_NO의 종류 수로 설명하시오.
  2. 인덱스 개수를 늘리지 않고 S1 ~ S3을 모두 받치는 인덱스를 설계하시오. 컬럼 순서를 그렇게 정한 이유를 설명하시오.
  3. 새 인덱스에서 S1과 S3의 조건이 각각 어떻게 처리되는지와, 반영 절차를 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자