테마
한빛유통 쇼핑몰의 「주문 등록」 API는 지난달 API 서버를 클라우드로 옮긴 뒤 한 건에 평균 90ms가 걸린다(옮기기 전 8ms). 저녁 8시 타임세일에는 주문이 초당 140건, 순간적으로는 초당 450건까지 몰리면서 커넥션 풀(서버 한 대에 40개)이 바닥나 연결 대기 시간 초과가 난다. DB CPU는 20%도 쓰지 않는다.
개발팀은 세 가지 안을 냈다. ① 커넥션 풀을 150개로 늘린다. ② 지금 코드를 두고 SQL을 합치고 JDBC 배치와 RETURNING으로 호출 수를 줄인다. ③ 주문 등록 전체를 프로시저 하나로 옮겨 한 번에 부른다. 박팀장이 숫자로 견주어 하나를 고르고 고른 안의 대가도 적어 달라고 했다.
T_ORD: 100,000행 · PK_T_ORD (ORD_SEQ) · 주문 번호는 시퀀스 SEQ_ORD(CACHE 1000)로 매긴다T_ORD_DET: 200,000행 · PK_T_ORD_DET (ORD_SEQ, ORD_DET_NO) · 주문당 1 ~ 3줄(타임세일은 평균 2줄)M_CUS: 2,000행 · PK_M_CUS · 등급 CUS_GD / M_ITM: 100행 · PK_M_ITM · 단가 UNT_PRCdiscount)은 API 서버의 자바 코드에 있고, 장바구니 화면의 예상 금액도 같은 코드로 계산한다.// OrderRegisterService.java : 주문 한 건 (상품 2줄이면 DB 호출 9번)
String gd = jdbc.queryForObject("SELECT CUS_GD FROM M_CUS WHERE CUS_ID = ?", String.class, cusId); // ①
long amt = 0;
for (CartLine c : cart) { // ② 줄마다
c.prc = jdbc.queryForObject("SELECT UNT_PRC FROM M_ITM WHERE ITM_ID = ?", Long.class, c.itmId);
amt += c.prc * c.qty;
}
long seq = jdbc.queryForObject("SELECT SEQ_ORD.NEXTVAL FROM DUAL", Long.class); // ③
jdbc.update("INSERT INTO T_ORD (ORD_SEQ, CUS_ID, ORD_DT, ORD_ST, ORD_AMT) VALUES (?, ?, SYSDATE, 'PENDING', ?)",
seq, cusId, discount(gd, amt)); // ④
int no = 0;
for (CartLine c : cart) { // ⑤ 줄마다
jdbc.update("INSERT INTO T_ORD_DET (ORD_SEQ, ORD_DET_NO, ITM_ID, ORD_QTY, UNT_PRC) VALUES (?, ?, ?, ?, ?)",
seq, ++no, c.itmId, c.qty, c.prc);
}
Map<String, Object> saved = jdbc.queryForMap(
"SELECT ORD_SEQ, ORD_DT, ORD_AMT FROM T_ORD WHERE ORD_SEQ = ?", seq); // ⑥ 화면에 돌려줄 값
// ⑦ COMMIT (트랜잭션 끝)
[주문 한 건 (상품 2줄) · APM 구간]
호출 문장 DB 안 왕복 대기
① SELECT CUS_GD FROM M_CUS ... 0.10 ms 9.0 ms
② × 2 SELECT UNT_PRC FROM M_ITM ... 0.10 ms 9.0 ms × 2
③ SELECT SEQ_ORD.NEXTVAL FROM DUAL 0.05 ms 9.0 ms
④ INSERT INTO T_ORD ... 0.30 ms 9.0 ms
⑤ × 2 INSERT INTO T_ORD_DET ... 0.20 ms 9.0 ms × 2
⑥ SELECT ORD_SEQ, ORD_DT, ORD_AMT ... 0.10 ms 9.0 ms
⑦ COMMIT 0.60 ms 9.0 ms
합계 9번 약 1.8 ms 81 ms → 주문 한 건 약 90 ms (나머지는 API 서버 안)
[타임세일 20:00 ~ 20:10 · 인스턴스]
주문 84,000건 · execute count 초당 1,120 · SQL*Net roundtrips 초당 1,270 · DB CPU 18%
한 건이 연결을 90 ms 쥔다 → 초당 140건이면 동시에 12.6개, 순간 초당 450건이면 40.5개 (풀 40개)