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

글자 하나 다르지 않은 고객 조회 SQL에 자식 커서가 셋씩 붙는다

기초PLSQL한빛유통564 / 700

상황 ​

박팀장이 AWR 보고서의 「SQL ordered by Version Count」를 보냈다. 맨 위는 고객 한 명의 이름과 등급을 읽는 문장이다. 정산(PKG_STLM), 고객센터(PKG_CS), 포인트(PKG_PNT) 패키지가 같은 정적 SQL을 쓰고, 셋이 합쳐 하루 40만 번 호출한다. 통계를 모으는 밤이 지나면 아침마다 이 문장이 세 번씩 하드 파싱된다. 정산 스키마에는 모양이 같은 문장이 30개쯤 더 있다. DBA가 물었다. "바인드 변수도 쓰고 텍스트도 하나인데 왜 자식이 셋이죠?"

테이블과 인덱스 ​

  • M_CUS: 2,000행 · 30블록 · PK_M_CUS (CUS_ID) · CUS_ID VARCHAR2(40), 지금 값은 'CUS_000123' 모양(10자)
  • 세 패키지의 정적 SQL은 PL/SQL이 대문자와 바인드 이름을 정리해 똑같은 텍스트(... WHERE CUS_ID = :B1)로 공유 풀에 들어간다.
  • DB 문자셋 AL32UTF8 · NLS_LENGTH_SEMANTICS = BYTE · CURSOR_SHARING = EXACT

원본 SQL ​

sql
-- PKG_STLM (2019) : 정산
PROCEDURE get_cus(p_cus IN VARCHAR2, o_nm OUT VARCHAR2, o_gd OUT VARCHAR2) IS
  v_cus_id  VARCHAR2(10) := p_cus;
BEGIN
  SELECT CUS_NM, CUS_GD INTO o_nm, o_gd FROM M_CUS WHERE CUS_ID = v_cus_id;
END;

-- PKG_CS (2021) : 고객센터
PROCEDURE load_customer(p_cus IN VARCHAR2) IS
  v_cus_id  VARCHAR2(40) := p_cus;
  v_nm      VARCHAR2(40);
  v_gd      VARCHAR2(1);
BEGIN
  SELECT CUS_NM, CUS_GD INTO v_nm, v_gd FROM M_CUS WHERE CUS_ID = v_cus_id;
  g_screen.cus_nm := v_nm;  g_screen.cus_gd := v_gd;
END;

-- PKG_PNT (2023) : 포인트 — "넉넉하게"
FUNCTION grade_of(p_cus IN VARCHAR2) RETURN VARCHAR2 IS
  v_cus_id  VARCHAR2(4000) := p_cus;
  v_nm      VARCHAR2(4000);
  v_gd      VARCHAR2(4000);
BEGIN
  select cus_nm, cus_gd into v_nm, v_gd from m_cus where cus_id = v_cus_id;
  RETURN v_gd;
END;

실행계획 학습용 예시 · 바인드 그릇 크기는 교재 11화 실측과 같은 단위 ​

xplan
[V$SQLAREA — 10:00]
 SQL_ID          VERSION_COUNT  EXECUTIONS  PARSE_CALLS  LOADS  SHARABLE_MEM
 7bq2mfx0d3k1a               3     141,207           38      3        61,512
 SQL_TEXT : SELECT CUS_NM, CUS_GD FROM M_CUS WHERE CUS_ID = :B1

[V$SQL — 자식 셋]
 CHILD  IS_SHAREABLE  EXECUTIONS  FIRST_LOAD_TIME          처음 부른 곳   PLAN_HASH
     0  N                  9,820  2024-06-17/06:10:02      PKG_STLM       2518412337
     1  N                 31,455  2024-06-17/07:58:41      PKG_CS         2518412337
     2  Y                 99,932  2024-06-17/09:03:17      PKG_PNT        2518412337

[V$SQL_BIND_METADATA — 자식마다 :B1의 그릇]
 CHILD  BIND_NAME  DATATYPE  MAX_LENGTH
     0  B1                1          32
     1  B1                1         128
     2  B1                1        4000

[V$SQL_SHARED_CURSOR — Y인 칸만]
   child 1 : BIND_LENGTH_UPGRADEABLE = Y
   child 2 : BIND_LENGTH_UPGRADEABLE = Y

[한 번 실행 — 어느 자식이든 같은 계획]
------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |      1 |        |      1 |00:00:00.01 |       2 |
|   1 |  TABLE ACCESS BY INDEX ROWID| M_CUS    |      1 |      1 |      1 |00:00:00.01 |       2 |
|*  2 |   INDEX UNIQUE SCAN         | PK_M_CUS |      1 |      1 |      1 |00:00:00.01 |       1 |
------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("CUS_ID"=:B1)

문제 ​

  1. 텍스트가 하나인데 자식 커서가 셋인 이유를 위 표로 설명하시오. 자식이 하나 늘 때마다 무엇이 되풀이되는지도 쓰시오.
  2. 세 패키지의 선언을 고쳐 자식이 하나로 모이게 하시오. 그 방법이 크기를 VARCHAR2(4000)으로 통일하는 것보다 나은 이유도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자