Skip to content
튜닝 연습Lock과 동시성 제어
LOCK-005

점심 피크에 결제 버튼을 누르면 1초쯤 멈췄다가 다시 시도하라고 뜬다

기초INSERT버거500 / 700

상황 ​

버거원 POS는 200개 매장에서 결제 버튼을 누를 때마다 주문 저장 프로시저를 부른다. 점심 피크(11:50 ~ 12:30)에는 결제가 초당 15건 남짓 들어온다. 지난달부터 점주들의 민원이 늘었다. "결제 버튼을 누르면 1초쯤 멈췄다가 '주문번호 중복, 다시 시도합니다'가 떠요." 앱은 ORA-00001을 받으면 세 번까지 다시 부르고, 세 번 다 실패하면 결제를 처음부터 다시 하게 한다.

테이블과 인덱스 ​

  • ORD 주문: 1,000,000행 · 22,422블록(오늘 피크 직전 기준) · ORD_NO NUMBER, 1부터 빈틈없이 이어진 번호라 지금 최댓값은 1,000,000이다
  • ORD_ITEM 주문상품: 2,472,717행 · ORD_ITEM_PK (ORD_NO, ITEM_ID) · 주문당 평균 2.5행
  • 인덱스 ORD_PK (ORD_NO): BLEVEL 2 · 이 문제의 POS 운영 DB에는 PK 두 개만 있다 · 통계 최신
  • 결제 한 번의 순서: 번호 받기 → ORD INSERT → ORD_ITEM INSERT(상품마다) → 카드 승인 요청(VAN 응답 평균 1.1초) → COMMIT
  • 시퀀스는 아직 없다

원본 SQL ​

sql
-- PKG_POS.SAVE_ORDER — 결제 버튼 한 번 (ORA-00001 이면 앱이 세 번까지 다시 부른다)
SELECT NVL(MAX(ORD_NO), 0) + 1 INTO v_no FROM ORD;

INSERT INTO ORD (ORD_NO, ORD_DT, ORD_HMS, SHOP_NO)
VALUES (v_no, TO_CHAR(SYSDATE, 'YYYYMMDD'), TO_CHAR(SYSDATE, 'HH24MISS'), :shop_no);

-- 상품마다
INSERT INTO ORD_ITEM (ORD_NO, ITEM_ID, ORD_ITEM_QTY, ORD_DT, ORD_HMS)
VALUES (v_no, :item_id, :qty, TO_CHAR(SYSDATE, 'YYYYMMDD'), TO_CHAR(SYSDATE, 'HH24MISS'));

-- 카드 승인 (VAN) 응답을 받은 뒤
COMMIT;

실행계획 학습용 예시 ​

xplan
[번호 받기 — ALLSTATS LAST]
---------------------------------------------------------------------------------------------
| Id  | Operation                  | Name   | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |        |      1 |        |      1 |00:00:00.01 |       3 |
|   1 |  SORT AGGREGATE            |        |      1 |      1 |      1 |00:00:00.01 |       3 |
|   2 |   INDEX FULL SCAN (MIN/MAX)| ORD_PK |      1 |      1 |      1 |00:00:00.01 |       3 |
---------------------------------------------------------------------------------------------

[시간표 — 11:50:03 · 두 매장]
시각           매장 SH0017 (SID 88)                          매장 SH0142 (SID 131)
11:50:03.20    MAX+1 → 1000001 · ORD INSERT · ORD_ITEM INSERT
               카드 승인 요청 (트랜잭션을 연 채)
11:50:03.24                                                  MAX+1 → 1000001 (88 의 행은 COMMIT 전이라 안 보인다)
                                                             ORD INSERT → 기다림
11:50:04.31    승인 · COMMIT                                 → ORA-00001: unique constraint (DEV.ORD_PK) violated
11:50:04.33                                                  앱이 다시 부른다 → MAX+1 → 1000002 · …

[V$SESSION — 11:50:03.9]
SID   EVENT                            SECS  BLOCKING_SESSION  하는 문장
88    SQL*Net message from client         1                    (카드 승인 응답 대기 · 트랜잭션을 연 채)
131   enq: TX - row lock contention       1  88                INSERT INTO ORD …

[V$LOCK — 같은 시각]
SID   TYPE  대상 / 이름표          LMODE  REQUEST  BLOCK
88    TM    ORD                      3      0        0
88    TX    88 의 트랜잭션            6      0        1
131   TM    ORD                      3      0        0
131   TX    88 의 트랜잭션            0      4        0     ← 같은 키를 먼저 넣은 쪽이 COMMIT 할지 ROLLBACK 할지 기다린다

[11:50 ~ 12:30 집계]
  결제 36,240건 · 첫 시도에서 ORA-00001 2,870번 (7.9%) · 세 번 다 실패 41건
  enq: TX - row lock contention 2,870번 · 평균 0.94초

문제 ​

  1. 번호를 받는 SQL은 3블록짜리인데 두 매장이 왜 같은 번호를 받는지, SID 131이 무엇을 기다리다가 에러를 받는지 시간표와 V$LOCK으로 설명하시오.
  2. 번호가 겹치지도 않고 기다리지도 않도록 고치시오. 그 대가로 무엇을 받아들여야 하는지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자