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

바인드 변수로 고쳤다는 주문 일괄 조회가 여전히 하드 파싱 상위에 있다

중급SELECT한빛유통554 / 700

상황 ​

고객센터의 「주문 일괄 조회」 화면은 상담원이 엑셀에서 주문번호를 1~1,000개 붙여 넣으면 주문 상태와 금액을 보여 준다. 지난달 개발팀은 주문번호를 문자열로 이어 붙이던 코드를 바인드 변수로 고쳤다. 그런데 DBA가 뽑은 하드 파싱 상위 목록에는 여전히 이 화면이 맨 위에 있다. 개발팀은 이렇게 말한다. "이제 값은 전부 ?로 넘기는데요."

테이블과 인덱스 ​

  • T_ORD: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ) · ORD_SEQ는 NUMBER
  • 하루 약 2,400번 호출된다. 붙여 넣는 개수는 1~1,000개로 제각각이고, 대부분은 수십 개 이하다.
  • 1,001개 이상을 붙이면 ORA-01795가 나서 화면이 1,000개로 막아 두었다.
  • CURSOR_SHARING = EXACT

원본 SQL ​

sql
// BulkOrderDao.java (지난달 수정) : 리터럴 대신 ? 를 개수만큼 만든다
String marks = String.join(",", Collections.nCopies(ids.size(), "?"));
String sql = "SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_ST, ORD_AMT FROM T_ORD WHERE ORD_SEQ IN (" + marks + ")";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
    for (int i = 0; i < ids.size(); i++) ps.setLong(i + 1, ids.get(i));
    try (ResultSet rs = ps.executeQuery()) { /* 화면에 뿌린다 */ }
}

실행계획 학습용 예시 ​

xplan
[V$SQLAREA · 금요일 17:00 · 이 화면의 부모 커서 (월요일부터 누적)]
  바인드 수   EXECUTIONS   LOADS   SHARABLE_MEM   FORCE_MATCHING_SIGNATURE
          3          214       1          18KB    7781020440133318265
          7          188       1          21KB    1290047755316282003
         12          121       1          26KB    16610993057461230118
        250            3       2         117KB    4436159902384511870
       1000            1       1         412KB    9022157316650884471
  …
  부모 734개 · 실행 9,860 · 합계 61.3MB · FORCE_MATCHING_SIGNATURE 734가지

[바인드 수별 하드 파싱 한 번의 비용 (10046 → TKPROF의 Parse 줄 cpu)]
    10개     0.002초
   100개     0.011초
  1000개     0.094초

[한 번 실행의 계획 — 주문번호 1,000개]
-----------------------------------------------------------------------------------------------
| Id  | Operation                    | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |          |      1 |        |   1000 |00:00:00.01 |    3012 |
|   1 |  INLIST ITERATOR             |          |      1 |        |   1000 |00:00:00.01 |    3012 |
|   2 |   TABLE ACCESS BY INDEX ROWID| T_ORD    |   1000 |   1000 |   1000 |00:00:00.01 |    3012 |
|*  3 |    INDEX UNIQUE SCAN         | PK_T_ORD |   1000 |   1000 |   1000 |00:00:00.01 |    2008 |
-----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access(("ORD_SEQ"=:1 OR "ORD_SEQ"=:2 OR "ORD_SEQ"=:3 OR … OR "ORD_SEQ"=:1000))

문제 ​

  1. 값을 모두 바인드 변수로 넘기는데도 부모 커서가 734개 생긴 이유를 설명하시오. CURSOR_SHARING = FORCE로는 이 문장들이 묶이지 않는 이유도 쓰시오.
  2. 붙여 넣는 개수와 상관없이 문장이 하나가 되도록 고치시오. 고친 방법이 결과와 실행계획에 주는 영향도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자