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

매장 결제 저장이 건마다 0.3초씩 걸리고 토요일 오후엔 가끔 저장이 실패한다

중급PLSQL유통507 / 700

상황 ​

본사 레거시 유통의 매장 POS(6개 매장)는 판매를 저장할 때 판매번호를 날짜 여덟 자리와 하이픈, 그날의 네 자리 일련번호로 매긴다('20231231-0612'). 일련번호는 날마다 0001부터 다시 시작한다. 매장에서는 결제 저장이 건마다 0.3초씩 멈칫한다고 하고, 손님이 몰리는 토요일 오후에는 하루 서너 번 ORA-00001: unique constraint (PUP.CF10_PK) violated로 저장이 실패해 결제를 다시 한다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · SLE_NO VARCHAR2(15) · SLE_DT VARCHAR2(8) · 하루 약 700건 · 토요일 오후 피크는 분당 6 ~ 8건
  • 인덱스는 CF10_PK (SLE_NO) 하나뿐이다(BLEVEL 2 · 리프 약 1,650블록) · SLE_DT에는 인덱스가 없다 · 통계 최신
  • CF11 판매상품: 1,096,708행 · CF11_PK (SLE_NO, ITM_NO) · 판매당 약 2.2행
  • 판매 저장 한 번: 번호 받기 → CF10 INSERT → CF11 INSERT(상품마다) → COMMIT · 카드 승인은 저장 전에 끝난다
  • 판매번호의 모양은 영수증, 반품, 본사 정산이 모두 쓰므로 바꿀 수 없다

원본 SQL ​

sql
-- PKG_POS.SAVE_SALE — 판매번호 받기
SELECT :dt || '-' || LPAD(NVL(MAX(TO_NUMBER(SUBSTR(SLE_NO, 10, 4))), 0) + 1, 4, '0')
  INTO v_sle_no
  FROM CF10
 WHERE SLE_DT = :dt;

INSERT INTO CF10 (SLE_NO, SLE_DT, SLE_HMS, SLE_DOW, SHP_NO)
VALUES (v_sle_no, :dt, :hms, :dow, :shp_no);
-- 상품마다 CF11 INSERT → COMMIT

실행계획 학습용 예시 ​

xplan
[번호 받기 — ALLSTATS LAST · 2023-12-16(토) 15시]
-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      1 |00:00:00.29 |    4583 |
|   1 |  SORT AGGREGATE    |      |      1 |      1 |      1 |00:00:00.29 |    4583 |
|*  2 |   TABLE ACCESS FULL| CF10 |      1 |    685 |    512 |00:00:00.29 |    4583 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("SLE_DT"=:DT)

[시간표 — 15:12:40 · 매장 둘]
시각          매장 1003 (SID 34)                               매장 1005 (SID 71)
15:12:40.00   번호 받기 시작 (FULL 4,583블록)
15:12:40.12                                                    번호 받기 시작
15:12:40.29   → 20231216-0513 · CF10 INSERT · CF11 INSERT
15:12:40.41                                                    → 20231216-0513 (34 의 행은 COMMIT 전) · CF10 INSERT
                                                               → 기다림 (TX REQUEST 4)
15:12:40.45   COMMIT                                           → ORA-00001 · POS "저장 실패, 다시 결제하세요"

문제 ​

  1. 번호 하나를 받는 데 왜 4,583블록을 읽는지, 그 0.29초가 중복 에러와 어떤 관계가 있는지 설명하시오.
  2. 판매번호의 모양은 그대로 두고 번호 받기를 고치시오. 번호가 겹쳤을 때 손님이 결제를 다시 하지 않도록 처리하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자