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

한 달 치 주문상품 내려받기가 사무실 PC에서는 1분 12초, IDC 안 PC에서는 6초다

기초SELECT버거577 / 700

상황 ​

버거 본사 영업관리팀은 매달 초 사내 리포트 도구(2015년, Java)로 지난달 주문상품을 엑셀로 내려받는다. 2013년 12월분 103,030행을 받는 데 1분 12초가 걸린다. 영업관리팀 사무실은 본사 IDC와 VPN으로 이어져 있다. 같은 도구를 IDC 안의 운영 PC에서 돌리면 6초 남짓이다. DBA는 이렇게 말했다. "DB 안에서 이 SQL이 쓴 시간은 0.4초뿐입니다. DB 문제는 아니에요."

테이블과 인덱스 ​

  • ORD_ITEM 주문상품: 2,472,717행 · 55,265블록 · ORD_ITEM_X01 (ORD_DT, ORD_HMS) · 날짜순으로 적재되어 있다 · 2013년 12월 103,030행(테이블 약 2,300블록, 인덱스 리프 약 380블록)
  • ITEM 상품: 92행 · 5블록 · PK (ITEM_ID)
  • 리포트 도구는 오라클 JDBC 드라이버를 쓰고 fetch size는 손대지 않았다(드라이버 기본값 10).
  • 사무실 PC와 DB 서버 사이의 왕복 시간(RTT)은 약 6.7ms, IDC 안 PC는 0.3ms다. 엑셀 시트를 쓰는 데는 어느 PC든 2.6초쯤 걸린다. 통계는 최신이다.

원본 SQL ​

sql
// MonthItemExport.java (2015) : 한 달 치 주문상품을 받아 엑셀 시트에 한 줄씩 쓴다
String sql = "SELECT i.ORD_DT, i.ORD_HMS, i.ORD_NO, i.ITEM_ID, m.ITEM_NM, i.ORD_ITEM_QTY "
           + "FROM ORD_ITEM i, ITEM m "
           + "WHERE i.ORD_DT BETWEEN ? AND ? "
           + "AND m.ITEM_ID = i.ITEM_ID";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, fromDt);                    // '20131201'
ps.setString(2, toDt);                      // '20131231'
ResultSet rs = ps.executeQuery();           // 한 번에 받는 행 수는 정하지 않았다
while (rs.next()) {
    sheet.addRow(rs.getString(1), rs.getString(2), rs.getLong(3), rs.getInt(4), rs.getString(5), rs.getInt(6));
}

실행계획 학습용 예시 · 페치 수의 셈은 교재 09화 실측과 같다 ​

xplan
[사무실 PC에서 한 번 · 세션 통계 증가분 (V$SESSTAT)]
  경과 시간 (도구 화면 기준)               72.4 초
  SQL*Net roundtrips to/from client       10,306
  user calls                              10,308
  session logical reads                   23,318
  bytes sent via SQL*Net to client        6.4 MB

[같은 세션 · V$SESSION_EVENT]
  SQL*Net message from client             10,305번 · 71.6 초 (평균 6.9 ms)
  SQL*Net message to client               10,306번 ·  0.01 초

[V$SQL]
  EXECUTIONS 1 · FETCHES 10,304 · ROWS_PROCESSED 103,030 · ELAPSED_TIME 0.41 초 · BUFFER_GETS 23,301

[DBMS_XPLAN.DISPLAY_CURSOR — 위 실행 (모든 페치를 합친 값)]
--------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name         | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |              |      1 |        |    103K|00:00:00.41 |   23301 |
|*  1 |  HASH JOIN                            |              |      1 |    102K|    103K|00:00:00.41 |   23301 |
|   2 |   TABLE ACCESS FULL                   | ITEM         |      1 |     92 |     92 |00:00:00.01 |       5 |
|   3 |   TABLE ACCESS BY INDEX ROWID BATCHED | ORD_ITEM     |      1 |    102K|    103K|00:00:00.33 |   23296 |
|*  4 |    INDEX RANGE SCAN                   | ORD_ITEM_X01 |      1 |    102K|    103K|00:00:00.08 |   10688 |
--------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("M"."ITEM_ID"="I"."ITEM_ID")
   4 - access("I"."ORD_DT">=:1 AND "I"."ORD_DT"<=:2)

문제 ​

  1. DB 안에서는 0.4초인 내려받기가 사무실에서는 1분 넘게 걸리는 이유를 위 통계로 설명하시오. IDC 안 PC에서는 빠른 이유도 쓰시오.
  2. 내려받기 프로그램을 고치고, 고친 뒤 왕복 수와 논리 읽기가 어떻게 바뀌는지 숫자로 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자