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

매일 아침 개점 직후 첫 주문에서 몇몇 매장의 키오스크가 번호 발급 실패를 띄운다

기초PLSQL버거508 / 700

상황 ​

버거원 매장은 주문마다 영수증과 대기 번호판에 오늘의 주문번호를 띄운다. 번호는 매장마다 매일 1번부터 시작하고, 카운터 POS와 키오스크 두 대가 같은 번호 체계를 쓴다. 매일 아침 개점 직후 첫 주문에서 하루 30 ~ 40개 매장의 키오스크나 POS가 "번호 발급 실패, 다시 주문해 주세요"를 띄운다. 그 뒤로는 하루 종일 괜찮다.

테이블과 인덱스 ​

  • SHOP 매장: 200행 · 5블록 · SHOP_NO 'SH0001' · PK
  • SHOP_RCPT_NUM 매장별 일별 번호(이 문제를 위해 더한 채번 테이블): SHOP_RCPT_NUM_PK (SHOP_NO, RCPT_DT) · LAST_NO · 하루 200행씩 생긴다
  • 한 매장에서 동시에 번호를 받는 단말은 많아야 세 대다. 번호를 받은 주문은 결제가 끝나면 바로 COMMIT한다(1 ~ 2초)
  • 개점 시각은 매장마다 다르다(06:30 ~ 10:00). 개점 직후에는 키오스크와 카운터에 손님이 한꺼번에 몰린다

원본 SQL ​

sql
-- PKG_POS.NEXT_RCPT_NO — 오늘의 주문번호
FUNCTION NEXT_RCPT_NO(p_shop VARCHAR2, p_dt VARCHAR2) RETURN NUMBER IS
  v_no NUMBER;
BEGIN
  SELECT LAST_NO + 1 INTO v_no
    FROM SHOP_RCPT_NUM
   WHERE SHOP_NO = p_shop AND RCPT_DT = p_dt
     FOR UPDATE;
  UPDATE SHOP_RCPT_NUM SET LAST_NO = v_no
   WHERE SHOP_NO = p_shop AND RCPT_DT = p_dt;
  RETURN v_no;
EXCEPTION
  WHEN NO_DATA_FOUND THEN                        -- 오늘 첫 주문이면 행을 만든다
    INSERT INTO SHOP_RCPT_NUM (SHOP_NO, RCPT_DT, LAST_NO) VALUES (p_shop, p_dt, 1);
    RETURN 1;
END;

실행계획 학습용 예시 ​

xplan
[시간표 — 매장 SH0088 · 07:00 개점]
시각          카운터 POS (SID 140)                         키오스크 1 (SID 166)
07:00:21.30   SELECT … FOR UPDATE → 0행 (NO_DATA_FOUND)
07:00:21.31                                                SELECT … FOR UPDATE → 0행 (NO_DATA_FOUND)
07:00:21.32   INSERT ('SH0088', '20131014', 1) → 1번
07:00:21.33                                                INSERT ('SH0088', '20131014', 1) → 기다림
07:00:22.90   결제 끝 · COMMIT                             → ORA-00001: unique constraint (DEV.SHOP_RCPT_NUM_PK) violated
                                                           → 키오스크 "번호 발급 실패"

[V$LOCK — 07:00:22]
SID   TYPE  대상 / 이름표            LMODE  REQUEST  BLOCK
140   TM    SHOP_RCPT_NUM              3      0        0
140   TX    140 의 트랜잭션             6      0        1
166   TM    SHOP_RCPT_NUM              3      0        0
166   TX    140 의 트랜잭션             0      4        0

[지난 2주 · 번호 발급 실패]
  하루 31 ~ 42건 · 전부 그 매장의 그날 첫 번째 ~ 세 번째 주문 · 그 뒤로는 0건
  같은 시각에 다른 매장끼리는 한 번도 기다리지 않았다

문제 ​

  1. 시간표를 보고 왜 그날 첫 주문에서만 실패하는지, FOR UPDATE가 왜 이 충돌을 막지 못하는지 설명하시오.
  2. 개점 직후에도 실패하지 않게 고치시오. 번호를 받는 문장도 줄일 수 있으면 줄이시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자