Skip to content
튜닝 연습DML과 배치 튜닝
DML-063

판매상품 6개월 치를 5,000건씩 끊어 지우는 정리가 56번 모두 판매 테이블을 통째로 다시 읽는다

중급DELETE유통488 / 700

상황 ​

본사 레거시 유통은 운영 판매 보관 기간을 2년에서 18개월로 줄이기로 했다. 처음 한 번은 2022년 1 ~ 6월을 지우고, 그 뒤로는 매달 가장 오래된 한 달을 지운다. 24시간 매장 두 곳의 POS가 새벽에도 판매를 넣으므로 테이블을 새로 만들어 바꿔 끼울 수는 없고, 언두 여유가 작아서 한 번에 지우지 말라는 DBA의 요청이 있었다. 개발자가 5,000건씩 지우고 커밋하는 루프를 짰다. 리허설에서 판매상품 루프만 1분 48초가 걸렸는데, DBA가 회차별 기록을 보더니 물었다. "5,000건 지우는 데 매번 블록을 4,600 ~ 6,600개씩 읽고, 뒤로 갈수록 더 읽네요?"

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록(블록당 약 109행) · 판매 날짜순 적재 · 2022-01-01 ~ 2023-12-31 · CF10_PK (SLE_NO) · IX_CF10_SLE_DT (SLE_DT) · 2022년 1 ~ 6월 124,318건
  • CF11 판매상품: 1,096,708행 · 8,204블록(블록당 약 134행) · CF11_PK (SLE_NO, ITM_NO) · 판매번호 순으로 쌓였다 · 2022년 1 ~ 6월 272,200행
  • SLE_NO VARCHAR2(15): 판매일자 8자리 + - + 그날의 일련번호 4자리('20220103-0127'). CF11과 CF10 사이에 FK는 없다.
  • T_JOB_LOG (JOB_NM, PIECE, ROWS_DONE, DONE_AT) 작업 기록 테이블이 있다. 통계는 최신이다.

원본 SQL ​

sql
-- 보관 기간 단축 · 처음 한 번 2022년 1 ~ 6월 (개발팀)
DECLARE
  v_cnt NUMBER;
BEGIN
  LOOP                                                   -- ① 판매상품
    DELETE FROM CF11
     WHERE SLE_NO IN (SELECT SLE_NO FROM CF10 WHERE SLE_DT < '20220701')
       AND ROWNUM <= 5000;
    v_cnt := SQL%ROWCOUNT;
    COMMIT;
    EXIT WHEN v_cnt = 0;
  END LOOP;

  LOOP                                                   -- ② 판매
    DELETE FROM CF10
     WHERE SLE_DT < '20220701'
       AND ROWNUM <= 5000;
    v_cnt := SQL%ROWCOUNT;
    COMMIT;
    EXIT WHEN v_cnt = 0;
  END LOOP;
END;

실행계획 학습용 예시 ​

xplan
[① 판매상품 루프 — 54회차 DELETE (DISPLAY_CURSOR · ALLSTATS LAST)]
-------------------------------------------------------------------------------------------
| Id  | Operation              | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------
|   0 | DELETE STATEMENT       |      |      1 |        |      0 |00:00:02.08 |   21730 |
|   1 |  DELETE                | CF11 |      1 |        |      0 |00:00:02.08 |   21730 |
|*  2 |   COUNT STOPKEY        |      |      1 |        |   5000 |00:00:00.41 |    6582 |
|*  3 |    HASH JOIN RIGHT SEMI|      |      1 |   5000 |   5000 |00:00:00.41 |    6582 |
|*  4 |     TABLE ACCESS FULL  | CF10 |      1 |    124K|    124K|00:00:00.09 |    4583 |
|   5 |     TABLE ACCESS FULL  | CF11 |      1 |   1096K|   5000 |00:00:00.03 |    1999 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(ROWNUM<=5000)
   3 - access("SLE_NO"="SLE_NO")
   4 - filter("SLE_DT"<'20220701')

[회차별 기록 — ① 판매상품]
  회차   지운 행   행을 찾는 데 읽은 블록   경과
   1     5,000       4,621              1.6초
   2     5,000       4,658              1.6초
  27     5,000       5,583              1.8초
  54     5,000       6,582              2.1초
  55     2,200       6,620              1.4초
  56         0       6,620              0.9초   (0행임을 알려고 한 번 더 다 읽는다)
  합계  272,200     약 316,000             1분 48초 · user commits 56

[② 판매 루프] 25회차 · 행을 찾는 데 읽은 블록 약 15,000 · 3초
[세션 통계 — ①과 ② 합계] redo size 약 96 MB · undo change vector size 약 41 MB

문제 ​

  1. 5,000건을 지우는 데 매번 4,600블록이 넘게 들고 뒤로 갈수록 더 읽는 이유를 실행계획의 줄 번호로 설명하시오.
  2. 5,000건씩 끊어 커밋하는 이유(언두)는 살리면서, 청크마다 자기 몫만 읽도록 다시 작성하시오. 그 방법이 기대는 전제와 확인 방법도 쓰시오.
  3. 청크로 나눠 지우면 무엇이 줄고 무엇이 그대로인지, 중간에 실패하면 어떻게 다시 돌리는지 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자