2009/01/08 13:21
※ 이 글은 "실전 SQL 튜닝" 강의를 듣고 개인적인 의견과 함께 정리한 내용입니다.

Cost Based Optimizer의 작동 원리


CBO의 작동 순서

사용자 삽입 이미지

CBO는 다음과 같은 순서를 거쳐서 수행 됩니다.

  1. Parsing 단계
    파싱 단계는 SQL 구문(syntax)과 의미검사(semantics)를 수행합니다. 예를 들어 SQL 구문이 정확한지 검사하고 참조된 테이블에 대해 사용자의 접근권한을 검사합니다. SQL은 파싱트리(parsing tree) 형태로 변형되어 옵티마이저에게 넘겨집니다.

  2. 옵티마이저(Qeury Optimizer)
    위 그림에서 점선형태의 사각형으로 표시된 부분이 옵티마이저의 주요 구성요소입니다. 각 구성요소의 역할은 아래와 같습니다.

  3. 질의 변환(Query Rewriter 또는 Transformer)
    질의 변환 단계는 파싱트리를 받아들여서 질의 변환을 수행합니다. 질의 변환은 사용자가 작성한 SQL문을 더 나은 실행계획을 찾을 수 있는 SQL문으로 변환합니다.

  4. 실행계획 생성(Plan Generator)와 Estimator
    오라클 옵티마이저는 실행계획 생성과 비용산정 모듈을 수행하기 전에 질의에서 사용된 모든 테이블들과 각 테이블에서 정의된 인덱스들에 관해 기본적인 통계정보들(테이블 블럭수, 로우의 평균길이, 인덱스의 높이, 인덱스 리프 블록의 수 등)과 각 테이블에 대한 다양한 엑세스 경로(Full table scan, Index scan 등)에 대한 비용정보를 미리 산출하여 Cost Model을 만듭니다. 이러한 일련의 정보를 바탕으로 옵티마이저는 최적의 실행계획을 생성하여 'Best Query Plan'을 작성합니다.

CBO에서만 가능한 기능들

CBO는 위와 같이 작동하기 때문에 RBO(Rule Based Optimizer)에는 없는 CBO에서만 가능한 기능들이 있습니다
  • 테이블 및 인덱스의 Partitioning
  • 인덱스 구성 테이블(Index-organized table)
  • Reverse Key Index
  • Function Based Index
  • SELECT 문장에서의 SAMPLE절
  • 병렬 Query 및 병렬 DML
  • Star Join 및 Star 변형
  • Optimizer 확장
  • Materialaized View를 이용한 Query rewrite
  • Enterprise Manager progress meter
  • 해쉬 Join
  • Bitmap 인덱스 및 Bitmap Join 인덱스
  • 인덱스 skip scan 알고리즘

CBO의  작동원리

  • 실행계획 중 cost가 가장 적은 실행계획을 선택
  • 컬럼별 데이터 분포에 대한 통계정보(=히스토그램) 사용
  • 복잡한 관계표현에서 때로 잘못된 실행계획을 수립할 수도 있는데 이런 경우 힌트를 사용하여 수정하도록 함
  • 오브젝트에 대한 통계정보를 기준으로 실행계획을 작성하므로 통계정보가 제대로 생성되어 있지 않은경우, 응용프로그램의 성능에 악영향을 미치게 되므로 주의
  • Cost engine은 I/O cost, Network cost, CPU cost 등도 고려하도록 설계되어 있음
※ 통계정보 : 테이블의 데이터 건수, 평균 실이, 컬럼 별 distinct 값의 수, 인덱스 leaf node의 depth 등이 저장
크리에이티브 커먼즈 라이선스
Creative Commons License
2008/12/31 08:15
※ 이 글은 "실전 SQL 튜닝" 강의를 듣고 개인적인 의견과 함께 정리한 내용입니다.

CBO와 관련된 잘못된 편견

  • 항상 주기적으로 통계정보를 생성해야 한다.
    - sql문의 실행계획은 통계정보가 바뀌면 항상 바뀌는 것은 아니지만 바뀔수도 있습니다. 평소에 잘 돌아가던 sql도 통계정보가 변경됨으로써 실행계획이 바뀌어서 기존보다 느려질 수도 있습니다. 하지만 대부분의 sql은 통계정보를 생성하면 통계정보가 크게 바뀐게 아니라면 기존과 큰 차이가 없을 것입니다.
  • CBO의 가장 적게 드는 COST가 실제로도 가장 적게 드는 COST인가?
    - 위 말을 간단하게 표현하자면 '최적의 실행계획이 실제로도 가장 최적인가?'입니다. 하지만 불행히도 실제로는 100% 맞다고 할 수 없습니다. 이는 CBO가 사용하는 자료가 통계적인 자료이기 때문에 sql과 data에 따라서 달라질 수 있기 때문입니다.
  • Oracle 10g에서는 RBO를 지원하지 않는다.
    - 10g에서는 기본이 CBO 입니다. 그러나 RBO의 기능은 존재합니다. 하지만 RBO에 관한 성능 및 기술지원은 보장하지 못합니다.

크리에이티브 커먼즈 라이선스
Creative Commons License
2008/12/10 18:49
※ 이 글은 "실전 SQL 튜닝" 강의를 듣고 개인적인 의견과 함께 정리한 내용입니다.

CBO에서 Cost란?


 Cost란 말 그대로 비용입니다. DB에서 쿼리를 실행할 때 필요한 비용인거죠. 그리고 비용이 많이 들수록 DB의 응답속도와 부하는 늘어납니다. 그렇다면 이 Cost는 무엇을 근거로 산출되고 무엇을 Cost라고 하는 걸까요?

Cost는 무엇을 기준으로 나오는 값인가?


Jonathan Lewis says : The cost is the optimizer's best estimate of the time it will take to execute the statement.
=> 비용이란 optimizer가 sql 문장을 수행하는데 걸리는 시간의 최선의 예측치다.

여기서 수행하는데 걸리는 시간의 최선의 예측치는 간단하게 말하자면 "execution time = CPU time + I/O time" 이런 공식으로 계산했을 때 가장 빠른 시간을 말합니다. 하지만 Lock등이 발생해서 생기는 wait time은 계산에 들어가지 않습니다. 왜냐구요? 당연히 optimizer는 wait time등을 예측 할 수 없기 때문이죠.
I/O time은 다음과 같습니다. "I/O time =  Single Block I/O time(like index)  + Multi Block I/O time(like table full scan"



크리에이티브 커먼즈 라이선스
Creative Commons License
CBO, cost
2008/11/25 19:31
※ 이 글은 "실전 SQL 튜닝" 강의를 듣고 개인적인 의견과 함께 정리한 내용입니다.

OPTIMIZER MODE란 간단하게 말하면 자동차의 기어와 같습니다.
짐을 가득 싣고 오르막길을 간다면 1단이나 2단으로 가야하고 고속도로에서 100km이상 달리고자 한다면
3,4단으로 가야 할 것입니다.
OPTIMIZER 또한 자동차의 기어처럼 상황에 맞는 모드의 선택이 필요합니다.
아... 그리고 자동차 기어에 비유했다고 해서 자동차처럼 수시로 바꾸시면 안됩니다. 운영중인 디비에서 그랬다간 시말서 써야 할지도 모릅니다.^^;;

우선 OPTIMIZER MODE의 종류에 대해서 알아볼까요?
OPTIMIZER MODE 종류
  1. CHOOSE
    - 오라클의 9i의 디폴트 설정입니다. 하지만 10g 부터는 ALL_ROWS가 디폴트 설정이죠. 간단하게 설명해서 CBO나 RBO를 사용할지 오라클이 알아서 선택합니다. /*+ RULL*/ 힌트를 사용하거나 통계정보가 하나도 없다면 RBO를 그 외의 경우에는 CBO를 사용하게 됩니다.
  2. RULE
    - 무조건 RBO를 사용합니다.
  3. FIRST_ROWS (FIRST_ROWS_N)
    - CBO의 일종으로 일부 데이터를 보여주는데 최적화된 모드입니다. 테이블에 1000건이 있어도 '최근 데이터 20건만 보여주겠다.'라면 FIRST_ROWS를 사용하시면 됩니다. 주로 OLTP(Online Transaction Processing)에 적당합니다.
  4. ALL_ROWS
    - CBO의 일종으로 전체 데이터를 사용하는 환경에 적당합니다. 예를들어 '테이블안에 있는 전체 데이터를 검색해서 전체의 합이나 평균을 구하겠다.'라면 ALL_ROWS를 사용하시면 됩니다. 주로 OLAP(Online Analytical Processing)에 적당합니다.

지금까지 OPTIMIZER MODE의 종류에 대해서 알아봤으니 지금 사용하고 있는 DB의 OPTIMIZER MODE가 궁금하시죠? 이번에는 OPTIMIZER MODE를 확인하는 방법을 알아보겠습니다.
[code sql]
SELECT NAME, VALUE, ISDEFAULT, ISMODIFIED, DESCRIPTION
FROM V$PARAMETER
WHERE NAME LIKE '%optimizer_mode%';
[/code]

제 DB에서는 이렇게 나오는 군요. 참고로 oracle10g에 기본 설정입니다.
    NAME    VALUE    ISDEFAULT    ISMODIFIED    DESCRIPTION
1    optimizer_mode    ALL_ROWS    TRUE    FALSE    optimizer mode

OPTIMIZER MODE는 Instance, Session, Statement  세 가지 레벨에서 설정할 수 있습니다.
OPTIMIZER MODE의 레벨별 설정
  1. Instance Level
    - initSID.ora에서 설정할 수 있으며 변경시에는 DB를 재시작해야 합니다.
    OPTIMIZER MODE = { RULL / CHOOSE / FIRST_ROWS / ALL_ROWS }
  2. Session Level
    [code sql]
    ALTER SESSION SET OPTIMIZER MODE = { RULL / CHOOSE / FIRST_ROWS / ALL_ROWS }
    [/code]
  3. Statement Level
    - SQL문에 Hint를 추가
    [code sql]
    SELECT /*+ FIRST_ROWS */ ename = from emp;
    [/code]
※ 각 설정의 우선순위는 Statement > Session > Instance 순입니다.

각 모드를 선택하는 기준은 환경에 따라 다르지만 "실전 SQL 튜닝"강의에서는 OLTP에서는 과거에 CHOOSE를 사용했다면 FIRST_ROWS, 9i 이상에서 신규개발이거나 과거에 CHOOSE를 사용하지 않았다면 FIRST_ROWS_N 사용을, OLAP에서는 ALL_ROWS를 추천~!

OPTIMIZER 관련 파라미터
  1. CURSOR_SHARING (FORCE, SIMILAR, EXACT)
    - SQL 조건절에 있는 상수값들을 변수로 전환시켜 파싱하는 옵션입니다.
    FORCE, SIMILAR는 조건절에 상수값을 변수로 인정하여 실행계획을 수립하므로 실행계획의 공유비율이 높아집니다. 반면 EXACT는 대소문자, 공백, 비교 상수값이 조금만 달라도 공유할 수 없습니다.
  2. DB_FILE_MULTIBLOCK_READ_COUNT
    - Full Table Scan, Index Fast Full Scan을 할때  한번 I/O에 읽을 블록 수를 지정할 수 있습니다. 기본값은 8이며 대용량의 배치처리가 많은 경우 좀더 높은 값을 주면 I/O 타임을 줄일 수 있습니다. 이 값이 커지면 풀 테이블 스캔과 병행해서 Sort Merge Join 또는 Hash Join의 경향이 커집니다.
  3. OPTIMIZER_INDEX_CACHING
    - Nested Loop Join시 버퍼 캐쉬내에 Inner Table의 인덱스를 캐쉬화 하는 비율(%)을 지정하므로 Nested Loop Join시 성능이 향상되며 이 수치가 높을 수록 CBO가 Nested Loop Join을 선호하게됩니다. 기존의 RBO를 CBO로 전환시 옵티마이저를 RBO 성향으로 보정하는데 효과적입니다. 기본값은 0으로 지정되어 있으며 주로 현업에서는 80~100을 사용한다고 합니다.
  4. OPTIMIZER_INDEX_COST_ADJ
    - 비용계산을 할 때 인덱스 엑세스 비중을 조정하는 역할을 담당하는 변수로써 기본값 100은 계산된 비용을 그대로 적용한다는 의미('계산된비용 값' * 100 )이며 10을 주었다면 '계산된비용 값' * 10으로 비중을 주겠다는 뜻입니다. 인덱스를 이용하는 플랜 위조로 하고자 한다면 100(%)이하를, 가능한 인덱스를 사용하지 않고자 한다면 100이상(100~10000)을 지정하면 됩니다. 기존의 RBO를 CBO로 전환시 옵티마이저를 RBO 성향으로 보정하는데 효과적입니다. 주로 현업에서는 10 또는 25정도를 사용한다고 합니다.
  5. OPTIMIZER_PERCENT_PARALLEL
    - CBO가 비용계산시 이 옵션의 수치가 높을 수록 병렬성을 이용하여 풀 테이블 스캔으로 테이블을 엑세스 하려고 합니다. 기본값은 0입니다.
  6. HASH_AREA_SIZE, HASH_JOIN_ENABLED
    - 이 파라미터의 값에 따라서 Hash Join으로 유도 할 수 있습니다. Hash Join이 가능하고 해쉬  메모리가 충분하다면 플랜에서 Hash Join의 경향이 커집니다.(항상 Hash Join으로 풀리는게 아니라 CBO가 자원이 충분하고 Hash Join을 사용하는게 더 좋다고 판단하면 Hash Join으로 풀립니다.)
  7. OPTIMIZER_SEARCH_LIMIT
    - 옵티마이저가 조인 비용을 계산할 경우 이 파라미터의 값을 가지고 조인의 경우의 수를 바탕으로 비용을 계산하게되므로 이 파라미터의 값이 기본값인 5일 경우 FROM 절에서  3개의 테이블이 있다면 3!=6, 5개의  테이블이 있다면 5!=120개의 경우의 수를 가지고 비용계산을 하게 됩니다. 이때 너무 많은 경우의 수를 바탕으로 비용을 계산하게 되면 옵티마이저가 많은 시간을 소비하게 됩니다.
  8. OPTIMIZER_SIZE_POLICY (AUTO | MANUAL)
    - 옵티마이저가 [Hash | SORT | BITMAP_MERGE | CREATE_BITMAP]*_AREA_SIZE를 자동으로 결정하는 PGA 자동관리 방식으로 인위적인 설정없이는 자동 할당된 메모리로 플랜이 결정된다.
  9. OPTIMIZER_DYNAMIC_SAMPLING
    - Oracle 9i일 경우는 기본값이 1, 10g일 경우는 2로서 0~10 레벨이 있습니다. 레벨이 높을 수록 SQL 문장의 실행시점에 통계정보를 만들기 위해 테이블의 데이터를 샘플링하기 위한 추가적인 Recurcive SQL이 발생된다. Oracle 10g의 경우 통계정보가 없는 경우 자동으로 '다이나믹 샘플링'이 적용된다. 이때 그만큼의 부하가 발생되고 또한 통계정보가 있는 것보다는 부정확하므로 CBO를 사용한다면 되도록이면 꼭 통계정보를 생성하도록 하는것이 좋습니다.

옵티마이저 관련 파라미터 [권장예시]
이 값은 예시일 뿐입니다. 현업에서 많이 사용되어진다고 해서 모든 환경에서 절대적인 수치가 아닙니다. 이 파라미터들을 적용하기전에 반드시 해당환경에서 여러 수치로 테스트를 거치고 가장 적합한 수치를 적용해야 합니다.
DB_FILE_MULTIBLOCK_READ_COUNT = 16 or 32
HASH_JOIN_ENABLED = TRUE
OPTIMIZER_INDEX_CACHING = 80~100
OPTIMIZER_INDEX_COST_ADJ = 10~25
이밖의 다른 파라미터 들은 전문 DBA가 아닌이상 테스트 해보고 적용하는데 있어서 들인 노력만큼의 성과를 얻기가 힘드리라 예상됩니다. 제가 DBA가 아니라 개발자라서 아무래도 개발자 입장에서 보게 되네요.^^;;






크리에이티브 커먼즈 라이선스
Creative Commons License
2008/11/24 08:43
※ 이 글은 "실전 SQL 튜닝" 강의를 듣고 개인적인 의견과 함께 정리한 내용입니다.

Cost_based Optimizer는 통계정보를 바탕으로 실행계획을 수립한다. 이는 Rule_based Optimizer보다 진보된 방법이지만 옵티마이저가 통계정보를 바탕으로 실행계획을 수립하기 때문에 sql이 작성자 의도대로 실행되지 않는 경우가 있기 때문에 실행계획 예측이 어렵다. 하지만 이런점은 초보자가 작성한 sql이라도 어느정도 성능을 보장해 주는 역할을 하기도 한다.
 실제 경험상 Oracle 9i에서 10g로 업그레이드만 했을 뿐인데도 쿼리의 실행 속도가 현저하게 빨라진 경우를 본적이 있다. 둘다 Cost_based Optimizer이긴 하지만 10g의 옵티마이저가 좀더 똑똑하다는(다양한 통계정보를 이용해서 실행계획을 잘 짜는) 이야기다. 이렇게 옵티마이저가 발전할 수록 옵티마이저는 최소한의 성능을 보장해 주고 좀더 빠른 실행속도를 보장해 줄 것이다.

 Cost_based Optimizer가 이용하는 통계정보
  1. 테이블의 로우 수와 블럭 수
  2. 블록당 평균 로우 수
  3. 로우의 평균길이
  4. 컬럼별 상수값의 종류
  5. 분포도
  6. 컬럼내의 NULL값의 수
  7. 클러스터링 팩터
  8. 인덱스의 깊이(Depth, Level)
  9. 컬럼의 최대, 최소값
  10. 리프(Leaf) 블록수
  11. 가동 시스템의 I/O, CPU정보
크리에이티브 커먼즈 라이선스
Creative Commons License
2008/11/24 08:23
※ 이 글은 "실전 SQL 튜닝" 강의를 듣고 개인적인 의견과 함께 정리한 내용입니다.

Optimizer에는 Rule_based와 Cost_based가 있다. Rule_based Optimizer는 아래와 RULE대로 우선순위를 적용해서 실행계획을 세운다. 또한 Cost_based Optimizer 또한 통계정보+RULE을 중심으로 실행계획을 세우므로 아래 순서를 외우지는 못하더라도 각 항목의 우선순위정도는 알아두면 좋을 것 같다.
  1. ROWID로 1로우 엑세스
  2. 클러스터 조인에 의한 1로우 엑세스
  3. Unique HASH Cluster에 의한 1로우 엑세스
  4. Unique INDEX에 의한 1로우 엑세스
  5. CLUSTER 조인
  6. Non Unique HASH Cluster Key
  7. Non Unique Cluster Key
  8. Non Unique 결합 인덱스
  9. Non Unique한 결합 인덱스
  10. 인덱스에 의한 범위처리
  11. 인덱스에 의한 전체 범위처리
  12. Sort Merge 조인
  13. 인덱스 컬럼의 MIN,MAX 처리
  14. 인덱스 컬럼의 ORDER BY
  15. 전체 테이블 스캔
크리에이티브 커먼즈 라이선스
Creative Commons License