테마
조민우가 주문 상태와 관련된 화면 넷을 인덱스 하나로 받치겠다며 IX_ORD_BIG_ST_CUS_DT (ORD_ST, CUS_ID, ORD_DT)를 INVISIBLE로 만들었다. 메모는 이렇다. "선두 ORD_ST는 3종뿐이라, 상태 조건이 없는 SQL도 SKIP SCAN으로 세 번만 들어가면 된다."
세션에서 재 보니 Q1, Q2는 몇 블록이면 끝나는데 Q3은 108블록, Q4는 4,381블록이다. 박팀장은 SQL마다 이 인덱스를 어떻게 쓰는지 판정해 오고, 인덱스를 하나만 더 만들 수 있다면 무엇을 만들지 정해 오라고 했다.
T_ORD_BIG: 1,000,000행 · 5,826블록 · ORD_ST COMPLETE 900,000 · PENDING 80,000 · CANCEL 20,000 · CUS_ID 2,000명(고객당 500건, 상태가 섞여 있다) · ORD_DT 1,095일(하루 913건, 전부 자정)IX_ORD_BIG_ST_CUS_DT (ORD_ST, CUS_ID, ORD_DT): INVISIBLE · BLEVEL 2 · 리프 약 4,350블록(리프 한 블록에 약 230항목) · CANCEL 2만 항목은 리프 약 87블록 · 통계 최신-- Q1 상담 화면 「고객 미결제 주문」 (하루 약 3천 번)
SELECT ORD_SEQ, ORD_DT, ORD_AMT
FROM T_ORD_BIG
WHERE ORD_ST = 'PENDING'
AND CUS_ID = :v_cus
AND ORD_DT >= TO_DATE(:v_from, 'YYYYMMDD');
-- Q2 상담 화면 「고객 상태별 주문 수」 (하루 약 3천 번)
SELECT ORD_ST, COUNT(*) AS ord_cnt
FROM T_ORD_BIG
WHERE CUS_ID = :v_cus
GROUP BY ORD_ST;
-- Q3 정산팀 「일 취소 목록」 (하루 수십 번)
SELECT ORD_SEQ, CUS_ID, ORD_AMT
FROM T_ORD_BIG
WHERE ORD_ST = 'CANCEL'
AND ORD_DT = TO_DATE(:v_dt, 'YYYYMMDD');
-- Q4 운영 상황판 「오늘 주문 건수」 (1분마다)
SELECT COUNT(*)
FROM T_ORD_BIG
WHERE ORD_DT = TO_DATE(:v_dt, 'YYYYMMDD');
-- 조민우 안
CREATE INDEX IX_ORD_BIG_ST_CUS_DT ON T_ORD_BIG (ORD_ST, CUS_ID, ORD_DT) INVISIBLE;
[Q3 · 2024-06-14 · 세션에서 잰 것]
----------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
----------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 18 |00:00:00.01 | 108 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| T_ORD_BIG | 1 | 18 | 18 |00:00:00.01 | 108 |
|* 2 | INDEX RANGE SCAN | IX_ORD_BIG_ST_CUS_DT | 1 | 18 | 18 |00:00:00.01 | 90 |
----------------------------------------------------------------------------------------------------------------------
2 - access("ORD_ST"='CANCEL' AND "ORD_DT"=TO_DATE(:V_DT,'YYYYMMDD'))
filter("ORD_DT"=TO_DATE(:V_DT,'YYYYMMDD'))
[Q4 · 2024-06-14 · 세션에서 잰 것]
-------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.08 | 4381 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.08 | 4381 |
|* 2 | INDEX FAST FULL SCAN| IX_ORD_BIG_ST_CUS_DT | 1 | 913 | 913 |00:00:00.08 | 4381 |
-------------------------------------------------------------------------------------------------------
2 - filter("ORD_DT"=TO_DATE(:V_DT,'YYYYMMDD'))
[Q1 · CUS_000123] INDEX RANGE SCAN · access("ORD_ST"='PENDING' AND "CUS_ID"=:V_CUS AND "ORD_DT">=…) · 테이블 3행 · Buffers 6
[Q2 · CUS_000123] INDEX SKIP SCAN · access("CUS_ID"=:V_CUS) filter("CUS_ID"=:V_CUS) · 인덱스만 · Buffers 11