테마
한빛유통 쇼핑몰은 매주 금요일 10:00에 타임세일을 연다. 10:00부터 5분 동안 주문 등록 세션이 120개 안팎으로 몰리고, 그동안 주문 등록이 0.03초에서 0.6초로 늘어난다. 주문번호는 이미 시퀀스(SQ_ORD_SEQ, CACHE 1000)로 받고 있어서 행 락 대기는 없다.
박팀장의 주문이다. "인덱스마다 무엇으로 바꿀지, 그러면 어떤 화면이 얼마나 손해를 보는지까지 적어 와라."
T_ORD 주문: 100,000행 · 896블록 · ORD_SEQ 시퀀스 번호 · ORD_DT DATE(주문 시각, 시분초 있음, SYSDATE로 넣는다) · CUS_ID(고객 2,000명)PK_T_ORD (ORD_SEQ) · IX_T_ORD_DT (ORD_DT) · IX_T_ORD_CUS (CUS_ID, ORD_DT) · 통계 최신WHERE ORD_SEQ = :seq · 초당 약 300번 · PK_T_ORD 3블록WHERE ORD_SEQ > :last_seq · 매일 02:00 한 번 · 약 3,000행 · PK_T_ORD 범위 스캔 38블록SELECT COUNT(*) … WHERE ORD_DT >= SYSDATE - 10/1440 · 40대가 10초마다 · IX_T_ORD_DT 범위 스캔 32블록WHERE CUS_ID = :c ORDER BY ORD_DT DESC · IX_T_ORD_CUS-- 주문 등록 (타임세일 때 120세션이 동시에)
INSERT INTO T_ORD (ORD_SEQ, CUS_ID, ORD_DT, ORD_ST, ORD_AMT)
VALUES (SQ_ORD_SEQ.NEXTVAL, :cus_id, SYSDATE, 'PENDING', :amt);
-- 지금 인덱스
CREATE UNIQUE INDEX PK_T_ORD ON T_ORD (ORD_SEQ);
CREATE INDEX IX_T_ORD_DT ON T_ORD (ORD_DT);
CREATE INDEX IX_T_ORD_CUS ON T_ORD (CUS_ID, ORD_DT);
-- 이 인덱스들을 쓰는 SQL
SELECT * FROM T_ORD WHERE ORD_SEQ = :seq; -- Q1
SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_AMT FROM T_ORD WHERE ORD_SEQ > :last_seq; -- Q2
SELECT COUNT(*) FROM T_ORD WHERE ORD_DT >= SYSDATE - 10/1440; -- Q3
SELECT ORD_SEQ, ORD_DT, ORD_AMT FROM T_ORD WHERE CUS_ID = :c ORDER BY ORD_DT DESC; -- Q4
[AWR — 금요일 10:00 ~ 10:05 · 주문 등록 14,800건 · 동시 약 120세션]
EVENT WAITS TIME(s) AVG(ms) %DB time
buffer busy waits 186,300 1,240 7 44.8
enq: TX - index contention 9,870 514 52 18.6
DB CPU 612 22.1
log file sync 14,830 118 8 4.3
enq: TX - row lock contention 0 0 0.0
Segments by Buffer Busy Waits
OBJECT TYPE BUSY WAITS %Total
PK_T_ORD INDEX 87,560 47.0
IX_T_ORD_DT INDEX 82,350 44.2
T_ORD TABLE 11,240 6.0
IX_T_ORD_CUS INDEX 5,150 2.8
[Q3 현황판 — ALLSTATS LAST · 10:04]
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 32 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 32 |
|* 2 | INDEX RANGE SCAN| IX_T_ORD_DT | 1 | 11900 | 11900 |00:00:00.01 | 32 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ORD_DT">=SYSDATE@!-.0069444444444444444444444444444444444444)
IX_T_ORD_CUS의 경합이 왜 작은지도 쓰시오.