Skip to content
튜닝 연습PL/SQL과 사용자 정의 함수
PLSQL-001

계획은 51블록인데 월말 포인트 명세는 9초가 걸린다

기초PLSQL이커머스651 / 700

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

상황 ​

CRM팀의 「월말 포인트 명세」는 고객 5,230명 전부의 포인트 거래 합계를 한 장으로 뽑는다. 담당자가 물었다. "실행계획을 떠 보면 51블록이에요. 그런데 9초가 걸려요. 서버가 작아서 그런가요?" 합계는 개발팀이 만든 함수 fn_point_sum이 고객마다 구한다.

테이블과 인덱스 ​

  • customers: 5,230행 · 51블록 · PK(customer_id)
  • point_transactions: 45,000행 · 210블록 · PK(tx_id)만 있다 · customer_id 인덱스 없음 · 고객 한 명에 평균 약 9건 · 거래가 없는 고객이 있을 수 있다
  • 함수 안의 SELECT는 바깥 문장과 따로 파싱되고 실행되는 재귀 SQL이다(dep=1).

원본 SQL ​

sql
CREATE OR REPLACE FUNCTION fn_point_sum(p_cust NUMBER) RETURN NUMBER IS
  v_sum NUMBER;
BEGIN
  SELECT NVL(SUM(amount), 0) INTO v_sum
    FROM point_transactions
   WHERE customer_id = p_cust;
  RETURN v_sum;
END;
/

SELECT c.customer_id, c.name, c.grade,
       fn_point_sum(c.customer_id) AS point_sum
  FROM customers c
 ORDER BY c.customer_id;

실행계획 학습용 예시 ​

xplan
[바깥 문장 — DISPLAY_CURSOR (ALLSTATS LAST)]
------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |      1 |        |   5230 |00:00:09.12 |      51 |          |
|   1 |  SORT ORDER BY     |           |      1 |   5230 |   5230 |00:00:09.12 |      51 |  372K (0)|
|   2 |   TABLE ACCESS FULL| CUSTOMERS |      1 |   5230 |   5230 |00:00:00.01 |      51 |          |
------------------------------------------------------------------------------------------------------

[세션 통계 — 이 문장 한 번 (V$MYSTAT 증가분)]
  경과 시간                  9.1 초 (거의 전부 CPU)
  recursive calls            5,236
  execute count              5,232
  session logical reads      1,098,412

[V$SQL — 함수 안의 문장 (dep=1)]
  SQL_TEXT                                                              EXECUTIONS  FETCHES  ROWS_PROCESSED  BUFFER_GETS
  SELECT NVL(SUM(AMOUNT), 0) FROM POINT_TRANSACTIONS WHERE CUSTOMER_ID = :B1     5,230    5,230           5,230    1,098,300
  계획 : SORT AGGREGATE ← TABLE ACCESS FULL POINT_TRANSACTIONS · filter("CUSTOMER_ID"=:B1) · 한 번 210 블록

문제 ​

  1. 계획의 Buffers는 51인데 9초가 걸린 이유를 세션 통계와 V$SQL로 설명하시오.
  2. 결과가 바뀌지 않게 고치시오. 함수를 스칼라 서브쿼리로 감싸 캐시에 맡기는 안이 여기서 효과가 있는지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자