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

롤백된 정산 정정 10만 건이 감사 기록에는 바뀐 것으로 남았다

실전DDL한빛유통699 / 700

상황 ​

감사팀 요구(2022)에 따라 정산 금액이 바뀌면 바꾼 사람과 시각, 바뀌기 전과 뒤의 금액을 감사 테이블에 남긴다. 개발팀은 T_ORD_STLM에 행 트리거를 달고, 감사 기록이 정산 쪽 오류에 휩쓸려 사라지지 않게 자율 트랜잭션 프로시저로 기록하게 했다. 지난달 말 「PG 수수료 정정」 배치가 이번 달 정산 10만 건의 금액을 고친 뒤, 합계가 PG사 정산 파일과 달라 ROLLBACK했다. 다음 날 고친 파일로 다시 돌려 성공했다. 분기 대조에서 감사팀이 물었다. "감사 기록을 이어 보면 10만 건이 중간에 끊겨요. 바뀐 적 없는 금액이 기록에 있어요." 박팀장은 감사 요구를 그대로 지키는 설계를 안끼리 비교해 정해 달라고 했다.

테이블과 인덱스 ​

  • T_ORD_STLM 정산: 이번 달 100,000행 · 640블록 · PK_T_ORD_STLM (STLM_SEQ) · STLM_AMT NUMBER(10) NOT NULL
  • T_STLM_AUD 정산 감사(이 문제용): AUD_SEQ(시퀀스 SQ_STLM_AUD) · STLM_SEQ · OLD_AMT · NEW_AMT · AUD_DT · AUD_USER · AUD_PGM · PK (AUD_SEQ) · 2022년부터 412,300행
  • 감사팀은 분기마다 정산별로 기록을 시간순으로 이어, 앞 기록의 NEW_AMT와 다음 기록의 OLD_AMT가 같은지, 마지막 NEW_AMT가 지금 금액과 같은지 대조한다.
  • 정산 금액을 고치는 곳: 정산 화면(한 건씩, 하루 30건쯤)과 월말 PG 수수료 정정 배치(UPDATE 한 문장, 이번 달 정산 전부).

원본 SQL ​

sql
CREATE OR REPLACE PROCEDURE P_AUD_WRITE(p_seq NUMBER, p_old NUMBER, p_new NUMBER) IS
  PRAGMA AUTONOMOUS_TRANSACTION;          -- 감사 기록이 정산 쪽 오류에 휩쓸려 사라지지 않게 (2022)
BEGIN
  INSERT INTO T_STLM_AUD (AUD_SEQ, STLM_SEQ, OLD_AMT, NEW_AMT, AUD_DT, AUD_USER, AUD_PGM)
  VALUES (SQ_STLM_AUD.NEXTVAL, p_seq, p_old, p_new, SYSDATE, USER, SYS_CONTEXT('USERENV', 'MODULE'));
  COMMIT;
END;
/

CREATE OR REPLACE TRIGGER TRG_STLM_AUD
AFTER UPDATE OF STLM_AMT ON T_ORD_STLM
FOR EACH ROW
WHEN (NEW.STLM_AMT <> OLD.STLM_AMT)
BEGIN
  P_AUD_WRITE(:OLD.STLM_SEQ, :OLD.STLM_AMT, :NEW.STLM_AMT);
END;
/

-- 월말 PG 수수료 정정 배치
PROCEDURE P_PG_FEE_FIX(p_rate NUMBER, p_pg_total NUMBER) IS
  v_total NUMBER;
BEGIN
  UPDATE T_ORD_STLM SET STLM_AMT = ROUND(STLM_AMT * p_rate);
  SELECT SUM(STLM_AMT) INTO v_total FROM T_ORD_STLM;
  IF v_total <> p_pg_total THEN
    ROLLBACK;                             -- PG사 합계와 다르면 통째로 되돌린다
    RAISE_APPLICATION_ERROR(-20031, 'PG 합계 불일치 ' || v_total || ' / ' || p_pg_total);
  END IF;
  COMMIT;
END;

실행계획 학습용 예시 ​

xplan
[지난달 말 — 정정 1차 (PG 합계 불일치로 ROLLBACK)]
  경과 12.1 초 · user commits 100,000 · redo size 98.2 MB
  T_ORD_STLM : 바뀐 행 0 (전부 되돌아감) · T_STLM_AUD : +100,000행

[다음 날 — 정정 2차 (고친 파일로 성공)]
  경과 12.5 초 · user commits 100,001 · T_STLM_AUD : +100,000행

[감사팀 분기 대조]
  앞 기록의 NEW_AMT ≠ 다음 기록의 OLD_AMT 인 정산 : 100,000건 (1차 기록의 NEW_AMT는 한 번도 존재하지 않은 금액)

[TKPROF — 2차 · 트리거가 부른 프로시저 안 (dep=1)]
  INSERT INTO T_STLM_AUD (…) VALUES (SQ_STLM_AUD.NEXTVAL, :B6, …)    Execute 100,000 · current 412,377
  (+ 자율 트랜잭션 COMMIT 100,000번 · 이 세션의 log file sync 3 · LGWR log file parallel write 4,180)

[시험 DB — 같은 정정 10만 건, 감사 방식만 바꿔서]
  안                                                              경과    user commits   redo     감사 행   1차처럼 ROLLBACK하면 남는 감사 행
  지금 (행 트리거 + 자율 트랜잭션 INSERT, COMMIT)                    12.4 초       100,001   98.7 MB   100,000   100,000
  ① 행 트리거 + 같은 트랜잭션에서 INSERT                              3.1 초             1   62.3 MB   100,000         0
  ② 복합 트리거 (행 구간은 배열에 모으고 1,000건마다 FORALL INSERT)    1.9 초             1   48.6 MB   100,000         0
  ③ 트리거 없이 배치가 UPDATE 전에 감사 행을 INSERT … SELECT           1.5 초             1   43.9 MB   100,000         0
  (비교) 감사 없음                                                    1.2 초             1   29.4 MB         0         —

문제 ​

  1. 정정 1차가 롤백됐는데 감사 기록 10만 건이 남은 이유와, 지금 판이 감사 없을 때보다 열 배 느린 이유를 설명하시오.
  2. 감사 요구를 지키는 설계를 ① ~ ③과 견주어 하나 고르고 DDL을 작성하시오.
  3. 고른 안의 부작용과, 이미 남은 1차 기록 10만 건을 어떻게 할지 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자