Skip to content
튜닝 연습DML과 배치 튜닝
DML-051

고객센터 최근 배송 목록 2,000건이 3천 블록을 읽는데, 테이블은 기본값으로 만들었다고 한다

중급DDL이커머스476 / 700

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

상황 ​

이커머스의 배송(shipping) 행은 주문할 때 한 줄로 생기고, 배송이 진행되면서 같은 줄이 채워진다. 출고 때 송장 번호와 택배사, 출고 시각이 들어가고, 배송 완료 때 완료 시각과 수령 메모가 들어간다. 배송은 보통 사흘 안에 끝난다. 고객센터 「최근 배송 목록」(최근 주문 2,000건)이 0.3초를 넘는다는 개선 요청이 왔다. 테이블은 개발 초기에 PCTFREE를 적지 않고(기본값 10) 만들었다.

테이블과 인덱스 ​

  • shipping(문제 설정, 운영과 같은 순서로 쌓인 사본): 34,908행 · order_id와 1:1 · shipping_pk (order_id)뿐 · 이 문제에서는 tracking_no VARCHAR2(20), courier_cd VARCHAR2(3), shipped_at DATE, delivered_at DATE, recv_memo VARCHAR2(100) 칸이 더 있다. 지금 1,012블록이다.
  • 행 길이: 주문할 때 약 40바이트 → 출고 뒤 약 67바이트 → 배송 완료 뒤 약 111바이트(수령 메모 평균 40바이트)
  • 시험 DB에서 같은 데이터를 처음부터 최종 길이로 넣은 사본은 548블록이다.
  • 지금 있는 행의 97%는 배송이 끝났다. 하루 새 주문은 수십 건이다.

원본 SQL ​

sql
CREATE TABLE shipping (
  order_id     NUMBER        NOT NULL,
  address_id   NUMBER,
  status       VARCHAR2(20)  NOT NULL,
  tracking_no  VARCHAR2(20),
  courier_cd   VARCHAR2(3),
  shipped_at   DATE,
  delivered_at DATE,
  recv_memo    VARCHAR2(100),
  CONSTRAINT shipping_pk PRIMARY KEY (order_id)
);                                  -- PCTFREE 를 적지 않았다 (기본값 10)

-- 출고 (택배사 연동)
UPDATE shipping
   SET status = 'SHIPPED', tracking_no = :trk, courier_cd = :cc, shipped_at = SYSDATE
 WHERE order_id = :oid;

-- 배송 완료 (택배사 연동)
UPDATE shipping
   SET status = 'DELIVERED', delivered_at = :dt, recv_memo = :memo
 WHERE order_id = :oid;

-- 고객센터 「최근 배송 목록」
SELECT s.order_id, s.status, s.tracking_no, s.delivered_at
  FROM shipping s
 WHERE s.order_id > :last_id - 2000
 ORDER BY s.order_id DESC;

실행계획 학습용 예시 · 행 이전의 비율은 교재 05화 실측과 같은 모양 ​

xplan
[고객센터 「최근 배송 목록」 — 최근 주문 2,000건]
-------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |             |      1 |        |   2000 |00:00:00.31 |    2968 |
|   1 |  TABLE ACCESS BY INDEX ROWID | SHIPPING    |      1 |   2000 |   2000 |00:00:00.31 |    2968 |
|*  2 |   INDEX RANGE SCAN DESCENDING| SHIPPING_PK |      1 |   2000 |   2000 |00:00:00.01 |       6 |
-------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("S"."ORDER_ID">:LAST_ID-2000)

  table fetch continued row   1,512 (한 번 실행할 때)

[ANALYZE TABLE shipping COMPUTE STATISTICS]
  NUM_ROWS 34,908 · BLOCKS 1,012 · CHAIN_CNT 26,140 (74.9%) · AVG_ROW_LEN 113 · PCT_FREE 10

[같은 SQL — 처음부터 최종 길이로 넣은 사본 (548블록 · CHAIN_CNT 0)]
  Buffers 41 · table fetch continued row 0

문제 ​

  1. 목록 2,000건이 2,968블록을 읽는 이유를 ANALYZE 결과와 세션 통계로 설명하시오.
  2. 이 테이블의 행이 자라는 모양을 근거로 PCTFREE를 몇으로 할지 계산하시오.
  3. 이미 생긴 문제를 고치고 앞으로 새로 채울 블록에 맞는 설정을 하시오. 이미 배송이 끝난 행에도 같은 PCTFREE를 주면 어떻게 되는지 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자