Skip to content
튜닝 연습옵티마이저와 통계
OPT-040

시험 DB에서 빨랐던 계획을 프로파일로 옮겨 넣었는데 운영의 기간별 상품 매출은 그대로다

중급PLSQL한빛유통630 / 700

상황 ​

정산팀의 「기간별 상품 매출」은 처음에는 하루 범위로 쓰다가 올해부터 한 달 범위로 매일 50번쯤 돈다. 운영에서는 0.05초, 시험 DB(운영 복제)에서는 0.01초가 걸린다. 개발팀은 시험 DB의 계획 Outline으로 SQL 프로파일을 만들어 운영에 넣었다. 프로파일은 붙었는데 계획은 바뀌지 않았다. 작년 11월 업그레이드 주간에는 계획을 지키려고 OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES를 일주일 동안 켜 두었다. 시험 DB로는 데이터만 복제했고 SQL 관리 객체(베이스라인, 프로파일)는 옮기지 않았다.

테이블과 인덱스 ​

  • T_ORD: 100,000행 · 896블록 · ORD_DT DATE(시분초 있음) · 한 달 약 2,740건 · IX_T_ORD_DT (ORD_DT)(날짜순 적재라 한 달 치가 약 25블록에 모여 있다) · PK_T_ORD (ORD_SEQ)
  • T_ORD_DET: 200,000행 · 896블록 · PK_T_ORD_DET (ORD_SEQ, ORD_DET_NO) 높이 2 · 주문당 평균 2건
  • 통계 최신 · Tuning Pack 계약 있음 · 운영에는 이 SQL의 베이스라인이 있다(아래)

원본 SQL ​

sql
-- 개발팀이 운영에 넣은 것 : 시험 DB 계획의 Outline 을 프로파일로
BEGIN
  DBMS_SQLTUNE.IMPORT_SQL_PROFILE(
    sql_text    => :q_text,          -- 아래 SQL 의 글자 그대로
    profile     => SQLPROF_ATTR('BEGIN_OUTLINE_DATA',
                                'IGNORE_OPTIM_EMBEDDED_HINTS',
                                'ALL_ROWS',
                                'INDEX_RS_ASC(@"SEL$1" "O"@"SEL$1" ("T_ORD"."ORD_DT"))',
                                'FULL(@"SEL$1" "D"@"SEL$1")',
                                'LEADING(@"SEL$1" "O"@"SEL$1" "D"@"SEL$1")',
                                'USE_HASH(@"SEL$1" "D"@"SEL$1")',
                                'END_OUTLINE_DATA'),
    name        => 'PROF_ITM_PERIOD',
    force_match => TRUE);
END;
/

SELECT d.ITM_ID, SUM(d.ORD_QTY) AS qty, SUM(d.ORD_QTY * d.UNT_PRC) AS amt
  FROM T_ORD o, T_ORD_DET d
 WHERE o.ORD_DT >= :v_from
   AND o.ORD_DT <  :v_to
   AND d.ORD_SEQ = o.ORD_SEQ
 GROUP BY d.ITM_ID

실행계획 학습용 예시 ​

xplan
[운영 — 6월 한 달 · 프로파일을 넣은 뒤]
---------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name         | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |              |      1 |        |    100 |00:00:00.05 |    8273 |
|   1 |  HASH GROUP BY                        |              |      1 |    100 |    100 |00:00:00.05 |    8273 |
|*  2 |   FILTER                              |              |      1 |        |   5480 |00:00:00.05 |    8273 |
|   3 |    NESTED LOOPS                       |              |      1 |   5480 |   5480 |00:00:00.04 |    8273 |
|   4 |     NESTED LOOPS                      |              |      1 |   5480 |   5480 |00:00:00.02 |    5515 |
|   5 |      TABLE ACCESS BY INDEX ROWID BATCHED| T_ORD      |      1 |   2740 |   2740 |00:00:00.01 |      35 |
|*  6 |       INDEX RANGE SCAN                | IX_T_ORD_DT  |      1 |   2740 |   2740 |00:00:00.01 |      10 |
|*  7 |      INDEX RANGE SCAN                 | PK_T_ORD_DET |   2740 |      2 |   5480 |00:00:00.01 |    5480 |
|   8 |     TABLE ACCESS BY INDEX ROWID       | T_ORD_DET    |   5480 |      2 |   5480 |00:00:00.01 |    2758 |
---------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(:V_FROM<:V_TO)
   6 - access("O"."ORD_DT">=:V_FROM AND "O"."ORD_DT"<:V_TO)
   7 - access("D"."ORD_SEQ"="O"."ORD_SEQ")

Note
-----
   - SQL profile PROF_ITM_PERIOD used for this statement
   - SQL plan baseline SQL_PLAN_5qh8mbn2xw3k1c9a4e07d used for this statement

[DBA_SQL_PLAN_BASELINES — 이 SQL]
 SQL_HANDLE            PLAN_NAME                        ORIGIN        ENABLED  ACCEPTED  FIXED  CREATED     계획
 SQL_5c2f0a4e6b1d93a7  SQL_PLAN_5qh8mbn2xw3k1c9a4e07d   AUTO-CAPTURE  YES      YES       NO     2023-11-08  NL (PK_T_ORD_DET)
 SQL_5c2f0a4e6b1d93a7  SQL_PLAN_5qh8mbn2xw3k17b61f2a0   AUTO-CAPTURE  YES      NO        NO     2024-02-19  HASH (T_ORD_DET FULL)

[시험 DB — 같은 SQL · 베이스라인 없음]  HASH JOIN · T_ORD_DET FULL · Buffers 931

문제 ​

  1. 프로파일이 붙었는데도 계획이 NL인 이유를 Note의 두 줄과 베이스라인 목록으로 설명하시오. 베이스라인의 NL이 어떻게 받아들여진 계획이 되었는지도 쓰시오.
  2. 해시 계획이 운영에서 돌도록 바로잡고, 한 SQL에 장치를 둘 겹쳐 두지 않도록 정리하시오. 하루 범위로 부를 때는 어떻게 되는지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자