2013년 8월 13일 화요일

[오라클자바community]Optimizer Mode를 변경하는 힌트 (RULE) , SQL강좌, 교육

Optimizer Mode를 변경하는 힌트 (RULE)

구로디지털 오엔제이프로그래밍실무교육센터
www.onjprogramming.co.kr
 
 
규칙 기반 옵티마이저(Rule-Based Optimizer)로 동작하여 실행 계획을 세우도록 하는 힌트인데 이 경우 테이블이나 인덱스의 통계 정보가 있다고 하더라도 무시하고 사용하지 않으며 규칙에 기반한 실행 계획을 세우게 됩니다.
 
옵티마이저는 순위가 매겨진 오퍼레이션에 근거하여 실행 계획을 세우며 순위가 높은 것이 우선 적용 됩니다. (어떤 규칙이 있는지 이전 강좌 규칙 기반 옵티마이저를 참고 하세요)
 
만약 SQL 문장에서 과 같이 RULE 힌트와 다른 힌트가 같이 사용된다면 RULE 힌트만 적용되니 이점 주의!!
 
select
       e.empno,
          e.ename,
          d.dname
from   scott.emp e, scott.dept d
where  e.deptno = d.deptno
 
Execution Plan
---------------------------------------------------------------
Operation            Object Name      Rows     Bytes    Cost     
SELECT STATEMENT Optimizer Mode=HINT: RULE                                                                NESTED LOOPS                                                                                           
    TABLE ACCESS FULL             SCOTT.EMP                                                            
    TABLE ACCESS BY INDEX ROWID         SCOTT.DEPT                                             
      INDEX UNIQUE SCAN            SCOTT.PK_DEPT                                                                                             
 
이번엔 Rule Based 이면서 인덱스를 사용하지 못하도록 to_char를 붙였습니다. 다들 아시죠? 인덱스 컬럼에 변형을 가하면 인덱스를 사용하지 못한다는 사실을! 그래서 FULL SCAN해서 정렬 후 MERGE JOIN을 하는 실행 계획으로 바뀌었습니다. 그리 좋은 방법은 되지 못하는 것 같습니다. 그리고 RBO를 이용하는 경우엔 철저히 원칙을 지켜야 합니다.
 
select
       e.empno,
          e.ename,
          d.dname
from   scott.emp e, scott.dept d
where  to_char(e.deptno) = to_char(d.deptno)
 
Execution Plan
-------------------------------------------------------------------
Operation            Object Name      Rows     Bytes    Cost     
SELECT STATEMENT Optimizer Mode=HINT: RULE                                        
  MERGE JOIN                                                                                                    
    SORT JOIN                                                                                                   
      TABLE ACCESS FULL          SCOTT.DEPT                                                          
    SORT JOIN                                                                                                   
      TABLE ACCESS FULL          SCOTT.EMP                                                                                        
 
이번엔 똑 같은 Query이지만 ALL_ROWS 힌트로 이용해 봅니다. 현재 EMP, DEPT 테이블의 통계 정보는 생성되어 있습니다. 11g에서는 EMP, DEPT의 데이터 건수가 얼마 안되니 FULL SCAN해서 HASH JOIN을 하는 방법으로 실행계획을 만들었습니다.
 
 
select
       e.empno,
          e.ename,
          d.dname
from   scott.emp e, scott.dept d
where  to_char(e.deptno) = to_char(d.deptno)
 
Execution Plan
-----------------------------------------------------------------
Operation            Object Name      Rows     Bytes    Cost     
SELECT STATEMENT Optimizer Mode=HINT: ALL_ROWS   14          7                                                       
  HASH JOIN                                           14              294       7                         
    TABLE ACCESS FULL             SCOTT.DEPT      4           44         3                         
    TABLE ACCESS FULL             SCOTT.EMP        14         140       3            
 
 
 
규칙 기반 옵티마이저에서 조인시 적용되는 원칙은 다음과 같습니다.
 
조인되는 두 컬럼 모두에 인덱스가 있는 경우 FROM절에서 나중에 나타나는 테이블이 드라이빙 테이블이 되어 먼저 SCAN하고 그 건건이 LOOP를 돌면서 비드라이빙 테이블을 인덱스 UYNIQUE SCAN을 하게 될겁니다. 비용 기반 옵티마이저에서는 FROM절에 나타나는 테이블의 순서와 관계없이 통계 정보에 따라 드라이빙 테이블이 결정 됩니다. 물론 ORDERED 힌트를 사용하는 경우는 예외인데 이 경우 FROM 절에 나오는 테이블의 순서대로 테이블들이 조인 됩니다.
 
[조인되는 두 테이블 모두에 인덱스가 있는 경우]
 
create index idx_emp_deptno on emp(deptno)
 
select
       e.empno,
          e.ename,
          d.dname
from   emp e, dept d
where  e.deptno = d.deptno;
 
 
Execution Plan
-------------------------------------------------------------------
Operation    Object Name  Rows   Bytes  Cost  
SELECT STATEMENT Optimizer Mode=HINT: RULE                          
  TABLE ACCESS BY INDEX ROWID     SCOTT.EMP                          
    NESTED LOOPS                                                 
      TABLE ACCESS FULL     SCOTT.DEPT      ß 드라이빙 테이블                        
      :namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />INDEX RANGE SCAN      SCOTT.IDX_EMP_DEPTNO                                                              
 
 
 
select
       e.empno,
          e.ename,
          d.dname
from   dept d, emp e
where  e.deptno = d.deptno;
 
Execution Plan
-------------------------------------------------------------------
Operation    Object Name  Rows   Bytes  Cost  
SELECT STATEMENT Optimizer Mode=HINT: RULE                           
  NESTED LOOPS                                                  
    TABLE ACCESS FULL       SCOTT.EMP           ß 드라이빙 테이블                     
    TABLE ACCESS BY INDEX ROWID   SCOTT.DEPT                         
      INDEX UNIQUE SCAN     SCOTT.PK_DEPT                                                     
 
조인 컬럼중 한 테이블에만 인덱스가 있는 경우 FROM절 이후 테이블의 순서 보다 인덱스가 생성되어 있지 않은 테이블이 드라이빙 테이블이 되는 실행 계획을 세우게 됩니다. 아래에서 DEPT 테이블의 DEPTNO PK로서 인덱스가 있지만 EMP 테이블의 DEPTNO는 인덱스가 없는 경우 입니다.
 
아래에서는 EMP 테이블이 드라이빙 테이블 입니다.
 
[조인되는 두 테이블중 한 테이블에만 인덱스가 있는 경우]
 
select
       e.empno,
          e.ename,
          d.dname
from   emp e, dept d
where  e.deptno = d.deptno;
 
 
Execution Plan
------------------------------------------------------------------
Operation            Object Name      Rows     Bytes    Cost     
SELECT STATEMENT Optimizer Mode=HINT: RULE                                        
  NESTED LOOPS                                                                                       
    TABLE ACCESS FULL             SCOTT.EMP                                                            
    TABLE ACCESS BY INDEX ROWID         SCOTT.DEPT                                             
      INDEX UNIQUE SCAN            SCOTT.PK_DEPT                                                                                             
 
 
select
       e.empno,
          e.ename,
          d.dname
from   dept d, emp e
where  e.deptno = d.deptno;
 
 
Operation            Object Name      Rows     Bytes    Cost     
SELECT STATEMENT Optimizer Mode=HINT: RULE                                        
  NESTED LOOPS                                                                                       
    TABLE ACCESS FULL             SCOTT.EMP                                                            
    TABLE ACCESS BY INDEX ROWID         SCOTT.DEPT                                             
      INDEX UNIQUE SCAN            SCOTT.PK_DEPT       
 
 
 
[실습] 아래 예문에서 만든 테이블로 위의 예문을 실습해 보라.
 
-- emptest 테이블은 250만건
    CREATE TABLE emptest  
    AS
       SELECT ROWNUM         AS empno
       ,      MOD(ROWNUM,5)   AS deptno--5개씩 그룹핑 (0,1,2,3,4값만가짐)
       ,      '홍길동'||ROWNUM           AS ename  --랜덤 문자5
       ,      '서울'||ROWNUM             AS addr 
       ,      DBMS_RANDOM.STRING('u',5)  AS random --랜덤문자 5
       FROM   dual
       CONNECT BY ROWNUM <= 2500000   --250만건 만들자...
      
      
     -- depttest 테이블은 5 
     CREATE TABLE depttest  
     AS
       SELECT
              MOD(ROWNUM,5)         AS deptno--5개씩 그룹핑 (0,1,2,3,4값만가짐)
       ,      '부서명'||MOD(ROWNUM,5) AS dname  --랜덤 문자5     
       FROM   dual
       CONNECT BY ROWNUM <= 5  --5건 만들자... 
 
 
 -- empno primary key를 만들자      
alter table emptest add primary key (empno)
      
    -- emptest deptno로 인덱스를 만들자.  
    create index idx_emptest_deptno on emptest(deptno);
 
 
 
-- 통계정보 생성
    exec DBMS_STATS.GATHER_TABLE_STATS(USER, 'EMPTEST')
   
    exec DBMS_STATS.GATHER_TABLE_STATS(USER, 'DEPT')
 
 
SQL> alter session set optimizer_mode = rule;
 
세션이 변경되었습니다.
 
   : 00:00:00.01
//depttest 테이블이 드라이빙 테이블이 된다. deptno로 인덱스가 없으니
SQL> select count(*) from emptest e, depttest d
  2  where e.deptno = d.deptno;
 
  COUNT(*)
----------
   2500000
 
   : 00:00:00.40
 
Execution Plan
----------------------------------------------------------
Plan hash value: 3957333709
 
--------------------------------------------------
| Id  | Operation           | Name               |
--------------------------------------------------
|   0 | SELECT STATEMENT    |                    |
|   1 |  SORT AGGREGATE     |                    |
|   2 |   NESTED LOOPS      |                    |
|   3 |    TABLE ACCESS FULL| DEPTTEST           |
|*  4 |    INDEX RANGE SCAN | IDX_EMPTEST_DEPTNO |
--------------------------------------------------
 
//인덱스 컬럼에 변형을 가해서 인덱스를 사용 못하도록 함
SQL>  select count(*) from emptest e, depttest d
  2  where to_char(e.deptno) = to_char(d.deptno);
 
  COUNT(*)
----------
   2500000
 
   : 00:00:01.36
 
-----------------------------------------
| Id  | Operation            | Name     |
-----------------------------------------
|   0 | SELECT STATEMENT     |          |
|   1 |  SORT AGGREGATE      |          |
|   2 |   MERGE JOIN         |          |
|   3 |    SORT JOIN         |          |
|   4 |     TABLE ACCESS FULL| DEPTTEST |
|*  5 |    SORT JOIN         |          |
|   6 |     TABLE ACCESS FULL| EMPTEST  |
-----------------------------------------
 
//마찬 가지로 depttest 테이블에 인덱스가 없으니 depttest를 드라이빙 테이블로
SQL>  select count(*) from depttest d, emptest e
  2  where e.deptno = d.deptno;
 
  COUNT(*)
----------
   2500000
 
   : 00:00:00.14
 
Execution Plan
----------------------------------------------------------
Plan hash value: 3957333709
 
--------------------------------------------------
| Id  | Operation           | Name               |
--------------------------------------------------
|   0 | SELECT STATEMENT    |                    |
|   1 |  SORT AGGREGATE     |                    |
|   2 |   NESTED LOOPS      |                    |
|   3 |    TABLE ACCESS FULL| DEPTTEST           |
|*  4 |    INDEX RANGE SCAN | IDX_EMPTEST_DEPTNO |

댓글 없음:

댓글 쓰기