Skip to content
튜닝 연습파싱과 바인드 변수
PARSE-002

어떤 날은 취소 탭이, 어떤 날은 완료 탭이 느리다

중급SELECT한빛유통547 / 700

상황 ​

정산 상태 확인 화면은 탭(완료, 미결, 취소)마다 최근 1,000건을 보여 준다. 2019년부터 탭마다 상태값을 상수로 쓴 문장이 셋 있었는데, 지난주 개발팀이 「SQL 코딩 가이드 1조: 바인드로 넘긴다」를 보고 :st 바인드 하나로 합쳤다. 조민우가 말했다. "월요일엔 취소 탭이 한참 걸렸는데, 오늘은 취소는 괜찮고 완료 탭이 느려요." 공유 풀에 있는 이 문장의 자식 커서는 하나다.

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · ORD_ST COMPLETE 900,000 · PENDING 80,000 · CANCEL 20,000 · FREQUENCY 히스토그램(10화 정책서 1호)
  • 미결과 취소는 1,095일 중 219일(닷새에 하루)에만 있다. 그래서 최근 순으로 내려가며 취소 1,000건을 채우려면 약 5만 행을 지나야 한다.
  • 인덱스 IX_ORD_BIG_DT_SEQ (ORD_DT, ORD_SEQ): 리프 약 3,000블록 · ORD_ST 인덱스는 없다
  • CURSOR_SHARING = EXACT · 바인드 피킹 켬 · statistics_level = TYPICAL(운영과 같다)

원본 SQL ​

sql
-- 개발팀 반영본 (지난주) — 탭이 고른 상태값을 바인드로 넘긴다
SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_AMT
  FROM (SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_AMT
          FROM T_ORD_BIG
         WHERE ORD_ST = :st
         ORDER BY ORD_DT DESC, ORD_SEQ DESC)
 WHERE ROWNUM <= 1000

실행계획 실측 · 교재 11화(줄별 값은 예시) ​

xplan
[V$SQL — 월요일 · 그날 첫 손님이 완료 탭 · 한 번 실행의 Buffers = BUFFER_GETS 증가분]
 순서  :st        CHILD  PLAN_HASH    BUFFERS
  1    COMPLETE      0   369784633      1,034
  2    CANCEL        0   369784633     50,347
  3    CANCEL        0   369784633     50,347
  4    CANCEL        0   369784633     50,347
  5    CANCEL        0   369784633     50,347

 CHILD  EXECUTIONS  IS_BIND_SENSITIVE  IS_BIND_AWARE  V$SQL_CS_HISTOGRAM (BUCKET:COUNT)
     0           5  Y                  N              0:0  1:5  2:0

[자식 0 의 계획 — 취소 탭 한 번 · FORMAT 'ALLSTATS LAST +PEEKED_BINDS']
Plan hash value: 369784633

------------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                   |      1 |        |   1000 |00:00:00.04 |   50347 |
|*  1 |  COUNT STOPKEY                 |                   |      1 |        |   1000 |00:00:00.04 |   50347 |
|   2 |   VIEW                         |                   |      1 |   1000 |   1000 |00:00:00.04 |   50347 |
|*  3 |    TABLE ACCESS BY INDEX ROWID | T_ORD_BIG         |      1 |    900K|   1000 |00:00:00.04 |   50347 |
|   4 |     INDEX FULL SCAN DESCENDING | IX_ORD_BIG_DT_SEQ |      1 |   1111 |  50191 |00:00:00.01 |     156 |
------------------------------------------------------------------------------------------------------------

Peeked Binds (identified by position):
--------------------------------------
   1 - :ST (VARCHAR2(32), CSID=873): 'COMPLETE'

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(ROWNUM<=1000)
   3 - filter("ORD_ST"=:ST)

[지난주까지 — 탭마다 상수 (문장 셋 · 각자의 계획)]
 완료  ORD_ST = 'COMPLETE'  INDEX FULL SCAN DESCENDING                  369784633   1,016
 미결  ORD_ST = 'PENDING'   TABLE ACCESS FULL + SORT ORDER BY STOPKEY   654798247   5,826
 취소  ORD_ST = 'CANCEL'    TABLE ACCESS FULL + SORT ORDER BY STOPKEY   654798247   5,826

문제 ​

  1. 같은 문장이 날마다 다른 탭에서 느린 이유를 Peeked Binds와 V$SQL 표로 설명하시오. 적응적 커서 공유(ACS)가 이 문제를 고치지 못한 이유도 쓰시오.
  2. 어느 탭이 먼저 열리든 탭마다 제 계획을 타도록 고치시오. '바인드로 넘긴다'는 가이드와 어떻게 맞추는지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자