테마
한빛유통의 「일 정산」 배치는 매일 09:30에 어제 결제 완료된 주문을 정산 테이블에 적재하고 주문에 정산 표시를 한다. 지난달 대사에서 정산에 들어간 주문과 정산 표시가 된 주문이 3건 달랐다. 개발자는 "두 문장이 같은 시점을 보게 하면 된다"며 맨 앞에 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE을 넣었다.
그 뒤로 4월 평일 20일 가운데 13일 동안 배치가 ORA-08177로 실패해, 오과장이 손으로 다시 돌렸다. 다시 돌리면 대개 성공한다.
T_ORD 주문: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ) · 인덱스 IX_T_ORD_PAY_DT (PAY_DT) · 이 문제에서는 정산 표시 STLM_YN('N', 'Y')을 더했다 · 어제 결제 완료 주문은 하루 약 1,100건T_ORD_STLM 정산: 100,000행 · 640블록 · STLM_SEQ는 시퀀스 SQ_STLM_SEQ로 매긴다ORD_ST를 CANCEL로 바꾸고 바로 COMMIT, 09:30 ~ 09:31 사이 평균 3건). 늦게 도착한 결제 콜백도 어제 주문을 COMPLETE로 바꾼다.PROCEDURE DAILY_STLM(p_dt DATE) IS
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- 지난달 건수 불일치 뒤 추가
INSERT INTO T_ORD_STLM (STLM_SEQ, ORD_SEQ, CUS_ID, STLM_DT, STLM_AMT)
SELECT SQ_STLM_SEQ.NEXTVAL, ORD_SEQ, CUS_ID, p_dt, PAY_AMT
FROM T_ORD
WHERE PAY_DT >= p_dt AND PAY_DT < p_dt + 1
AND ORD_ST = 'COMPLETE' AND STLM_YN = 'N';
UPDATE T_ORD
SET STLM_YN = 'Y'
WHERE PAY_DT >= p_dt AND PAY_DT < p_dt + 1
AND ORD_ST = 'COMPLETE' AND STLM_YN = 'N';
COMMIT;
END;
[4월 17일 — 시간표]
09:30:00.02 배치 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE (읽기 시점 고정)
09:30:00.10 INSERT … SELECT → 1,104행 (주문 97,833 은 COMPLETE 로 보여 적재)
09:30:00.61 고객센터 주문 97,833 취소 UPDATE ORD_ST = 'CANCEL' · COMMIT
09:30:01.22 UPDATE T_ORD … → ORA-08177: can't serialize access for this transaction
→ 배치 ROLLBACK · 09:52 오과장이 다시 돌려 성공
[INSERT … SELECT — ALLSTATS LAST]
---------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | 1 | | 0 | 00:00:00.08 | 1290 |
| 1 | LOAD TABLE CONVENTIONAL | T_ORD_STLM | 1 | | 0 | 00:00:00.08 | 1290 |
| 2 | SEQUENCE | SQ_STLM_SEQ | 1 | | 1104 | 00:00:00.02 | 118 |
|* 3 | TABLE ACCESS BY INDEX ROWID BATCHED | T_ORD | 1 | 1080 | 1104 | 00:00:00.01 | 110 |
|* 4 | INDEX RANGE SCAN | IX_T_ORD_PAY_DT | 1 | 1100 | 1180 | 00:00:00.01 | 5 |
---------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(("ORD_ST"='COMPLETE' AND "STLM_YN"='N'))
4 - access("PAY_DT">=:B1 AND "PAY_DT"<:B1+1)
[지난달 — SERIALIZABLE 을 넣기 전의 불일치 3건]
주문 INSERT … SELECT 때 UPDATE 때 결과
96,410 COMPLETE (적재) CANCEL 정산에 들어갔는데 STLM_YN 'N' 으로 남음 (취소된 주문이 정산됐다)
96,522 PENDING (빠짐) COMPLETE 정산에 없는데 STLM_YN 'Y' → 영영 정산되지 않음
96,587 PENDING (빠짐) COMPLETE (같은 모양)
[4월 · 평일 20일] 실패 13일 · 다시 돌린 횟수 17번 · 실패 때마다 걸린 행은 배치가 시작한 뒤 고객센터나 결제 콜백이 고친 어제 주문