01. 옵티마이저
(1) 옵티마이저란?
- 옵티마이저(Optimizer)는 사용자가 요청한 SQL을 가장 효율적이고 빠르게 수행할 수 있는 최적(최저비용)의 처리경로를 선택해주는 DBMS의 핵심엔진이다.
- 구조화된 질의언어(SQL)로 사용자가 원하는 결과집합을 정의하면 이를 얻는 데 필요한 처리절차(프로시저)는 DBMS에 내장된 옵티마이저가 자동으로 생성해준다.
- 옵티마이저에는 크게 두 가지가 있다.
- 규칙기반 옵티마이저(Rule-Based Optimizer, 이하 RBO)
- 비용기반 옵티마이저(Cost-Based Optimizer, 이하 CBO)
(2) 규칙기반 옵티마이저
- 휴리스틱(Heuristic) 옵티마이저라고 불리며, 미리 정해놓은 우선순위에 따라 액세스 경로(Access Path)를 평가하고 실행계획을 선택한다.
- RBO는 대용량 데이터를 처리하는 데 있어 합리적이지 못할 때가 많다.
예를 들어, 조건절 컬럼에 인덱스가 있으면 인덱스를 사용한다. 항상 인덱스를 신뢰하며, Full Table Scan과의 손익을 따지지 않는다.
또 다른 예로, 아래와 같은문장을 수행할 때도 empno 컬럼에 인덱스가 있으면 무조건 그 인덱스를 이용해 sort order by 연산을 대체한다.
select /*\+ rule \*/ * from emp order by empno
(3) 비용기반 옵티마이저
- 비용기반 옵티마이저는 말 그대로 비용을 기반으로 최적화를 수행한다.
- 옵티마이저의 최적화 수행단계를 요약하면 다음과 같다.
- 1. 사용자가 던진 쿼리수행을 위해, 후보군이 될만한 실행계획을 찾는다.
- 2. 데이터 딕셔너리(Data Dictionary)에 미리 수집해 놓은 오브젝트 통계 및 시스템 통계정보를 이용해 각 실행계획의 예상비용을 산정한다.
- 3. 각 실행계획의 비용을 비교해서 최저비용을 갖는 하나를 선택한다.
(4) 옵티마이저 모드
- rule : RBO 모드를 선택하고자 할때 사용한다.
- all_rows : 쿼리 최종 결과집합을 끝까지 Fetch하는 것을 전제로, 시스템 리소스를 가장 적게 사용하는 실행계획을 선택한다.
- first_rows : 전체 결과집합 중 일부 로우만 Fetch하다가 멈추는 것을 전제로, 가장 빠른 응답 속도를 낼 수 있는 실행계획을 선택한다.
- first_rows_n : 사용자가 처음 n개 로우만 Fetch하는 것을 전제로, 가장 빠른 응답 속도를 낼 수 있는 실행계획을 선택한다.
- choose : 액세스되는 테이블 중 적어도 하나에 통계정보가 있다면 CBO, 그중에서도 all_rows 모드를 선택한다. 어느 테이블에도 통계정보가 없다면 RBO를 선택한다.
- 옵티마이저 모드 선택 : 일반적으로 first_rows는 OLTP 환경에서, all_rows는 DW나 배치 프로그램 등에서 사용하는 옵티마이저 모드라고 알려져 있다.
하지만 요즘과 같은 웹 어플리케이션 환경에서는 OLTP이더라도 대개 all_rows가 올바른 선택이다. 애플리케이션에서 수행되는 쿼리 자체가 전체범위처리를 요구하기 때문이다.
SQL> create table t_emp
2 as
3 select * from scott.emp, (select rownum no from dual connect by level <= 1000)
4 order by dbms_random.value;
SQL> alter table t_emp add constraint t_emp_pk primary key(empno, no);
SQL> begin
2 dbms_stats.gather_table_stats(
3 ownname => user
4 , tabname => 't_emp'
5 , method_opt => 'forcolumns sal');
6 end;
7 /
SQL> set autotrace traceonly exp
SQL> select /*+ all_rows */ * from t_emp
2 where sal >= 5000
3 order by empno, no;
----------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 41000 | 26 (8) |
| 1 | SORT ORDER BY | | 1000 | 41000 | 26 (8) |
| 2 | TABLE ACCESS FULL|T_EMP | 1000 | 41000 | 25 (4) |
----------------------------------------------------------------
SQL> select /*+ first_rows */ * from t_emp
2 where sal >= 5000
3 order by empno, no;
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost(%CPU)|
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 41000 | 13900 (1)|
| 1 | TABLE ACCESS BY INDEX ROWID |T_EMP | 1000 | 41000 | 13900 (1)|
| 2 | INDEX FULL SCAN |T_EMP_PK | 14000 | | 37 (3)|
----------------------------------------------------------------------------
SQL> set autotrace traceonly exp
SQL> select /*+ first_rows */ * from t_emp
2 where sal >= 5001
3 order by empno, no;
----------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 41 | 26 (8) |
| 1 | SORT ORDER BY | | 1 | 41 | 26 (8) |
| 2 | TABLE ACCESS FULL|T_EMP | 1 | 41 | 25 (4) |
----------------------------------------------------------------
SQL> select /*+ rule */ * from t_emp
2 where sal >= 5001
3 order by empno, no;
------------------------------------------------
| Id | Operation | Name |
------------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | TABLE ACCESS BY INDEX ROWID |T_EMP |
| 2 | INDEX FULL SCAN |T_EMP_PK |
------------------------------------------------
SQL> select /*+ first_rows(10) */ * from t_emp
2 where sal >= 2000
3 order by empno, no;
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost(%CPU)|
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 11 | 451 | 28 (0)|
| 1 | TABLE ACCESS BY INDEX ROWID |T_EMP | 5999 | 240K| 28 (0)|
| 2 | INDEX FULL SCAN |T_EMP_PK | 26 | | 2 (0)|
----------------------------------------------------------------------------
SQL> select /*+ first_rows(100) */ * from t_emp
2 where sal >= 2000
3 order by empno, no;
----------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5999 | 240K| 93 (4) |
| 1 | SORT ORDER BY | | 5999 | 240K| 93 (4) |
| 2 | TABLE ACCESS FULL|T_EMP | 5999 | 240K| 25 (4) |
----------------------------------------------------------------
문서에 대하여