테마
박팀장이 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자)... WHERE CUS_ID = :B1)로 공유 풀에 들어간다.NLS_LENGTH_SEMANTICS = BYTE · CURSOR_SHARING = EXACT-- 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;
[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)
VARCHAR2(4000)으로 통일하는 것보다 나은 이유도 쓰시오.