테마
버거 본사는 매장 POS 주문을 가맹 정산 시스템으로 보내는 전송 큐를 둔다. POS가 주문을 넣을 때마다 큐에 한 행이 쌓이고, 본사의 전송 작업이 100건씩 집어 보낸 뒤 DONE으로 바꾼다. 점심 피크에 큐가 1만 건 넘게 쌓여 정산 시스템의 매출이 40분씩 늦어지자, 지난주 전송 작업을 하나에서 넷으로 늘렸다. 쌓이는 모양은 그대로다. 개발팀은 이렇게 말한다. "SKIP LOCKED를 넣었으니 넷이 서로 다른 건을 집을 거예요. 정산 시스템이 느린 것 같아요."
ORD_SEND_Q 전송 큐(이 문제의 파생 테이블): Q_SEQ (PK, 시퀀스) · ORD_NO · SHOP_NO · SEND_ST (READY · DONE) · REG_DTT · 인덱스 ORD_SEND_Q_X01 (SEND_ST, Q_SEQ) · DONE 행은 밤에 지운다PROCEDURE SEND_WORKER IS
TYPE t_q IS TABLE OF ORD_SEND_Q%ROWTYPE;
v_q t_q;
BEGIN
LOOP
SELECT * BULK COLLECT INTO v_q
FROM ORD_SEND_Q
WHERE SEND_ST = 'READY'
AND ROWNUM <= 100
FOR UPDATE SKIP LOCKED; -- 남이 잡은 건은 건너뛴다
IF v_q.COUNT = 0 THEN
DBMS_SESSION.SLEEP(30); -- 큐가 비었으면 30초 쉰다
ELSE
PKG_STLM_IF.SEND(v_q); -- 정산 시스템으로 보낸다 (100건에 약 30초)
FORALL i IN 1 .. v_q.COUNT
UPDATE ORD_SEND_Q SET SEND_ST = 'DONE' WHERE Q_SEQ = v_q(i).Q_SEQ;
COMMIT;
END IF;
END LOOP;
END;
[「다음 100건」 한 번 — W2 · W1 이 100건을 쥐고 보내는 중]
---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 0 |00:00:00.01 | 104 |
| 1 | FOR UPDATE | | 1 | | 0 |00:00:00.01 | 104 |
|* 2 | COUNT STOPKEY | | 1 | | 100 |00:00:00.01 | 4 |
| 3 | TABLE ACCESS BY INDEX ROWID | ORD_SEND_Q | 1 | 100 | 100 |00:00:00.01 | 4 |
|* 4 | INDEX RANGE SCAN | ORD_SEND_Q_X01 | 1 | 100 | 100 |00:00:00.01 | 2 |
---------------------------------------------------------------------------------------------------------
2 - filter(ROWNUM<=100)
4 - access("SEND_ST"='READY')
[작업 넷 — 12:00 ~ 12:10]
작업 집은 묶음 보낸 건 0건을 받고 쉰 횟수
W1 19 1,900 0
W2 1 100 18
W3 0 0 20
W4 1 100 19
큐 (READY) 12:00 9,850건 → 12:10 10,920건 (그동안 3,170건이 들어왔다)
[V$SESSION — 12:05:00]
SID EVENT SECS 하고 있는 일
311 TCP Socket (KGAS) 12 PKG_STLM_IF.SEND (W1 · 100건을 쥔 채 보내는 중)
318 PL/SQL lock timer 21 DBMS_SESSION.SLEEP (W2)
325 PL/SQL lock timer 7 DBMS_SESSION.SLEEP (W3)
332 PL/SQL lock timer 28 DBMS_SESSION.SLEEP (W4)
(enq: TX - row lock contention 은 하나도 없다)