Skip to content
튜닝 연습인덱스 스캔 효율과 테이블 액세스
ACC-066

바꾸는 주문 수는 지난달과 같은데 7월 1일 정산 확정의 redo가 9.6MB에서 24.3MB가 됐다

중급UPDATE한빛유통141 / 700

상황 ​

매월 1일 03:00 마감 배치는 지난달 주문을 정산 확정으로 바꾼다(STLM_YN을 'N'에서 'Y'로). 정산팀의 「미정산 주문 현황」은 정산 담당 열 명이 수시로 열어 하루 약 300번 실행되는데, 매번 테이블을 통째로 읽어서 6월에 조민우가 (STLM_YN, ORD_DT, ORD_AMT) 인덱스를 만들었다. 화면은 5,826블록에서 50블록 안팎으로 줄었다. 그런데 7월 1일 마감에서 확정 UPDATE의 redo가 9.6MB에서 24.3MB로, 시간이 1.3초에서 3.4초로 늘었다. 배치 담당 오과장이 물었다. "바꾸는 건 지난달처럼 2만 7천 건 남짓인데 왜 두 배 반이냐?"

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · 이 문제에서는 STLM_YN CHAR(1)(정산 여부) 컬럼이 있다고 둔다 · 새 주문은 'N'으로 들어오고, 마감 배치가 지난달 주문을 한꺼번에 'Y'로 바꾼다 · 그래서 'N'인 행은 늘 그달 치(최대 약 2만 8천 건)다
  • 5월 주문 28,303건 · 6월 주문 27,390건 · 7월 15일 기준 미정산 13,695건
  • 인덱스 IX_ORD_BIG_DT_AMT (ORD_DT, ORD_AMT): 「기간 매출」용 · IX_ORD_BIG_YN_DT_AMT (STLM_YN, ORD_DT, ORD_AMT): 6월에 새로 만들었다 · BLEVEL 2 · 리프 약 3,520블록(리프 한 블록에 약 284항목) · 통계 최신
  • 두 인덱스 모두 CLUSTERING_FACTOR가 약 1,000,000이다 · 낮에는 주문이 하루 약 900건 INSERT된다

원본 SQL ​

sql
-- 매월 1일 03:00 · 정산 확정 (지난달 주문)
UPDATE T_ORD_BIG
   SET STLM_YN = 'Y'
 WHERE ORD_DT >= ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -1)
   AND ORD_DT <  TRUNC(SYSDATE, 'MM')
   AND STLM_YN = 'N';

-- 「미정산 주문 현황」 · 하루 약 300번
SELECT ORD_DT, COUNT(*) AS cnt, SUM(ORD_AMT) AS amt
  FROM T_ORD_BIG
 WHERE STLM_YN = 'N'
 GROUP BY ORD_DT
 ORDER BY ORD_DT;

실행계획 학습용 예시 · 행당 비용은 교재 04화 실측 비례 ​

xplan
[정산 확정 UPDATE · 세션 통계]
                           6월 1일 (5월분, 인덱스 전)   7월 1일 (6월분, 인덱스 뒤)
  바뀐 행                  28,303                        27,390
  경과                     1.3초                         3.4초
  redo size                9.6 MB                        24.3 MB
  db block changes         57,148                        168,931
  session logical reads    64,410                        251,302

[7월 1일 · 정산 확정 UPDATE]
----------------------------------------------------------------------------------------
| Id  | Operation          | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT   |           |      1 |        |      0 |00:00:03.38 |    251K |
|   1 |  UPDATE            | T_ORD_BIG |      1 |        |      0 |00:00:03.38 |    251K |
|*  2 |   TABLE ACCESS FULL| T_ORD_BIG |      1 |  27390 |  27390 |00:00:00.06 |    5826 |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(("STLM_YN"='N' AND "ORD_DT">=ADD_MONTHS(TRUNC(SYSDATE@!,'fmmm'),-1) AND
              "ORD_DT"<TRUNC(SYSDATE@!,'fmmm')))

(6월 1일에도 같은 모양이었다 · 1번 줄 Buffers 64K)

[「미정산 주문 현황」 7월 15일 · 인덱스 뒤]
-----------------------------------------------------------------------------------------------------
| Id  | Operation            | Name                 | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |                      |      1 |        |     15 |00:00:00.01 |      51 |
|   1 |  SORT GROUP BY NOSORT|                      |      1 |     31 |     15 |00:00:00.01 |      51 |
|*  2 |   INDEX RANGE SCAN   | IX_ORD_BIG_YN_DT_AMT |      1 |  14000 |  13695 |00:00:00.01 |      51 |
-----------------------------------------------------------------------------------------------------
   2 - access("STLM_YN"='N')
(인덱스 전에는 TABLE ACCESS FULL · Buffers 5,826)

문제 ​

  1. 바뀐 행 수는 비슷한데 redo와 블록 변경이 두 배 반 넘게 늘어난 이유를, 행 하나가 바뀔 때 새 인덱스에서 일어나는 일로 설명하시오. 행 하나당 숫자로도 쓰시오.
  2. 새 인덱스를 둘지 판단하시오. 마감 배치가 더 내는 비용과 화면이 아끼는 블록을 한 달 기준으로 비교하시오.
  3. 화면은 빠르게 두면서 마감 배치의 부담을 줄일 방법이 있다면 그 대가와 함께 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자