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

한 번에 11블록 읽는 조회가 오전마다 공유 풀 경보를 울린다

기초SELECT유통546 / 700

상황 ​

본사 레거시 유통의 「영수증 조회 · 반품」 화면(2011년, Java)은 매장 6곳에서 영수증 번호로 판매 한 건을 연다. 오전에만 3만 번쯤 호출된다. DBA는 이렇게 말했다. "오전마다 공유 풀 경보와 latch: shared pool 대기가 뜹니다. CURSOR_SHARING을 FORCE로 바꾸면 한 번에 정리된다니 오늘 밤 반영하겠습니다." 같은 DB에는 다른 화면과 배치도 붙어 있다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · CF10_PK (SLE_NO) · SLE_NO VARCHAR2(15) '20230601-0123'(영수증 번호)
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) · 영수증 한 장에 상품 1~4개
  • CF01 상품: 109행 · 5블록 · CF01_PK (ITM_NO)
  • 한 번 실행하면 인덱스로 11블록 안팎을 읽는다. 통계는 최신이다.

원본 SQL ​

sql
// ReceiptDao.java (2011) — 영수증 번호를 SQL 문자열에 이어 붙인다
String sql = "SELECT h.SLE_NO, h.SLE_DT, h.SLE_HMS, d.ITM_NO, i.ITM_NM, d.SLE_QTY, d.SLE_UPRC "
           + "FROM CF10 h, CF11 d, CF01 i "
           + "WHERE h.SLE_NO = '" + sleNo + "' "
           + "AND d.SLE_NO = h.SLE_NO AND i.ITM_NO = d.ITM_NO";
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);

실행계획 학습용 예시 · 파싱 비율은 교재 11화 실측과 같은 수준 ​

xplan
[오전 3만 번 — 연습 DB 에서 화면 그대로 재현 · 한 세션 (세션 통계 증가분)]
  경과 시간                     27.8 초
  parse count (total)           30,214
  parse count (hard)            30,012
  parse time cpu                2,472 cs
  CPU used by this session      2,630 cs
  session logical reads         391,830   (한 번에 약 13 — 실행 11 + 하드 파싱의 딕셔너리 조회)

[V$SQL 에 보이는 것]
  SELECT h.SLE_NO, h.SLE_DT, … WHERE h.SLE_NO = '20231105-0412' AND d.SLE_NO = h.SLE_NO AND i.ITM_NO = d.ITM_NO
  SELECT h.SLE_NO, h.SLE_DT, … WHERE h.SLE_NO = '20231105-0413' AND d.SLE_NO = h.SLE_NO AND i.ITM_NO = d.ITM_NO
  …

[3만 번 직후 V$SQLAREA 를 FORCE_MATCHING_SIGNATURE 로 묶어 센 것]
  FMS                     PARENTS   EXECS    MB   SAMPLE
  11893327705119548241        386     386   7.9   SELECT h.SLE_NO, h.SLE_DT, h.SLE_HMS, d.ITM_NO, i.ITM_NM, d.SLE_Q…

[한 번 실행의 계획]
----------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |         |      1 |        |      2 |00:00:00.01 |      11 |
|   1 |  NESTED LOOPS                          |         |      1 |      2 |      2 |00:00:00.01 |      11 |
|   2 |   NESTED LOOPS                         |         |      1 |      2 |      2 |00:00:00.01 |       9 |
|   3 |    NESTED LOOPS                        |         |      1 |      2 |      2 |00:00:00.01 |       8 |
|   4 |     TABLE ACCESS BY INDEX ROWID        | CF10    |      1 |      1 |      1 |00:00:00.01 |       4 |
|*  5 |      INDEX UNIQUE SCAN                 | CF10_PK |      1 |      1 |      1 |00:00:00.01 |       3 |
|   6 |     TABLE ACCESS BY INDEX ROWID BATCHED| CF11    |      1 |      2 |      2 |00:00:00.01 |       4 |
|*  7 |      INDEX RANGE SCAN                  | CF11_PK |      1 |      2 |      2 |00:00:00.01 |       3 |
|*  8 |    INDEX UNIQUE SCAN                   | CF01_PK |      2 |      1 |      2 |00:00:00.01 |       1 |
|   9 |   TABLE ACCESS BY INDEX ROWID          | CF01    |      2 |      1 |      2 |00:00:00.01 |       2 |
----------------------------------------------------------------------------------------------------
   5 - access("H"."SLE_NO"='20231105-0412')
   7 - access("D"."SLE_NO"='20231105-0412')
   8 - access("I"."ITM_NO"="D"."ITM_NO")

문제 ​

  1. 한 번에 11블록을 읽는 조회가 CPU를 다 쓰는 이유를 위 통계로 설명하시오. V$SQL에 남은 문장은 386개뿐인데 '3만 개'라고 말할 수 있는 근거도 쓰시오.
  2. SQL과 화면 코드를 고치시오. DBA가 내놓은 CURSOR_SHARING = FORCE 안을 평가하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자