Skip to content
튜닝 연습소트 튜닝과 페이징
SORT-027

총건수 쿼리를 목록에 합쳤더니 1쪽까지 느려졌다

중급SELECT이커머스332 / 700

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

상황 ​

운영팀의 「주문 관리」는 전체 주문을 최신순으로 한 쪽에 20건씩 보여 주고 위에 「총 n건」을 띄운다. 지난달까지는 목록과 건수를 두 SQL로 보냈는데, 이번 배포에서 개발자가 "DB 왕복을 한 번 줄이겠다"며 COUNT(*) OVER ()로 둘을 합쳤다. 그 뒤로 1쪽을 여는 데 걸리는 시간이 0.01초에서 0.06초로 늘고 DB 서버 CPU가 눈에 띄게 올랐다. 이 화면은 하루 약 20만 번 열린다(주문 알림을 받으면 운영자가 새로고침한다).

테이블과 인덱스 ​

  • orders: 34,908행 · 193블록 · PK(order_id) · order_date DATE(시분초 있음) · customer_id · status · total_amount
  • 인덱스: PK(order_id, 리프 약 70블록) · IX_ORDERS_DT (order_date, order_id)(지난달 이 화면용으로 만들었다). 주문은 날짜순으로 쌓여 있다.
  • 통계는 최신이다.

원본 SQL ​

sql
SELECT order_id, order_date, customer_id, status, total_amount,
       COUNT(*) OVER () AS total_cnt
  FROM orders
 ORDER BY order_date DESC, order_id DESC
 FETCH FIRST 20 ROWS ONLY

실행계획 학습용 예시 ​

xplan
[합친 뒤 · 지금]
-----------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name         | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |              |      1 |        |     20 |00:00:00.06 |     193 |
|*  1 |  VIEW                          |              |      1 |  34908 |     20 |00:00:00.06 |     193 |
|   2 |   WINDOW SORT                  |              |      1 |  34908 |  34908 |00:00:00.05 |     193 |
|   3 |    TABLE ACCESS FULL           | ORDERS       |      1 |  34908 |  34908 |00:00:00.01 |     193 |
-----------------------------------------------------------------------------------------------------------
   1 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=20)
   2번 줄 WINDOW SORT 의 Used-Mem : 2381K (0)

[합치기 전 · 지난달 · 목록]
-----------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name         | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |              |      1 |        |     20 |00:00:00.01 |       4 |
|*  1 |  VIEW                          |              |      1 |     20 |     20 |00:00:00.01 |       4 |
|*  2 |   WINDOW NOSORT STOPKEY        |              |      1 |     20 |     20 |00:00:00.01 |       4 |
|   3 |    TABLE ACCESS BY INDEX ROWID | ORDERS       |      1 |  34908 |     20 |00:00:00.01 |       4 |
|   4 |     INDEX FULL SCAN DESCENDING | IX_ORDERS_DT |      1 |     20 |     20 |00:00:00.01 |       2 |
-----------------------------------------------------------------------------------------------------------
   1 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=20)
   2 - filter(ROW_NUMBER() OVER ( ORDER BY INTERNAL_FUNCTION("ORDER_DATE") DESC ,
              INTERNAL_FUNCTION("ORDER_ID") DESC )<=20)

[합치기 전 · 지난달 · 건수]   SELECT COUNT(*) FROM orders
  SORT AGGREGATE ← INDEX FAST FULL SCAN (PK) · Buffers 75

문제 ​

  1. 합친 SQL의 2번 줄이 34,908행을 정렬하는 까닭을 COUNT(*) OVER ()와 부분범위 처리로 설명하시오.
  2. 합치기 전과 뒤의 일량을 하루 단위로 비교하고, "왕복을 한 번 줄였다"는 판단이 왜 손해였는지 쓰시오.
  3. 목록 SQL을 고치고, 「총 n건」을 어떻게 다룰지 대안을 두 가지 이상 비교하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자