오라클/튜닝

테이블 random 액세스 쿼리 튜닝 예시

dbavayne 2023. 2. 13. 15:14
테스트 데이터 환경

아래 스크립트 참조 

인덱스 : 주문지역코드, 주문일자, 주문금액
* 튜닝 포인트 : 인덱스 수정 불가 하며 SQL은 수정 가능

* 전체데이터 : 1,000만건
* 주문일자     : 2019.05.24 - 2019.08.31 (100일)  1일당 10만건
* 주문지역코드 : 1, 2, 3, 4, 5 (고르게 분포)
* 주문금액     : 1만원 - 100만원 (고르게 분포) 

ALTER SESSION SET WORKAREA_SIZE_POLICY = MANUAL;
ALTER SESSION SET SORT_AREA_SIZE = 2000000000;

drop table test.t_order_km;

--테이블생성
create table test.t_order_km AS
SELECT A.custno, orderAmt, orderLoc, B.orderDt
     , 'ASDFFDSAASDFFDSAASDFFDSAASDFFDSAASDFFDSA' C1
     , 'ASDFFDSAASDFFDSAASDFFDSAASDFFDSAASDFFDSA' C2
     , 'ASDFFDSAASDFFDSAASDFFDSAASDFFDSAASDFFDSA' C3
     , 'ASDFFDSAASDFFDSAASDFFDSAASDFFDSAASDFFDSA' C4
     , 'ASDFFDSAASDFFDSAASDFFDSAASDFFDSAASDFFDSA' C5
     , 'ASDFFDSAASDFFDSAASDFFDSAASDFFDSAASDFFDSA' C6
FROM   (select  'C' || lpad(trim(to_char(rownum)), 4, '0')             custno
              , round(dbms_random.value(10000, 100000))                orderAmt
              , lpad(to_char(round(dbms_random.value(1, 5))), 2, '0') orderLoc
        from dual connect by level <= 1000
        ) A,
       (SELECT  TO_CHAR(TO_DATE('20190901', 'YYYYMMDD') - ROWNUM, 'YYYYMMDD') orderDt
        FROM DUAL CONNECT BY LEVEL <= 100
       ) B,
       (SELECT ROWNUM R_NUM 
        FROM   DUAL
        CONNECT BY LEVEL <= 100
       )
ORDER BY DBMS_RANDOM.RANDOM();

--시노님생성
create public synonym t_order_km for test.t_order_km;

--인덱스생성
CREATE INDEX test.IX_t_order_km_01 ON test.t_order_km(orderLoc, orderDt, orderAmt);

--통계정보수집
EXECUTE DBMS_STATS.GATHER_TABLE_STATS('test', 't_order_km');

--sort_area 조정
ALTER SESSION SET WORKAREA_SIZE_POLICY = AUTO;

 

[통계데이터 확인]

- 리프블록은 39202 와 depth는 2레벨

SELECT TABLE_NAME, INDEX_NAME, STATUS, NUM_ROWS, LEAF_BLOCKS, BLEVEL FROM
DBA_INDEXES
WHERE INDEX_NAME ='IX_T_ORDER_KM_01';

TABLE_NAME       INDEX_NAME               STATUS     NUM_ROWS LEAF_BLOCKS     BLEVEL
---------------- ------------------------ -------- ---------- ----------- ----------
T_ORDER_KM       IX_T_ORDER_KM_01         VALID      10000000       39202          2


select i.index_name, t.blocks, i.num_rows, i.clustering_factor
from dba_tables t, dba_indexes i
where t.table_name = 'T_ORDER_KM'
and i.table_name = t.table_name;

index_name         blocks        num_rows     clustering_factor
---------------- ------------- -----------    -----------------
IX_T_ORDER_KM_01   385488         10000000      9998767


* CF값이 블록에 가까울수록 좋고 num_rows에 가까울수록 나쁘다.

 

[AS-IS 쿼리]

- 드라이빙 조건 이후에 체크 조건이여서 filter 테이블 랜덤 액세스 발생

SELECT  /*+ no_index_ss (a IX_t_order_km_01) */
custno, orderAmt,
orderLoc, orderDt, C1, C2, C3
FROM t_order_km  A
WHERE orderAmt BETWEEN 80000 AND 81000  -- 체크조건
AND  orderLoc BETWEEN '01' AND '03'     -- 드라이빙 조건
AND orderDt     = '20190710';  -- 체크조건

Elapsed: 00:00:00.97

Execution Plan
----------------------------------------------------------
Plan hash value: 3045225975

--------------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                  |   919 |   131K| 28456   (1)| 00:00:02 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T_ORDER_KM       |   919 |   131K| 28456   (1)| 00:00:02 |
|*  2 |   INDEX RANGE SCAN                  | IX_T_ORDER_KM_01 |   919 |       | 27535   (1)| 00:00:02 |
--------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("ORDERLOC">='01' AND "ORDERDT"='20190710' AND "ORDERAMT">=80000 AND
              "ORDERLOC"<='03' AND "ORDERAMT"<=81000)
       filter("ORDERDT"='20190710' AND "ORDERAMT">=80000 AND "ORDERAMT"<=81000)

 

[TO-BE 쿼리]

- in-list 형식으로 변경하여 주문일자도 드라이빙조건으로 가도록 튜닝

SELECT  custno, orderAmt, orderLoc, orderDt, C1, C2, C3
FROM t_order_km  A
WHERE orderAmt     BETWEEN 80000 AND 81000 --드라이빙조건
AND  orderLoc in ('01','02','03')   --드라이빙조건 
AND  orderDt     = '20190710'    --드라이빙조건


Elapsed: 00:00:00.06

Execution Plan
----------------------------------------------------------
Plan hash value: 3334870416

---------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                  |   788 |   112K|   796   (0)| 00:00:01 |
|   1 |  INLIST ITERATOR                     |                  |       |       |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| T_ORDER_KM       |   788 |   112K|   796   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | IX_T_ORDER_KM_01 |   788 |       |     8   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access(("ORDERLOC"='01' OR "ORDERLOC"='02' OR "ORDERLOC"='03') AND "ORDERDT"='20190710'
              AND "ORDERAMT">=80000 AND "ORDERAMT"<=81000)