테마
매월 1일 「정산 수수료 반영」 단계는 지난달 정산마다 결제 수단과 고객 등급으로 PG 수수료를 계산해 정산 행에 적는다. 수수료 규칙은 결제 화면과 같은 패키지 PKG_PG_FEE를 쓴다.
지난주 코드 리뷰에서 "행마다 UPDATE"를 지적받은 개발자가 BULK COLLECT와 FORALL로 바꿨다. 그런데 걸리는 시간은 1.41초에서 1.37초가 됐을 뿐이다. 개발자가 물었다. "recursive calls는 4,162에서 19로 줄었는데 왜 그대로죠?"
T_ORD_STLM 정산(이 문제용으로 FEE_AMT 컬럼을 더한 판): 100,000행 · 640블록 · 2024년 11월 정산 4,110건 · 인덱스 없음T_ORD: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ) · PAY_TP CARD · CASH · POINTM_CUS: 2,000행 · 30블록 · PK_M_CUS (CUS_ID) · CUS_GDPKG_PG_FEE.CALC(pay_tp, cus_gd, amt): SQL 없는 PL/SQL 함수 · 이 단계가 도는 동안 다른 세션은 T_ORD_STLM을 고치지 않는다PROCEDURE APPLY_PG_FEE(p_ym VARCHAR2) IS -- p_ym = '202411'
CURSOR c IS
SELECT s.STLM_SEQ, s.STLM_AMT, o.PAY_TP, m.CUS_GD
FROM T_ORD_STLM s, T_ORD o, M_CUS m
WHERE s.STLM_DT >= TO_DATE(p_ym, 'YYYYMM')
AND s.STLM_DT < ADD_MONTHS(TO_DATE(p_ym, 'YYYYMM'), 1)
AND o.ORD_SEQ = s.ORD_SEQ
AND m.CUS_ID = s.CUS_ID;
TYPE t_num IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
TYPE t_vc IS TABLE OF VARCHAR2(40) INDEX BY PLS_INTEGER;
v_seq t_num; v_amt t_num; v_tp t_vc; v_gd t_vc; v_fee t_num;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT INTO v_seq, v_amt, v_tp, v_gd LIMIT 1000;
EXIT WHEN v_seq.COUNT = 0;
FOR i IN 1 .. v_seq.COUNT LOOP
v_fee(i) := PKG_PG_FEE.CALC(v_tp(i), v_gd(i), v_amt(i));
END LOOP;
FORALL i IN 1 .. v_seq.COUNT
UPDATE T_ORD_STLM
SET FEE_AMT = v_fee(i)
WHERE STLM_SEQ = v_seq(i);
END LOOP;
CLOSE c;
COMMIT;
END;
[두 판 비교 — 2024년 11월 정산 4,110건 (V$MYSTAT 증가분)]
판 경과 recursive calls UPDATE Executions session logical reads
지난주 판 (커서 FOR 루프 · 행마다 UPDATE) 1.41 초 4,162 4,110 2,636,178
지금 판 (BULK 1000 · FORALL UPDATE) 1.37 초 19 5 2,636,141
[V$SQL — 지금 판의 UPDATE]
SQL_TEXT EXECUTIONS ROWS_PROCESSED BUFFER_GETS
UPDATE T_ORD_STLM SET FEE_AMT = :B1 WHERE STLM_SEQ = :B2 5 4,110 2,634,510
[그 UPDATE의 원소 하나 — 실행계획]
---------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 1 | | 0 |00:00:00.01 | 641 |
| 1 | UPDATE | T_ORD_STLM | 1 | | 0 |00:00:00.01 | 641 |
|* 2 | TABLE ACCESS FULL| T_ORD_STLM | 1 | 1 | 1 |00:00:00.01 | 640 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("STLM_SEQ"=:B1)
V$SQL과 실행계획으로 설명하시오.