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

매장 관리 도구에서 하루 주문 목록을 열 때만 22,422블록을 읽는다

중급SELECT버거576 / 700

상황 ​

버거 본사 화면(Java)과 매장 관리 도구(C#, ODP.NET, 2022)가 같은 「매장 하루 주문 목록」 SQL을 쓴다. 본사 화면은 0.01초인데 매장 관리 도구는 0.4초쯤 걸린다. 점장들이 하루 1만 5천 번쯤 열어서 DB CPU의 상당 부분을 차지한다. 매장 관리 도구 개발자가 말했다. "다국어 때문에 문자열 파라미터는 모두 NVarchar2로 넘기라는 사내 규칙이 있어서요."

테이블과 인덱스 ​

  • ORD 주문: 1,000,000행 · 22,422블록 · 날짜순 적재 · ORD_DT VARCHAR2(8) 하루 약 1,370건 · ORD_HMS VARCHAR2(6) · SHOP_NO VARCHAR2 'SH0001' ~ 'SH0200'(매장마다 하루 약 7건)
  • 인덱스 ORD_PK (ORD_NO) · ORD_X01 (ORD_DT, ORD_HMS): CF 22,223
  • DB 문자셋 AL32UTF8 · 국가별 문자셋 AL16UTF16 · 통계 최신

원본 SQL ​

sql
-- 「매장 하루 주문 목록」 (두 프로그램이 같은 문자열)
SELECT ORD_NO, ORD_HMS
  FROM ORD
 WHERE ORD_DT = :d
   AND SHOP_NO = :shop
 ORDER BY ORD_HMS

// 매장 관리 도구 (C#, ODP.NET) : 사내 규칙대로 문자열은 NVarchar2
cmd.Parameters.Add(new OracleParameter("d", OracleDbType.NVarchar2) { Value = ordDt });
cmd.Parameters.Add(new OracleParameter("shop", OracleDbType.NVarchar2) { Value = shopNo });

실행계획 학습용 예시 ​

xplan
[V$SQL — 같은 SQL_ID의 자식 둘]
 CHILD  PLAN_HASH    EXECUTIONS  BUFFER_GETS/EXEC  MODULE
     0  3702938145        3,240                38  JDBC Thin Client
     1  1093856276       14,872            22,422  ShopManager.exe

[V$SQL_SHARED_CURSOR — Y인 칸]   child 1 : BIND_MISMATCH = Y
[V$SQL_BIND_CAPTURE]
 CHILD  NAME   DATATYPE_STRING
     0  :D     VARCHAR2(32)
     0  :SHOP  VARCHAR2(32)
     1  :D     NVARCHAR2(32)
     1  :SHOP  NVARCHAR2(32)

[자식 1 — 매장 관리 도구 한 번]
-------------------------------------------------------------------------------------------------------------
| Id  | Operation                  | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |         |      1 |        |      7 |00:00:00.38 |   22422 |          |
|   1 |  SORT ORDER BY             |         |      1 |    100 |      7 |00:00:00.38 |   22422 | 2048  (0)|
|*  2 |   TABLE ACCESS FULL        | ORD     |      1 |    100 |      7 |00:00:00.38 |   22422 |          |
-------------------------------------------------------------------------------------------------------------
   2 - filter((SYS_OP_C2C("ORD_DT")=:D AND SYS_OP_C2C("SHOP_NO")=:SHOP))

[자식 0 — 본사 화면 한 번]
-------------------------------------------------------------------------------------------------------------
| Id  | Operation                  | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |         |      1 |        |      7 |00:00:00.01 |      38 |          |
|*  1 |  TABLE ACCESS BY INDEX ROWID| ORD    |      1 |      7 |      7 |00:00:00.01 |      38 |          |
|*  2 |   INDEX RANGE SCAN         | ORD_X01 |      1 |   1370 |   1370 |00:00:00.01 |       7 |          |
-------------------------------------------------------------------------------------------------------------
   1 - filter("SHOP_NO"=:SHOP)
   2 - access("ORD_DT"=:D)

문제 ​

  1. 같은 SQL 텍스트에 자식이 둘이고, 매장 관리 도구의 자식만 테이블 전체를 읽는 이유를 위 자료로 설명하시오.
  2. 매장 관리 도구도 인덱스를 타도록 고치시오. 사내 규칙(다국어 때문에 NVarchar2)을 어떻게 고쳐 쓸지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자