Skip to content
튜닝 연습PL/SQL과 사용자 정의 함수
PLSQL-042

주간 정산 보정이 0.3초에서 9초가 됐고, 실패한 날 다시 돌린 보정은 6만 건이 모자랐다

중급PLSQL한빛유통692 / 700

상황 ​

주간 정산 보정 배치 P_STLM_ADJ는 0.3초쯤 걸렸다. 지난달 장애 회의에서 "배치가 어디까지 했는지 알 수 없다"는 말이 나와, 개발자가 정산 한 건을 처리할 때마다 PKG_LOG.WRITE로 한 줄씩 남기게 했다. 로그는 배치가 실패해 롤백돼도 남도록 자율 트랜잭션으로 쓴다. 그 뒤로 배치가 9초 넘게 걸린다. 지난주 목요일 새벽 배치가 테이블스페이스 부족으로 실패했다. 운영 담당은 공간을 늘린 뒤, 로그의 마지막 OK 번호 다음부터 다시 돌렸다. 월요일 정산 확정에서 보정이 64,000건 모자랐다.

테이블과 인덱스 ​

  • T_ORD_STLM 정산: 이번 주 100,020행 · 약 640블록 · STLM_SEQ 1 ~ 100,020
  • M_CUS: 2,000행 · PK_M_CUS (CUS_ID) · 보정률은 등급 A 0.1%, B 0.2%, 그 밖 0.3%
  • T_STLM_ADJ 보정 이력(이 문제용): 주차(ADJ_WK)마다 지우고 다시 넣는다 · 인덱스 없음
  • T_BATCH_LOG(이 문제용): PK (LOG_SEQ) · LOG_SEQ는 시퀀스 SQ_BATCH_LOG(CACHE 20) · 지금 3,214,906행
  • p_from_seq 인자는 2021년에 1,000건마다 커밋하던 판의 재시작 인자다. 지금 판은 끝에 한 번만 커밋한다.
  • 운영 담당은 배치가 도는 동안 T_BATCH_LOG를 조회해 진행 상황을 본다.

원본 SQL ​

sql
-- 로그 패키지 (지난달) — 배치가 롤백돼도 남도록 자율 트랜잭션
CREATE OR REPLACE PACKAGE BODY PKG_LOG IS
  PROCEDURE WRITE(p_job VARCHAR2, p_key NUMBER, p_msg VARCHAR2) IS
    PRAGMA AUTONOMOUS_TRANSACTION;
  BEGIN
    INSERT INTO T_BATCH_LOG (LOG_SEQ, LOG_DT, JOB_NM, KEY_NO, MSG)
    VALUES (SQ_BATCH_LOG.NEXTVAL, SYSTIMESTAMP, p_job, p_key, p_msg);
    COMMIT;
  END;
END;
/

-- 주간 정산 보정
PROCEDURE P_STLM_ADJ(p_wk VARCHAR2, p_from_seq NUMBER DEFAULT 0) IS
  CURSOR c IS
    SELECT s.STLM_SEQ, s.STLM_AMT, m.CUS_GD
      FROM T_ORD_STLM s, M_CUS m
     WHERE m.CUS_ID = s.CUS_ID
       AND s.STLM_SEQ > p_from_seq
     ORDER BY s.STLM_SEQ;
  TYPE t_rows IS TABLE OF c%ROWTYPE;
  TYPE t_num  IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
  v      t_rows;
  v_rate t_num;
BEGIN
  PKG_LOG.WRITE('STLM_ADJ', NULL, 'START ' || p_wk);
  IF p_from_seq = 0 THEN
    DELETE FROM T_STLM_ADJ WHERE ADJ_WK = p_wk;
  END IF;
  OPEN c;
  LOOP
    FETCH c BULK COLLECT INTO v LIMIT 1000;
    EXIT WHEN v.COUNT = 0;
    FOR i IN 1 .. v.COUNT LOOP
      v_rate(i) := CASE v(i).CUS_GD WHEN 'A' THEN 0.001 WHEN 'B' THEN 0.002 ELSE 0.003 END;
      PKG_LOG.WRITE('STLM_ADJ', v(i).STLM_SEQ, 'OK');          -- 장애 때 어디까지 했는지 보려고 (지난달)
    END LOOP;
    FORALL i IN 1 .. v.COUNT
      INSERT INTO T_STLM_ADJ (ADJ_WK, STLM_SEQ, ADJ_RATE, ADJ_AMT)
      VALUES (p_wk, v(i).STLM_SEQ, v_rate(i), ROUND(v(i).STLM_AMT * v_rate(i)));
  END LOOP;
  CLOSE c;
  COMMIT;
  PKG_LOG.WRITE('STLM_ADJ', NULL, 'END');
EXCEPTION
  WHEN OTHERS THEN
    ROLLBACK;
    PKG_LOG.WRITE('STLM_ADJ', NULL, 'FAIL ' || SQLERRM);
    RAISE;
END;

실행계획 학습용 예시 ​

xplan
[세션 통계 — 정상 한 판 (정산 100,020건)]
                                     로그 넣기 전      지금
  경과 시간                              0.31 초        9.4 초
  CPU used by this session              0.17 초        5.9 초
  recursive calls                         2,412       207,461
  user commits                                1       100,023
  session logical reads                  22,363       521,870
  redo size                             11.2 MB       78.4 MB
  log file sync (이 세션)                      1             3
  LGWR log file parallel write (같은 구간)    14         4,106
  T_BATCH_LOG 증가                         —       100,023행

[지난주 목요일 — T_BATCH_LOG]
  02:00:03  START 2024W23
  02:00:09  OK 64000                                   ← 마지막 OK
  02:00:09  FAIL ORA-01653: unable to extend table SQLP.T_STLM_ADJ by 128 in tablespace USERS
  09:30:41  START 2024W23      (공간을 늘리고 P_STLM_ADJ('2024W23', 64000)로 다시 돌림)
  09:30:44  END

[월요일 정산 확정 대조]
  정산 100,020 · 보정 이력 36,020 · 차이 64,000

문제 ​

  1. 로그 한 줄 때문에 배치가 30배 느려진 이유를 세션 통계로 설명하시오. 이 세션의 log file sync가 3번뿐인데도 커밋이 원인인 까닭도 쓰시오.
  2. 다시 돌린 보정이 64,000건 모자란 이유를 설명하시오.
  3. 진행 상황을 볼 수 있으면서 두 문제가 없도록 배치를 고치시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자