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

인프라팀의 통보: 「CURSOR_SHARING을 FORCE로, 오늘 밤 반영합니다」

실전SELECT한빛유통548 / 700

상황 ​

공유 풀 경보가 지난주 목요일부터 매일 오전에 울린다. 인프라팀은 이렇게 통보했다. "모양이 거의 같은 SQL이 끝도 없이 들어왔다 나간다. CURSOR_SHARING을 FORCE로 바꾸면 한 번에 정리된다. 오늘 밤 반영하겠다." 박팀장이 덧붙였다. "튜닝 쪽 의견을 오후 4시까지." 경보가 울린 시각에 공유 풀 맨 위를 차지한 문장은 정산 목록(Q4)이다. 리비전 파일에는 바인드로 적었는데, 개발팀은 포스트잇 값을 문자열로 이어 붙여 반영했다. 같은 공유 풀에는 2019년에 강도현이 탭마다 상수로 쓴 정산 상태 확인 문장 셋도 있다.

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · ORD_ST(COMPLETE 90% · PENDING 8% · CANCEL 2%) · FREQUENCY 히스토그램
  • 인덱스 IX_ORD_BIG_DT (ORD_DT) · IX_ORD_BIG_DT_AMT (ORD_DT, ORD_AMT) · IX_ORD_BIG_DT_SEQ (ORD_DT, ORD_SEQ)
  • 정산 목록(Q4): 키 기반 페이징 · 한 번에 15블록(09화) · '다음'을 누를 때마다 포스트잇(날짜, 번호)이 바뀐다
  • 정산 상태 확인: 탭 셋 · 탭마다 최근 1,000건 · 세 문장은 상태값만 다르다
  • CURSOR_SHARING = EXACT · 바인드 피킹 켬 · statistics_level = TYPICAL

원본 SQL ​

sql
-- 인프라팀 변경 요청 (오늘 밤 22:00)
ALTER SYSTEM SET CURSOR_SHARING = FORCE SCOPE = BOTH;

-- ① 정산 목록 '다음' — 개발팀 구현 (Java) : 포스트잇 값을 문자열로 이어 붙인다
--    String sql = "... WHERE ORD_DT <= DATE '" + postitDate + "' AND (ORD_DT < DATE '" + postitDate
--               + "' OR ORD_SEQ <= " + postitSeq + ") ORDER BY ORD_DT DESC, ORD_SEQ DESC) WHERE ROWNUM <= 11";
--    → 공유 풀에 들어오는 모양 (클릭마다 날짜 · 번호가 다르다)
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_DT <= DATE '2024-12-30'
           AND (ORD_DT < DATE '2024-12-30' OR ORD_SEQ <= 988784)
         ORDER BY ORD_DT DESC, ORD_SEQ DESC)
 WHERE ROWNUM <= 11;

-- ② 정산 상태 확인 — 강도현 2019 : 탭마다 문장 따로 (완료 · 미결 · 취소) · 상태값은 상수
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 = 'CANCEL' ORDER BY ORD_DT DESC, ORD_SEQ DESC)
 WHERE ROWNUM <= 1000;

실행계획 실측 · 교재 11화 ​

xplan
[① 3만 번 '다음' — 개발팀 구현 그대로 · 연습 DB 재현 · 세션 통계]
  걸린 시간                   36.19 초
  parse count (total)         30,802
  parse count (hard)          30,431
  parse time cpu               3,466   (1/100 초)
  CPU used by this session     3,617
  session logical reads      522,931   (한 번 약 17 블록)

[V$SQLAREA — FORCE_MATCHING_SIGNATURE 로 묶은 맨 위 무리 (위 직후)]
  PARENTS   EXECS    MB   SQL_TEXT (앞부분)
      308     308   9.4   SELECT ... WHERE ORD_DT <= DATE '2024-12-30' AND (ORD_DT < DATE '2024-12-30' OR ORD_SEQ <= 988784) ...
  → 부모 308 개가 지문 하나 · 3만 개가 '지나갔다' · 미리 올려 둔 이웃 Q1 rev03 은 V$SQL 에서 사라졌다

[인프라팀 제안을 내 세션에만 — ALTER SESSION SET CURSOR_SHARING = FORCE]
  Q1 · Q2 · Q3 · Q4         계획 해시 그대로 · Buffers 91 · 5,868 · 839 · 15
  3만 번 '다음'             1.45 초 · parse count (hard) 329 · 부모 1 · VERSION_COUNT 329 (지나간 날짜 329일)
  V$SQL_SHARED_CURSOR       새 자식마다 HASH_MATCH_FAILED = Y
  (같은 3만 번을 바인드로 · EXACT)   0.67 초 · parse count (hard) 1

[② 정산 상태 확인 — 탭마다 상수 (EXACT · 지금)]
  탭     문장                  계획                                              PLAN_HASH   Buffers
  완료   ORD_ST = 'COMPLETE'   INDEX FULL SCAN DESCENDING (IX_ORD_BIG_DT_SEQ)    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
  (FORCE 아래의 상태 탭은 아직 재지 않았다)

문제 ​

  1. FORCE를 켜면 무엇이 좋아지고 무엇이 망가지는지 위 자료를 근거로 문장별(정산 목록, 정산 상태 확인)로 예측하시오.
  2. 인스턴스 스위치 대신 무엇을 재고 무엇을 고칠지 SQL과 함께 제안하시오.
  3. 개발팀 수정이 반영되기 전까지 꼭 무언가를 켜야 한다면 그 범위와 안전장치를 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자