테마
본사 레거시 유통의 자동발주 배치는 매일 17:30에 재고가 기준 아래로 떨어진 상품을 모아 발주 60건 안팎을 만든다. 한 트랜잭션으로 돌고 약 3분 걸린다. 지난주부터 그 3분 동안 물류센터의 「입고 확인」 화면(발주 진행 코드를 입고 완료로 바꾼다)이 저장 버튼에서 멈춘다. 2주 전에 번호 받기 함수를 고친 사람은 이렇게 적어 두었다. "발주번호가 두 번 겹쳐서 LOCK TABLE 추가 (MAX에 FOR UPDATE는 ORA-01786)."
CF12 발주: 734행 · 10블록 · CF12_PK (ORD_NO) · ORD_DT VARCHAR2(8) · ORD_PRGS_CD 발주 진행 코드ORD_NO: 발주일자 여덟 자리와 그날의 두 자리 일련번호(예 '2023010205') · 날마다 01부터 다시 시작한다CF13 발주상품: 7,102행 · 57블록 · PK (ORD_NO, ITM_NO)UPDATE CF12 SET ORD_PRGS_CD = :cd WHERE ORD_NO = :no · 17:00 ~ 18:00이 가장 바쁘다-- PKG_PO.NEW_ORDER_NO — 자동발주 배치와 MD 화면이 같이 부른다 (COMMIT 은 부른 쪽이 한다)
FUNCTION NEW_ORDER_NO(p_dt VARCHAR2) RETURN VARCHAR2 IS
v_no CF12.ORD_NO%TYPE;
BEGIN
LOCK TABLE CF12 IN EXCLUSIVE MODE; -- 번호가 겹쳐서 추가 (2주 전)
SELECT p_dt || LPAD(NVL(MAX(SUBSTR(ORD_NO, 9, 2)), '00') + 1, 2, '0')
INTO v_no
FROM CF12
WHERE ORD_DT = p_dt;
RETURN v_no;
END;
-- 자동발주 배치 (17:30 · 한 트랜잭션)
FOR r IN (SELECT PTNR_NO FROM … /* 기준 미달 상품을 거래처별로 묶은 60 건 */) LOOP
v_no := NEW_ORDER_NO(:today);
INSERT INTO CF12 (ORD_NO, ORD_DT, PTNR_NO, ORD_PRGS_CD) VALUES (v_no, :today, r.PTNR_NO, '10');
INSERT INTO CF13 (ORD_NO, ITM_NO, ORD_QTY) SELECT v_no, … ;
END LOOP;
COMMIT;
[V$SESSION — 17:31:40 · 배치가 돈 지 100초]
SID EVENT SECS BLOCKING_SESSION 하는 문장
212 db file sequential read 0 INSERT INTO CF13 … (자동발주 배치)
57 enq: TM - contention 88 212 UPDATE CF12 SET ORD_PRGS_CD … (입고 확인 1)
63 enq: TM - contention 61 212 UPDATE CF12 SET ORD_PRGS_CD … (입고 확인 2)
81 enq: TM - contention 14 212 LOCK TABLE CF12 IN EXCLUSIVE MODE (MD 발주 등록)
[V$LOCK — 같은 시각]
SID TYPE 대상 LMODE REQUEST BLOCK
212 TM CF12 6 0 1 ← LOCK TABLE … EXCLUSIVE · COMMIT 까지
212 TM CF13 3 0 0
212 TX 212 의 트랜잭션 6 0 0
57 TM CF12 0 3 0
63 TM CF12 0 3 0
81 TM CF12 0 6 0
[17:30 ~ 17:33 · 배치 한 번]
발주 61건 · 발주번호 '2023121501' ~ '2023121561' · 입고 확인 대기 최대 172초
입고 확인 화면의 조회(SELECT)는 막히지 않았다 · 배치가 COMMIT 하자 기다리던 UPDATE 가 한꺼번에 풀렸다
V$LOCK을 보고 입고 확인 화면이 무엇을 기다리는지, 왜 배치가 끝날 때까지 풀리지 않는지 설명하시오.