테마
한빛유통 쇼핑몰은 결제 대행사(PG)가 결제 완료를 알려 오면(콜백) 주문의 상태와 결제 정보를 고친다. 평소에는 초당 몇 건이지만, 저녁 8시 타임세일이 시작되면 처음 10분 동안 초당 140건까지 몰린다. PG는 3초 안에 응답이 없으면 같은 콜백을 다시 보낸다. 지난 금요일 20:00 ~ 20:10에 들어온 재시도 콜백만 11,400건이었다.
T_ORD: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ) · ORD_ST(PENDING → COMPLETE) · PAY_TP(CARD, BANK, POINT) · PAY_DT · PAY_AMTCURSOR_SHARING = EXACT// PgCallbackController.java : 콜백 값을 문자열로 이어 붙여 UPDATE 한다
String sql = "UPDATE T_ORD SET ORD_ST = 'COMPLETE', PAY_DT = SYSDATE, PAY_TP = '" + payTp
+ "', PAY_AMT = " + payAmt + " WHERE ORD_SEQ = " + ordSeq;
try (Statement st = conn.createStatement()) {
st.executeUpdate(sql);
}
conn.commit();
[ASH · 금요일 20:00 ~ 20:10 · 콜백 세션의 표본 비율]
CPU 38%
latch: shared pool 26%
library cache: mutex X 11%
enq: TX - row lock contention 9% (재시도 콜백이 아직 처리 중인 같은 주문을 고치려고 기다린다)
row cache mutex 6%
[V$SYSSTAT · 20:00 ~ 20:10 증가분]
execute count 97,100
parse count (hard) 95,860
parse time cpu 61,400 cs
CPU used by this session 84,900 cs
[V$SQLAREA · 20:10 · FORCE_MATCHING_SIGNATURE로 묶은 맨 위 무리]
PARENTS EXECS MB SAMPLE
6,210 6,480 98.4 UPDATE T_ORD SET ORD_ST = 'COMPLETE', PAY_DT = SYSDATE, PAY_TP = 'CARD', PAY_AMT = 38500 WHERE ORD_SEQ = 98431
[콜백 응답 시간] 평소 p99 0.04초 · 20:00 ~ 20:10 p99 2.9초 · 3초 넘김 11,400건 (모두 재시도로 돌아왔다)
[한 번 실행의 계획]
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 1 | | 0 |00:00:00.01 | 4 |
| 1 | UPDATE | T_ORD | 1 | | 0 |00:00:00.01 | 4 |
|* 2 | INDEX UNIQUE SCAN| PK_T_ORD | 1 | 1 | 1 |00:00:00.01 | 2 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ORD_SEQ"=98431)