Skip to content
튜닝 연습파티션
PART-036

블랙프라이데이 오픈 20분 동안 상품 조회 기록 저장이 0.002초에서 0.09초로 늘어난다

중급DDL이커머스411 / 700

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

상황 ​

이커머스는 상품 상세 화면을 열 때마다 product_views_p에 한 행을 넣는다. 2025년 11월 28일 블랙프라이데이 오픈(10:00 ~ 10:20)에는 동시에 약 150세션이 조회 기록을 넣었고, 그동안 기록 저장이 평균 0.002초에서 0.09초로 늘어 상세 화면이 눈에 띄게 늦게 떴다. 지난해 같은 증상 때는 PK를 역방향 키 인덱스로 바꿔 인덱스 경합을 풀었다. DBA가 올해 AWR을 뽑아 왔다.

테이블과 인덱스 ​

  • product_views_p 상품 조회 기록(파티션 복사본, 문제 설정): product_views 100,000행을 PARTITION BY RANGE (view_date) INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'))로 나눠 CTAS로 지었다(STORAGE (INITIAL 64K)). 복사본에는 2020년 1월 ~ 2024년 12월의 60칸이 있고, 칸 하나는 약 1,670행, FULL로 약 7블록이다.
  • 운영에서는 같은 테이블에 달마다 칸이 이어서 생기고 있다. 지금 행이 쌓이는 칸은 2025년 11월 칸 SYS_P3307이다.
  • view_id는 시퀀스 번호(CACHE 1000)다. customer_id는 비회원 조회면 NULL이다(약 30%). 블랙프라이데이에는 조회가 특가 상품 몇 개에 몰린다.
  • 인덱스는 GLOBAL 역방향 키 인덱스 pk_product_views_p (view_id) 하나다.
  • 조회 기록을 읽는 SQL은 야간 배치의 「월 상품 조회 순위」뿐이다.

원본 SQL ​

sql
-- 상세 화면을 열 때마다
INSERT INTO product_views_p (view_id, product_id, customer_id, view_date)
VALUES (seq_view_id.NEXTVAL, :product_id, :customer_id, SYSDATE);

-- 「월 상품 조회 순위」 (야간 배치 · :v_mon = 그달 첫날)
SELECT product_id, COUNT(*) AS view_cnt
  FROM product_views_p
 WHERE view_date >= :v_mon
   AND view_date <  ADD_MONTHS(:v_mon, 1)
 GROUP BY product_id
 ORDER BY view_cnt DESC;

실행계획 학습용 예시 ​

xplan
[AWR · 2025-11-28 10:00 ~ 10:20 · 조회 기록 INSERT 186,400건 · 동시 약 150세션]
  EVENT                            WAITS     TIME(s)   AVG(ms)   %DB time
  buffer busy waits               98,310       688         7       38.6
  enq: HW - contention            21,740       413        19       23.2
  DB CPU                                       402                 22.6
  log file sync                  186,900       176         1        9.9
  enq: TX - index contention         212         2         9        0.1

  Segments by Buffer Busy Waits
  OBJECT                SUBOBJECT    TYPE               BUSY WAITS   %Total
  PRODUCT_VIEWS_P       SYS_P3307    TABLE PARTITION        96,120     97.8
  PK_PRODUCT_VIEWS_P                 INDEX                   1,410      1.4

  V$WAITSTAT (같은 구간 · 블록 종류)
  data block 63% · 1st level bmb 24% · segment header 11% · 기타 2%

[「월 상품 조회 순위」 · 2024년 11월 (복사본)]
----------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name            | Starts | E-Rows | A-Rows |      A-Time | Buffers | Pstart | Pstop |
----------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                 |      1 |        |    280 | 00:00:00.01 |       7 |        |       |
|   1 |  SORT ORDER BY               |                 |      1 |    280 |    280 | 00:00:00.01 |       7 |        |       |
|   2 |   HASH GROUP BY              |                 |      1 |    280 |    280 | 00:00:00.01 |       7 |        |       |
|*  3 |    FILTER                    |                 |      1 |        |   1668 | 00:00:00.01 |       7 |        |       |
|   4 |     PARTITION RANGE ITERATOR |                 |      1 |   1667 |   1668 | 00:00:00.01 |       7 |    KEY |   KEY |
|*  5 |      TABLE ACCESS FULL       | PRODUCT_VIEWS_P |      1 |   1667 |   1668 | 00:00:00.01 |       7 |    KEY |   KEY |
----------------------------------------------------------------------------------------------------------------------------

   3 - filter(:V_MON<ADD_MONTHS(:V_MON,1))
   5 - filter(("VIEW_DATE">=:V_MON AND "VIEW_DATE"<ADD_MONTHS(:V_MON,1)))

문제 ​

  1. AWR을 근거로 무엇이 무엇을 기다리는지 설명하고, 월별 칸으로 나눠 둔 것이 왜 이 경합을 나누지 못하는지 쓰시오.
  2. 월 단위 관리를 지키면서 경합을 나누도록 테이블을 고치는 DDL을 쓰고, 해시 키를 무엇으로 할지 이유와 함께 쓰시오.
  3. 고친 뒤의 대가를 「월 상품 조회 순위」와 세그먼트 수로 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자