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

하루 5,000개 한정 세트가 5,037개 팔렸다

기초UPDATE버거525 / 700

상황 ​

버거원 본사는 신메뉴 출시에 맞춰 「더블 패티 세트」를 전국 하루 5,000개 한정으로 팔았다. POS는 세트를 담을 때마다 본사 DB의 남은 수량을 확인하고 담은 만큼 줄인다. 12:41에 남은 수량이 0이 되어 판매가 멈췄는데, 마감 뒤 주문상품을 세어 보니 5,037개가 팔려 있었다. 재료가 모자란 매장 37곳은 고객에게 쿠폰으로 사과했다. 개발팀은 이렇게 말했다. "UPDATE가 행 락을 기다리게 되어 있으니 동시에 줄여도 안전합니다."

테이블과 인덱스 ​

  • ITEM_LMT 한정 판매 수량(이 문제를 위해 더한 테이블): PK (ITEM_ID, SALE_DT) · LMT_QTY 한정 수량 · REMAIN_QTY 남은 수량 · 한정 메뉴 하루 한 행
  • ITEM 상품: 92행 · 5블록 · 더블 패티 세트는 ITEM_ID = 'I0088'
  • ORD_ITEM 주문상품: 2,472,717행 · ORD_ITEM_PK (ORD_NO, ITEM_ID)
  • 결제 한 번의 순서: 한정 수량 줄이기, ORD와 ORD_ITEM INSERT, 카드 승인(VAN 응답 평균 1.1초), COMMIT
  • 점심 피크(12:00 ~ 12:41)에는 전국 매장에서 이 세트가 분당 70 ~ 90개 팔렸다.

원본 SQL ​

sql
-- PKG_POS.ADD_LIMITED — 한정 세트를 담을 때 (같은 트랜잭션에서 주문을 넣고, 카드 승인 뒤 COMMIT)
SELECT REMAIN_QTY INTO v_remain
  FROM ITEM_LMT
 WHERE ITEM_ID = :item_id AND SALE_DT = :sale_dt;

IF v_remain < :qty THEN
  RAISE_APPLICATION_ERROR(-20010, '한정 수량이 모두 팔렸습니다');
END IF;

UPDATE ITEM_LMT
   SET REMAIN_QTY = v_remain - :qty
 WHERE ITEM_ID = :item_id AND SALE_DT = :sale_dt;

실행계획 학습용 예시 ​

xplan
[시간표 — 12:31:07 · 남은 수량 412]
시각           매장 SH0031 POS (SID 74)                      매장 SH0158 POS (SID 191)
12:31:07.10    SELECT → 412 · 412 - 1 = 411
               UPDATE … SET REMAIN_QTY = 411 · 주문 INSERT · 카드 승인 요청
12:31:07.30                                                  SELECT → 412 (74 는 COMMIT 전이라 412 가 보인다) · 411 계산
                                                             UPDATE … SET REMAIN_QTY = 411 → 기다림
12:31:08.21    승인 · COMMIT (REMAIN_QTY 411)                → 깨어남 · 계산해 둔 411 을 그대로 쓴다
12:31:09.40                                                  승인 · COMMIT (REMAIN_QTY 411)
               → 두 개가 팔렸는데 남은 수량은 하나만 줄었다

[V$SESSION — 12:31:07.8]
SID   EVENT                            SECS  BLOCKING_SESSION  하는 문장
74    SQL*Net message from client         0                    (카드 승인 응답 대기 · 트랜잭션을 연 채)
191   enq: TX - row lock contention       0  74                UPDATE ITEM_LMT SET REMAIN_QTY = :B3 - :B2 …

[마감 집계]
  한정 5,000 · 판매(ORD_ITEM 집계) 5,037 · 마지막 REMAIN_QTY 0
  12:00 ~ 12:41  enq: TX - row lock contention (ITEM_LMT) 1,906번 · 평균 0.7초
  기다렸다 깨어난 UPDATE 가운데 앞사람이 줄인 값을 덮어쓴 경우 37번

문제 ​

  1. 시간표를 보고 SID 191이 행 락을 기다렸는데도 수량이 하나만 줄어든 이유를 설명하시오.
  2. 한정 수량을 넘겨 팔지 않도록 고치시오. 고친 뒤에도 남는 기다림이 무엇인지 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자