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

MyBatis를 쓰는데 상품 검색이 들어올 때마다 하드 파싱이 일어난다

기초SELECT이커머스550 / 700

이 문제는 교재 18화 졸업 과제와 같은 이커머스 데이터베이스를 씁니다. 『튜너의 1년』을 순서대로 읽고 있다면 졸업 과제를 먼저 풀고 오세요. 아래 「테이블과 인덱스」 칸에 과제의 답 일부(제약 조건, 인덱스, 통계의 실제 상태)가 나옵니다.

상황 ​

이커머스의 상품 검색 API(Spring, MyBatis)는 카테고리와 검색어, 정렬 기준을 받아 상품을 찾는다. 세일 기간에는 초당 20번쯤 호출된다. DBA가 하드 파싱이 시간당 5만 번이라고 알렸지만, 개발팀의 답은 이랬다. "MyBatis를 쓰니까 바인드는 프레임워크가 알아서 합니다."

테이블과 인덱스 ​

  • products: 280행 · 8블록 · PK(product_id)만 있다 · category_id, name
  • categories: 53행 · 4블록 · 상품은 카테고리마다 5개 남짓이다
  • 정렬 기준은 이름순(name)과 최신순(product_id DESC) 둘이고, 화면은 고른 정렬 문구를 그대로 sort 파라미터로 넘긴다.
  • CURSOR_SHARING = EXACT

원본 SQL ​

sql
<!-- ProductMapper.xml -->
<select id="search" resultType="ProductDto">
  SELECT p.product_id, p.name, p.category_id
    FROM products p
   WHERE p.category_id = ${categoryId}
     AND p.name LIKE '%${keyword}%'
   ORDER BY p.${sort}
</select>

실행계획 학습용 예시 ​

xplan
[세일 첫날 20시대 한 시간 · V$SYSSTAT 증가분]
  execute count (이 API)        71,940
  parse count (hard)            51,320

[그 직후 V$SQLAREA · 이 API의 부모 커서 (일부)]
  SQL_ID          EXECUTIONS  SQL_TEXT
  9x2kq0w1m3c7a            1  SELECT p.product_id, p.name, p.category_id FROM products p WHERE p.category_id = 12 AND p.name LIKE '%텀블러%' ORDER BY p.name
  0f7hd2s8vb41n            1  SELECT p.product_id, p.name, p.category_id FROM products p WHERE p.category_id = 12 AND p.name LIKE '%보온병%' ORDER BY p.name
  3jr6a1y0q9p2t            2  SELECT p.product_id, p.name, p.category_id FROM products p WHERE p.category_id = 31 AND p.name LIKE '%우산%' ORDER BY p.product_id DESC
  …

[같은 시각 · FORCE_MATCHING_SIGNATURE로 묶은 이 API 무리]
  PARENTS   EXECS     MB
    3,410   4,020   49.6    (나머지는 이미 공유 풀에서 밀려났다)

[한 번 실행의 계획]
------------------------------------------------------------------------------------------
| Id  | Operation          | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |          |      1 |        |      2 |00:00:00.01 |       8 |
|   1 |  SORT ORDER BY     |          |      1 |      1 |      2 |00:00:00.01 |       8 |
|*  2 |   TABLE ACCESS FULL| PRODUCTS |      1 |      1 |      2 |00:00:00.01 |       8 |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(("P"."CATEGORY_ID"=12 AND "P"."NAME" LIKE '%텀블러%'))

문제 ​

  1. MyBatis를 쓰는데도 검색할 때마다 하드 파싱이 일어나는 이유를 매퍼와 V$SQLAREA로 설명하시오.
  2. 매퍼를 고치시오. 정렬 기준처럼 바인드 변수로 넘길 수 없는 부분은 어떻게 처리할지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자