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

본사 실시간 매출판의 하루 합계가 마감 매출보다 늘 몇 건 적다

실전INSERT유통509 / 700

상황 ​

본사 레거시 유통은 매장 판매를 1분마다 본사 사본 테이블 HQ_SLE로 옮겨 실시간 매출판에 띄운다. 지난달 매장 POS의 판매번호 받기를 고친 뒤부터, 매출판의 하루 합계가 마감 정산보다 늘 2 ~ 5건 적다. 빠진 판매는 다음 날에도 매출판에 나타나지 않는다. 지난달의 변경은 이렇다. 결제 저장 때 번호를 받느라 다른 매장의 저장을 기다린다는 민원이 있어서, 번호를 받는 시점을 결제 저장에서 첫 상품 스캔으로 앞당기고 채번 행은 자율 트랜잭션으로 바로 COMMIT하게 했다. 판매 행(CF10, CF11)은 결제가 끝나야 INSERT하고 COMMIT한다. 본사 전산 담당자는 이렇게 제안했다. "판매번호를 시퀀스로 바꾸고 ORDER를 붙이면 순서가 맞지 않을까요?"

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · CF10_PK (SLE_NO) · SLE_NO 날짜 여덟 자리와 하이픈, 그날의 네 자리 일련번호 · 하루 약 700건
  • SLE_HMS는 결제 시각이다. 결제 저장의 INSERT가 DB의 SYSDATE로 적고, 바로 COMMIT한다
  • CF11 판매상품: 1,096,708행 · CF11_PK (SLE_NO, ITM_NO) · 판매당 약 2.2행
  • CF10_NUM 판매번호 채번 행(지난달에 만들었다): PK (SLE_DT) · 번호는 자율 트랜잭션으로 받고 바로 COMMIT한다
  • HQ_SLE 본사 사본(이 문제를 위해 더한 테이블): PK (SLE_NO) · SLE_DT · SHP_NO · SLE_AMT
  • 첫 상품 스캔부터 결제 COMMIT까지는 보통 20 ~ 90초다. 손님이 지갑을 찾으면 몇 분이 걸린다

원본 SQL ​

sql
-- 1분마다 — 지난번에 옮긴 마지막 판매번호 뒤부터 옮긴다
INSERT INTO HQ_SLE (SLE_NO, SLE_DT, SHP_NO, SLE_AMT)
SELECT h.SLE_NO, h.SLE_DT, h.SHP_NO, SUM(d.SLE_QTY * d.SLE_UPRC)
  FROM CF10 h
  JOIN CF11 d ON d.SLE_NO = h.SLE_NO
 WHERE h.SLE_NO > :last_sle_no
 GROUP BY h.SLE_NO, h.SLE_DT, h.SHP_NO;

SELECT MAX(SLE_NO) INTO :last_sle_no FROM HQ_SLE;    -- 다음 번의 시작점
COMMIT;

실행계획 학습용 예시 ​

xplan
[시간표 — 2023-12-16(토)]
시각        매장 1002 POS (SID 41)                  매장 1004 POS (SID 58)                  1분 추출 (SID 90)
10:00:05    첫 스캔 → 번호 20231216-0412 (자율 COMMIT)
10:00:20                                            첫 스캔 → 번호 0413 (자율 COMMIT)
10:00:41                                            결제 · CF10 · CF11 INSERT · COMMIT
10:01:00                                                                                    SLE_NO > '…-0411' → 0413 을 옮긴다
                                                                                            (0412 는 COMMIT 전이라 안 보인다)
                                                                                            다음 시작점 '…-0413'
10:01:12    결제 · CF10 · CF11 INSERT · COMMIT
10:02:00                                                                                    SLE_NO > '…-0413' → 0412 는 범위 밖

[12월 둘째 주 · 하루 대사]
날짜        마감 정산 판매   HQ_SLE   빠진 판매번호
12/11(월)        684          681    0233 · 0571 · 0602
12/16(토)        731          726    0412 · 0498 · 0517 · 0603 · 0688
  빠진 판매는 모두 번호를 받고 나서 다음 번호보다 늦게 COMMIT 한 판매다

[추출 한 번 — ALLSTATS LAST · 10:01:00]
------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT               |         |      1 |        |      0 |00:00:00.01 |      21 |
|   1 |  LOAD TABLE CONVENTIONAL       | HQ_SLE  |      1 |        |      0 |00:00:00.01 |      21 |
|   2 |   HASH GROUP BY                |         |      1 |      1 |      1 |00:00:00.01 |      12 |
|   3 |    NESTED LOOPS                |         |      1 |      2 |      2 |00:00:00.01 |      12 |
|   4 |     TABLE ACCESS BY INDEX ROWID| CF10    |      1 |      1 |      1 |00:00:00.01 |       4 |
|*  5 |      INDEX RANGE SCAN          | CF10_PK |      1 |      1 |      1 |00:00:00.01 |       3 |
|   6 |     TABLE ACCESS BY INDEX ROWID| CF11    |      1 |      2 |      2 |00:00:00.01 |       8 |
|*  7 |      INDEX RANGE SCAN          | CF11_PK |      1 |      2 |      2 |00:00:00.01 |       3 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access("H"."SLE_NO">:LAST_SLE_NO)
   7 - access("D"."SLE_NO"="H"."SLE_NO")

문제 ​

  1. 0412가 왜 영영 옮겨지지 않는지 번호를 받는 시점과 COMMIT 시점, 읽기 일관성으로 설명하시오.
  2. 빠지는 판매가 없도록 고치시오. 추출 쪽을 고치는 방법과 번호 받기 쪽을 고치는 방법을 비교하고, 하나를 골라 SQL로 작성하시오.
  3. 본사 전산 담당자의 "시퀀스와 ORDER" 제안에 답하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자