승적이익강 (勝敵而益强)

튜닝예제(1) 본문

Database

튜닝예제(1)

그녕이 2008. 9. 19. 12:21

첫번째로 인덱스 사용에 관한 사항입니다.

다음은 KBORA의 대표적인 튜닝사례입니다.

아래 SQL은 하나의 테이블을 access합니다.

win_pay_info : 약 2100만건
- 인덱스

1. win_pay_info_date: period_flag+pay_date
2. win_pay_info_date_draw: pay_date+sale_draw_no+period_flag
3. win_pay_info_draw: period_flag+sale_draw_no
4. win_pay_info_draw_date: sale_draw_no+pay_date+period_flag
- 컬럼

period_flag: varchar2(2)
pay_date: varchar2(8)
sale_draw_no: number(8)
        
********************************************************************************
/* 튜닝전 */

SELECT sysdate, sysdate, sysdate, state_name,

             to_char(nvl((SUM(win_game_1)), 0) ,'999,999,999'),
             to_char(nvl(SUM(win_amount_1), 0),'999,999,999,999,999,999'),

             to_char(nvl((SUM(win_game_2)), 0) ,'999,999,999'),
             to_char(nvl(SUM(win_amount_2), 0),'999,999,999,999,999,999'),

             to_char(nvl((SUM(win_game_3)), 0) ,'999,999,999'),
             to_char(nvl(SUM(win_amount_3), 0),'999,999,999,999,999,999'),

             to_char(nvl((SUM(win_game_4)), 0) ,'999,999,999'),
             to_char(nvl(SUM(win_amount_4), 0),'999,999,999,999,999,999'),

             to_char(nvl((SUM(win_game_5)), 0) ,'999,999,999'),
             to_char(nvl(SUM(win_amount_5), 0),'999,999,999,999,999,999'),

             to_char(nvl((SUM(pay_tickets)), 0) ,'999,999,999')
FROM    win_pay_info
WHERE  period_flag='R' 
and       to_date('2006-03-01', 'YYYY-MM-DD') <= to_date(pay_date, 'YYYYMMDD')

and       to_date(pay_date,'YYYYMMDD') <= to_date('2006-03-21', 'YYYY-MM-DD')
and       retailer_id like '11100003%'
Group by state_name

call         count      cpu      elapsed       disk      query     current     rows
----------------------------------------------------------------------
Parse       2           0.01          0.02            0          0             0           0
Execute    2           0.00          0.00            0          0             0           0
Fetch       4        322.71        348.11     230104     242066          0           2
----------------------------------------------------------------------
total         8        322.72        348.13     230104     242066          0           2

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 21

Rows     Row Source Operation
----------------------------------------------------------------------
      1      SORT GROUP BY
     46     TABLE ACCESS BY INDEX ROWID WIN_PAY_INFO
513779    INDEX RANGE SCAN (object id 4028)

********************************************************************************

설명.

where에 있는 pay_date는 날짜를 나타내고 속성는 varchar2입니다. 그런데 이걸 굳이 date로 변경을 해서 date와 비교를 하고 있습니다. to_date('2006-03-01', 'YYYY-MM-DD') <= to_date(pay_date, 'YYYYMMDD') 와 같이 말입니다.
이렇게 되면 첫번째 인덱스인 period_flag+pay_date를 사용할 수 없게 됩니다. 그럼. 위에 plan에서는 513779 INDEX RANGE SCAN (object id 4028) 와 같이 나와 있습니다. 개발자가 생각하기에 인덱스를 사용하고 있지 않나 생각을 할 수 있습니다.
맞습니다. 인덱스를 사용하고는 있습니다. 하지만 period_flag='R'인 모든 날짜에 대해 range scan을 하고 있습니다. 우리가 필요한 '2006-03-01'부터 '2006-03-21'만 뽑아 먹는 것이 아닙니다.
이유는 바로 to_date와 같은 컬럼 suppressing이 발생해서 입니다. 발생하지 않게 다음과 같이 쓰면 됩니다.
'20060301' <= pay_date

********************************************************************************
/* 튜닝후 */

SELECT sysdate, sysdate, sysdate, state_name,

             to_char(nvl((SUM(win_game_1)), 0) ,'999,999,999'),
             to_char(nvl(SUM(win_amount_1), 0),'999,999,999,999,999,999'),

             to_char(nvl((SUM(win_game_2)), 0) ,'999,999,999'),
             to_char(nvl(SUM(win_amount_2), 0),'999,999,999,999,999,999'),

             to_char(nvl((SUM(win_game_3)), 0) ,'999,999,999'),
             to_char(nvl(SUM(win_amount_3), 0),'999,999,999,999,999,999'),

             to_char(nvl((SUM(win_game_4)), 0) ,'999,999,999'),
             to_char(nvl(SUM(win_amount_4), 0),'999,999,999,999,999,999'),

             to_char(nvl((SUM(win_game_5)), 0) ,'999,999,999'),
             to_char(nvl(SUM(win_amount_5), 0),'999,999,999,999,999,999'),

             to_char(nvl((SUM(pay_tickets)), 0) ,'999,999,999')
FROM    win_pay_info
WHERE  period_flag='R' 
and       '20060301' <= pay_date 
and       pay_date <= '20060321' 
and       retailer_id like '11100003%'
Group by state_name

call         count      cpu      elapsed       disk      query     current     rows
----------------------------------------------------------------------
Parse          1        0.01       0.01              0            0           0           0
Execute      1        0.01        0.00              0            0           0           0
Fetch          2        1.61       1.63              3         8672          0           1
----------------------------------------------------------------------
total            4        1.63       1.64              3         8672          0           1

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 21

Rows     Row Source Operation
----------------------------------------------------------------------

       1     SORT GROUP BY
     46     TABLE ACCESS BY INDEX ROWID WIN_PAY_INFO
513779    INDEX RANGE SCAN (object id 4028)

********************************************************************************

결과 : 348.13초 -> 1.64초

********************************************************************************

개선점.

인덱스를 보시면 아시겠지만 같은 컬럼중복이 많이 됐다라는 느낌을 받으실겁니다. 모든 프로그램을 확인해 봤는데 이중 사용하지 않은 인덱스도 존재하는 것 같습니다.
위 sql은 retailer_id라는 똑똑한 조건의 컬럼이 있는데, 이것을 사용할 수 있는 인덱스가 없는 것이 아쉽습니다. 현재 잘 운영되고 있는 시스템이라 물리적인 변경, 즉 인덱스 같은 것은 수정을 하지 않았습니다.

********************************************************************************

'Database' 카테고리의 다른 글

튜닝예제(3)  (0) 2008.09.19
튜닝예제(2)  (0) 2008.09.19
lock 걸린 자료 찾기 및 삭제  (0) 2008.09.19
집합의 최적화 (2)  (0) 2008.09.19
집합의 최적화 (1)  (0) 2008.09.19
Comments