알티베이스는 .. 개발자로써 개발하기 너무나 좋은 곳이다 .. ㅎㅎ;;
DB/DBMS 를 배우고자 한다면 알티베이스를 적극 권장한다.
어떤 회사보다 기술력 있고 미래가 밝은 회사라고 생각한다.
물론 가서 열심히 해야 한다 ..
사람들도 좋고, 사내 분위기도 좋은것 같다.
웹기업(NHN, 다음 등등..) 의 창의적이고 적극적인 마인드를 추구하는듯한 느낌?
음 .. 아직 뭐가 몬지 잘 모르겠지만,
열심히 해서 프로젝트 잘 마무리 하자~ ^^;
2009년 1월 18일 일요일
지금은 알티베이스 인턴중 ...
옵티마이저의 원리와 특징
Oracle Optimizer의 원리 이해 및 SQL & 애플리케이션의 튜닝(상):
옵티마이저의 원리와 특징
최세훈 | 한국오라클
다수의 데이타베이스 튜닝과 SQL / 애플리케이션 튜닝을 통해 튜닝의 효과를 확신하는 필자가 유익한 튜닝 정보 를 제공한다.
여기에서 필자는 SQL 문장 개별 단위의 튜닝보다는 우선 옵티마이저의 원리를 이해하고, 전체 구조적인 문제, 유형문제 또한 옵티마이저 관련 파라미터의 설정이 먼저 최적으로 설정된 상황하에서 SQL 문 장의 단위 플랜에 대한 튜닝이 이루어져야 한다고 강조한다. 그런 취지에서 이 글에서는 옵티마이저의 원리에대한 이해를 기반으로 SQL 및 애플리케이션 튜닝에 필요한 기본지식을 개발자와 DBA가 쉽게 이해 할 수 있도록 2회에 걸쳐 소개할 예정 이다.
옵티마이저의 질의 처리 단계에 대한 이해
오라클에서 사용하는 옵티마이저(Optimizer)는 크게 RBO(Rule Base Optimizer)와 CBO(Cost Base Optimizer) 2개로 구분된다. 1992년 Oracle 7에서 처음 CBO가 지원된 이래 새로운 기능들이 적용되면서 CBO가 계속 향상되고 있는 데 반해, RBO는 오라클이 더 이상은 추구하지 않는 옵티마이저로서, 현재의 Oracle Database 10g에서도 명맥은 남아 있지만 향후는 더 이상 지원되지 않을 것이다.
옵티마이저의 질의 처리 단계에 대한 이해
오라클에서 사용하는 옵티마이저(Optimizer)는 크게 RBO(Rule Base Optimizer)와 CBO(Cost Base Optimizer) 2개로 구분된다. 1992년 Oracle 7에서 처음 CBO가 지원된 이래 새로운 기능들이 적용되면서 CBO가 계속 향상되고 있는 데 반해, RBO는 오라클이 더 이상은 추구하지 않는 옵티마이저로서, 현재의 Oracle Database 10g에서도 명맥은 남아 있지만 향후는 더 이상 지원되지 않을 것이다.
옵티마이저의 입장에서 질의(query) 처리는 5단계로 나눌 수 있는데, 옵티마이저는 서브질의와 뷰의 병합(merge) 등을 수행하는‘Query Rewrite’단계와‘Query Optimization’단계에 참여한다. 여기서 옵티마 이저는 ▲ 데이타를 어떠한 방법으로 액세스할 것이며 ▲ 올바른 결과를 어떻게 제공할 것이며 ▲ 데이타를 얼마나 효과적으로 액세스할 것인가를 결정한다. ‘QEP Generation’단계는‘Query Optimization’단계에서 제공된 정보를 이용해서 질의에 대한 최적의 실행계획(execution plan)을 만들어 내는 단계이다. CBO에서는 질의 실행계획(QEP)을 구하기 위하 여RBO보다 복잡한 단계를 거치게 된다<그림 1>, <표 1>.
소프트 파싱과 하드 파싱
SQL 문장이 옵티마이저에 의해 처리되고 그 결과물로서, SQL 문장이 어 떻게 실행될 것인지의 정보, 즉, QEP가 생기게 된다. 이들 정보는 한번 쓰 고 버리는 것이 아니라 오라클의 캐쉬(cache) 영역인 SGA의 공유 풀 (shared pool)에 이들 모든 정보를 캐쉬화해 관리한다. 다음 번에 같은 SQL 문장이 사용자에 의해서 실행되면, 이를 재활용하게 된다.
SQL 문장이 실행되면, 우선 SQL 문장 텍스트의 스트링을 해쉬 함수을 통과시켜 결과 값에 해당되는 버킷(어레이형 구조)에 매달린 체인 정보에서 같은 SQL 문장이 존재하는지 찾는 처 리절차를 수행하게 된다. 또한 같은 SQL 문장을 찾았어도 여러 버전이 존재할 수 있다. 여러 버전이란, 같은 SQL 문장(대/소문자, 화이트 스페이스 등이 모두 같아야 함)이지만 서로 다른 스키마의 테이블(예, scott의 emp, sys의 emp)이거나, 바인드 변수 를 사용한 경우는 바인드 변수의 타입, 길이 등에 의해서도 서로 다른 버전이 된다는 것이다. 이와 같이 같은 SQL 문장에 같은 버전을 찾았다면 이를 ‘소프트 파싱(soft parsing)’이라고 한다.
그렇지만, 체인을 다 찾았는데 같은 문장을 발견하지 못했다면, 해당 SQL 문장이 Parsing/Optimizing 단계를 거친 결과로 나온 정보를 저장하 기 위해 공유 풀로부터 메모리를 확보 받고, 기록한 정보를 체인에 매달게 된다. 이를‘하드 파싱(hard parsing)’이라고 한다. 당연히 하드 파싱의 작 업량이 소프트 파싱의 작업량에 비해 월등히 클 것이다<그림 2>.
이와 같은 소프트 파싱과 하드 파싱의 과정을 생각해 볼 때, 집중적인 SQL 문장이 실행되는 OLTP(초당 수천 ~ 수만 개 이상)에서 하드 파싱이 많다면 어떻게 될까? 한정된 메모리인 캐쉬에 새로운 메모리를 계속 할당 하고, LRU 알고리즘에 의해 제거하고, 체인에 매달고 끊는 등의 일들을 반 복해야 할 것이다. 또한 하드 파싱은 복잡한 처리과정을 거치므로 많은 자 원(CPU)을 사용하게 된다. 그러므로 OLTP 환경에서는 이와 같은 하드 파 싱을 가능한 줄이도록 해야 한다. 특히 SQL 실행 규모가 큰 OLTP 업무는 1% 미만을 권장한다.
애플리케이션을 개발할 때 이러한 하드 파싱을 줄이기 위한 방법으로 거의 대부분의 데이타베이스 접속 방식(JDBC, ODBC, ADO, PRO*C 등) 에서 자주 사용되는 SQL 문장들은 바인드 변수 기법들을 사용하여 개발하 는 방법들을 제공하고 있다. 또한 일부에서는 소프트 파싱 자체도 줄일 수 있는 기법들을 제공하고 있다. 실제 이러한 기법을 적용해서 튜닝한 결과, 시스템 CPU/메모리 측면에서 40~50% 이상 개선된 사례가 많이 있다. 혹 시 현재 운영중인 시스템이 사용자가 많아지면서CPU 리소스가 급격히 증 가해, 라이브러리 캐쉬, 공유 풀 경합 현상이 발생한다면, 이러한 점을 의심 해 볼 수 있다.
<표 4>는 SQL 문장을 바인드 변수를 사용한 공유 SQL과, 상수를 결 합한 형태로 SQL 문장을 만들어 실행시키는 비공유 SQL을 9,999회 실행 시켜 오라클의 공유 풀 메모리 사용현황과 파싱시CPU 사용시간을 테스트 한 것이다(단, 그 결과치는 실행 서버별로 차이가 있다).
결론적으로 보면, 비공유 SQL 방식의 사용 메모리와 CPU 사용률이 실행 규모에 비례해 증가하고, 실행된 SQL 문장이 기존에 캐쉬화되어 있 는 SQL 문장들을 밀어내는 역할을 한다는 것을 알 수 있다.
이와 같은 SQL 문장을 공유하기 위해서 오라클 입장에서 처리해주는 CURSOR_SHARING이라는 파라미터를 제공하기도 한다. 그러나, CURSOR_SHARING은 모든 상수를 다 바인드 변수로 바꿔버리기 때문에 개발자가 의도하지 않은 Literal까지도 바꾸게 되므로, 애플리케이션을 수 정할 수 있다면 가능한 애플리케이션 단에서 바인드 변수를 사용하는 것이 효과적이다.
하드 파싱을 줄이기 위해 모든 업무에 바인드 변수 사용방법을 적용하는 것은 잘못된 생각이다. 옵티마이저의 입장 에서 보면, 바인드 변수 기법 보다는 Literal을 사용한 비공유 SQL 방식을 좋아한다. Literal SQL 문장일 경우는 상수 값에 따라서 범위를 정확히 알 수 있기 때문에 효과적인 플랜 을 결정하는 주요 결정요소로 작용하기 때문이다. 즉 바인드 변수 기법은 옵티마이저의 판단에는 좋지 않지만 SQL 문장이 집중적으로 실행되는 OLTP 환경에서 하드 파싱의 비율을 줄이기 위한 방법인 것이다. 즉, 업무 의 특징에 따라서 다른 적용방식이 사용되어야 한다. 다음은 OLTP와DW 의 특징에 따라 다르게 고려되어야 할 사항이다.
• OLTP의특징
- 목표 : 신속한응답시간, 적은 데이타 처리량
- 파싱 타임을 최소화하고 SQL 등이 공유될 수 있도록 바인드 변수를 사용해야한다.
- 인덱스의 사용률이 높아야 한다.
- 정렬(sorting)을 최소화해야 한다.
- Nested Loop Join(FIRST_ROWS_n) 방식으로 많이 유도한다.
• DW의특징
- 목표 : 최고의 처리량, 방대한 데이타 처리량
- 인덱스의 참조는 중요한 사항이 아니다.
- 정렬 또는 Aggregate함수 등이 중요한 역할을 한다.
- Hash Join 등을 많이 사용하도록 유도한다.
- 파싱 타임 등은 그리 중요하지 않으며, 바인드 변수의 사용이 문제가 될 수 있다. - 병렬 질의 등의 사용률을 높인다.
Rule Base Optimizer
질의 최적화(query optimization)에서 RBO(Rule Base Optimizer)는 정해진 랭킹(ranking)에 의해 플랜을 결정한다. 같은 랭킹이라면 Where 절의 뒤부터, From절 뒤의 객체가 우선 순위를 갖는다. 한 객체(예 : 테이 블)에서 같은 랭킹의 인덱스가 있다면 가장 최근에 만들어진 인덱스를 사 용한다. 이는 CBO(Cost Base Optimizer)에서도 같이 적용되는 사항이다.
다분히RBO는 개발자들이 프로그래밍 단계에서 SQL 문장 구조의 인위적 인 조정 등으로 인덱스를 사용 못하게 하는 등 개발자가 코딩에 신경을 많 이 써야 하는 문제점이 있다. 또한RBO는 해당 질의에 대한 테이블의 인덱 스가 존재한다면 전체 90% 이상의 대상이어도 인덱스를 선택한다는 것이 다. 즉, RBO는 무조건 다음과 같은 미리 정해진 룰을 기준으로 플랜을 결 정하게 된다. 1992년 Oracle 7에서 CBO가 지원되면서 CBO는 계속적인 신기능의 적용으로 발전해 온 반면, RBO는 더 이상의 기능 향상은 없으며, 향후는CBO만 지원될 계획이다. 그러므로RBO에 더 이상의 미련을 갖지 말기 바라며, CBO의 훌륭한 기능들을 적극 활용하길 바란다.
다음은RBO의 랭킹을 정리한 것이다.
Path 1 : Single Row by Rowid
Path 2 : Single Row by Cluster Join
Path 3 : Single Row by Hash Cluster Key with Unique or Primary Key
Path 4 : Single Row by Unique or Primary Key
Path 5 : Clustered Join
Path 6 : Hash Cluster Key
Path 7 : Indexed Cluster Key
Path 8 : Composite Index
Path 9 : Single-Column Indexes
Path 10 : Bounded Range Search on Indexed Columns
Path 11 : Unbounded Range Search on Indexed Columns
Path 12 : Sort-Merge Join
Path 13 : MAX or MIN of Indexed Column
Path 14 : ORDER BY on Indexed Column
Path 15 : Full Table Scan
특히 Path 8, 9, 10에 주의를 해야 한다. 예를 들면, ‘emp’테이블에 ‘A’인덱스가“deptno”로 구성되어 있고, ‘B’인덱스가“deptno + empno”로 구성되어 있다면, 다음과 같은 SQL 문장은‘A’인덱스를 사용 하게 된다. 조건이 Bounded Range Search(Between)로 왔기 때문에 아래의 SQL 문장에서 (A)와 (B)의 랭킹은 (A) ==> Rank 9 , (B) ==> Rank 10 조건이 되므로 싱글 칼럼 인덱스를 사용한다는 것이다.
그러면, 이제CBO에 대해 살펴보기 전에, 참고로RBO를CBO로 전환 한 사례를 잠깐 소개하겠다.
현재RBO를 사용하고 있는 상황에서 마이그래이션시CBO로 전환하 고 싶으나, 막연히 두려운 부분도 많을 것이다. 실제 RBO에서 CBO로 전 환하고 나서 가장 효과를 보는 부분은 배치 잡 형태이다. 특히 Oracle9i Database 이상의 WORKAREA_SIZE_POLICY=AUTO로 운영하는 곳 이라면 더욱 더 그럴 것이다. 그러나 OLTP의 변화는 조심해야 한다. 아래 의 경우는, 이전하면서 옵티마이저 모드를RBO에서CBO로 전환한 것 뿐 만 아니라, 블록 사이즈와 CBO 옵티마이저에 민감한 db_file_multi block_read_count 값도 크게 늘렸다. 특히 WORKAREA_SIZE_ POLICY=AUTO로 필요한 워킹 메모리(Sort, Hash, Bitmap 등)를 옵티마 이저가 판단하에 가능한 충분히 사용하게 하는 방식을 사용하였다.
그러다 보니, CBO에 영향을 주는 소트 메모리와 해쉬 메모리가 풍부 하게 되었고, 블록 사이즈도 커졌으며, 풀 테이블 스캔의 정도를 결정하는 db_file_multiblock_read_count 값도 아주 커진 상태이다. 또한 마이그 래이션되면서 데이타가 재정리되어 있는 상태이므로, 풀 테이블 스캔과 Sort Merge Join, Hash Join의 경향이 커진 상태이다. 그러므로 배치 잡의 경우는 최적의 조건이 되었으나, 기존에 주로 Nested Loop Join을 선호하 던 RBO 환경의 OLTP들은 많은 플랜의 변화에 직면하게 된다. 그러면 이 러한 부분을 어떻게 보정해 줄 것인가? optimizer_index_caching, optimizer_index_cost_adj의 파라미터가 그 해답일 것이다. 가능한 Nested Loop Join를 선호하고, CBO의 옵티마이저 모드가 인덱스에 점수 를 더 주어서 인덱스의 비중을 키울 수가 있는 것이다. 물론 이러한 전환형 태 말고 옵티마이저 모드를FIRST_ROWS_n으로 운영하거나, 아웃라인을 이용하는 방법 등도 있을 것이다. 여러 방법이 있겠지만, 필자는 아래와 같 은 방법을 선호한다.
다음은 마이그래이션시RBO에서CBO로 전환한 사례이다.
Cost Base Optimizer
질의 최적화에서 CBO(Cost Base Optimizer)는해당SQL 문장이 참조하 고 있는 객체들(테이블, 인덱스 등)에 대한 수집된 통계정보(statistics)의 값과 데이타베이스 파라미터(init.ora) 설정 값을 기초로 가장 적은 비용 (cost)이 발생되는 플랜을 결정하는 옵티마이저 방식이다.
여기서 중요한 사실은 RBO에서는 전혀 사용되지 않았던 통계정보를 CBO에서는 이용한다는 것이다. 이들 통계정보는 DBA에 의해서 또는 자 동 수집 기능(Oracle9i Database Release 2, Oracle Database10g)에 의 해 객체들의 통계정보를 관리하는 시스템 딕셔널리(Dictionary)에 저장되 고, 이 정보를CBO 옵티마이저가 이용하는 것이다. 이들 정보는 SQL 문장 을 실행하는 데 얼마만큼의 I/O 횟수가 발생할 것인가를 계산하기 위한 각 종 데이타를 가지고 있다. 여기서 중요한 사실은 I/O 크기는 중요하지 않 으며 I/O 횟수가 중요하다는 것이다. 즉, CBO 옵티마이저는 SQL 문장에 대한 여러 가지 경우의 수별로 I/O의 횟수에 비례한 비용을 산출해내고, 이들 비용에서 가장 작은 비용을 갖는 플랜을 결정한다는 것이다. 즉, 비용 은 I/O 횟수에 비례하는 값이라고 보면 쉬울 것이다.
그러나, Oracle Database 10g부터는 비용의 단위 기준이 I/O에서 처리시간으로 바뀌었다(time base). 또한 Oracle9i Database부터 시스템 통계정보(CPU, 디스크 액세스 타임)를 이용해서 I/O로 환산한 방식을 제공 하였으나, 이것은 단지 옵션이었다. 그러나, Oracle Database 10g부터는 시스템 통계정보(CPU, 디스크 액세스 타임)를 이용해서 처리시간으로 환산한 방식을 디폴트로 사용하므로 상당히 정확한 플랜을 만들어내며, 실행 예측 시간도 상당히 정확하다.
그러면, 여기서 잠깐CBO에서 사용되는 통계정보가 저장된 Dictinary 정보 예를 참고로 살펴보자.
[USER|ALL|DBA]_TABLES : Table의 통계정보
NUM_ROWS,BLOCKS,AVG_ROW_LEN,SAMPLE_SIZE,LAST_ANALYZED
[USER|ALL|DBA]_INDEXES : Index의 통계정보
BLEVEL,LEAF_BLOCKS,DISTINCT_KEYS,AVG_LEAF_BLOCKS_PER_KEY,
AVG_DATA_BLOCKS_PER_KEY,CLUSTERING_FACTOR,NUM_ROWS,SAMPLE_SIZE,
LAST_ANALYZED
[USER|ALL|DBA]_TAB_COLUMNS : Column의 통계정보
NUM_DISTINCT,LOW_VALUE,HIGH_VALUE,DENSITY,NUM_NULLS,NUM_BUCKETS,
LAST_ANALYZED,SAMPLE_SIZE,AVG_COL_LEN
[USER|ALL|DBA]_TAB_HISTOGRAMS : Column의Data 분포도 정보
TABLE_NAME,COLUMN_NAME,ENDPOINT_NUMBER,ENDPOINT_VALUE,
ENDPOINT_ACTUAL_VALUE
기타 파티션 / 클러스터 등에 대한 통계정보
그러면“select * from dept where deptno = 10”과 같은 SQL 문장을 실행해야 한다고 가정하자. 여기서 dept Table은 deptno에 대한 인덱스가 있고, 테이블은 전체 10 블록으로 구성되어 있으며, 풀 테이블 스캔일 경우 I/O 단위를 결정하는 파라미터는 DBA가 db_file_multiblock_read_count=8로 지정하여 운영하고 있다고 가정하자 . 여기서RBO라면 무조건 인덱스를 타는 플랜을 결정하였을 것이다.
그러나, CBO의 경우는 deptno의 인덱스를 이용해 실행하면, 3회(인덱스는 싱글 블록 단위 I/O)의 I/O가 발생한다 고 가정하고, 풀 테이블 스캔의 경우는 2회(8블록 + 2블록)의 I/O가 발생한다고 가정하면, CBO에서 는 인덱스가 있음에도 I/O 횟수 측면에서 더 효과적인 풀 테이블 스캔을 선택한다는 것이다. 그러므로 CBO는 이와 같이 가능한 정확한(현실 데이 타와 맞는) 통계정보와 적절한 데이타베이스의 파라미터인 init.ora에의해 플랜이 결정되는 것이다.
CBO에서만 가능한 기능들
CBO는 오라클의 신기능을 지원하도록 지속적으로 발전하고 있으며, 다음의 경우는 반드시 CBO에서만 플랜 결정 시 검토되거나 무조건 CBO로 동작되는 경우이다. 예를 들어, 파티션 테이블을 사용한다면 통계정보가 없더라도 무조건CBO로 동작된다는 것이다.
• Partitioned tables (*)
• Index-organized tables
• Reverse key indexes
• Function-based indexes
• SAMPLE clauses in a SELECT statement (*)
• Parallel execution and parallel DML
• Star transformations
• Star joins
• Extensible optimizer
• Query rewrite (materialized views)
• Progress meter
• Hash joins
• Bitmap indexes
• Partition views (release 7.3)
• Hint (*)
• Parallel DEGREE & INSTANCES - ‘DEFAULT’도 해당 (*)
CBO의 옵티마이저에 영향을 줄 수 있는 파라미터 예
옵티마이저가 플랜을 수립하는 데 영향을 줄 수 있는 파라미터 값이 무엇인 지를 알고 있는 것이 무엇보다 중요하다. 실제 옵티마이저가 참조하는 파라 미터는 Oracle9i Database 기준으로 보더라도 60여 개에 이른다. 특히 DBA는 이들 옵티마이저의 파라미터 설정에 신중해야 한다. 또한 이들 파라 미터의 효과적인 설정은 개발 중이거나, 마이그래이션 중에 업무의 특징을 판단한 다음, 해당 업무에 가장 효과적인 것을 설정해야 한다. 기준이 잘못되 면 개발자들은 SQL 문장마다 힌트를 넣기 바쁠 것이고, 많은 인적 자원을 튜 닝에 소모해야 할 것이다. 그러므로 대부분의 업무들이 최적화되어 잘 운영 될 수 있는 형태로 이들 파라미터를 바꿔가면서 기준을 정하는 것이 중요하 다. 물론 이들 값보다도CBO에서 사용되는 통계정보가 중요하다는 것은 당 연한 사실이다. 오라클에서는 개발장비에도 운영장비에 있는 통계정보와 같 게 운영할 수 있도록 DBMS_STATS 패키지를 제공한다.
다음은 질의 수행시 옵티마이저가 플랜을 수립하기 위해 참조한 파라 미터 중 일부이다(버전마다 다르다).
OPTIMIZER_PERCENT_PARALLEL (Default = 0)
Optimizer_Percent_Parallel의 Parameter는 CBO가 비용을 계산하는 데 영향을 주는 파라미터이다. 즉 수치가 높을수록 병렬성을 이용하여 풀 테이블 스캔으로 테이블을 액세스하려고 한다. 이 값이 0인 경우는 최적의 시리얼 플 랜이나 패러렐 플랜을 사용하며, 1~100일 경우는 비용 계산에서 객체의 등급 을 사용한다.
OPTIMIZER_MODE (Default=Choose(Oracle7 ~ Oracle9i
Database),ALL_ROWS)
{Choose(<=9i)|Rule(<=9i)|First_rows|First_rows_n(> =Oracle9i)|All_rows}
기본적인 옵티마이저 모드를 결정한다(왼쪽상자기사‘옵티마이저 모드의 종류 및 특징’참조).
HASH_AREA_SIZE, HASH_JOIN_ENABLED (Oracle Database 10g : _ hash_join_enabled=true)
위의 파라미터 값에 따라서 Hash Join으로유도할수있다. Hash Join이가능 하고 해쉬 메모리가 충분하다면, 플랜에 Hash Join의 경향이 커진다.
OPTIMIZER_SEARCH_LIMIT (Default = 5)
옵티마이저에게 조인 비용을 계산할 경우, From절에 나오는 테이블의 개수에 따라서 조인의 경우의 수가 있을 수 있으며, 옵티마이저는 이들 각각의 경우의 수에 대한 조인 비용을 계산하게 된다. 물론 일부 예외사항은 있다. 예를 들어, Cartesian Production Join 등은 우선 순위가 낮으므로 뒤로 미뤄질 것이다. 이 파라미터의 값이 5일 경우 From절에 5개의 테이블에 대해서 모든 조인의 경우의 수를 가지고 비용을 계산하게 되며, 그 개수는 5!=120개의 경우의 수 에 대한 조인 비용을 계산하게 되므로 옵티마이저가 많은 시간을 소모하게 되 므로 성능에 영향을 미칠 수도 있다.
SORT_AREA_SIZE , SORT_MULTIBLOCK_READ_COUNT
위의 파라미터의 값에 따라서 Sort Merge Join으로 유도할 수 있다. 소트 메모 리가 충분하다면, 플랜에 Sort Merge Join의 경향이 커진다.
DB_FILE_MULTIBLOCK_READ_COUNT
이 파라미터의 수치가 클수록 인덱스 스캔보다는 풀 테이블 스캔의 비중이 높 아진다. 이 파라미터는 옵티마이저의 플랜 결정에 민감하게 영향을 주는 값이 다. 즉, 이 값이 커지면 풀 테이블 스캔과 병행해서 Sort Merge Join 또는 Hash Join의 경향이 커진다.
OPTIMIZER_INDEX_CACHING (Default = 0)
CBO가 Nested Loop Join을 선호하도록 조절하는 파라미터, Nested Loop Join시 버퍼 캐쉬 내에 이너 테이블의 인덱스를 캐쉬화하는 비율(%)을 지정하 므로 Nested Loop Join시 성능이 향상되며, 옵티마이저는 비용 계산시 이 비 율을 반영하여 Nested Loop Join을 선호하도록 플랜이 선택된다(0~100). 100에 근접할수록 인덱스 액세스 경로가 결정될 가능성이 높다. 기존의 RBO 를CBO로 전환시 옵티마이저를RBO 성향으로 보정하는 데 효과적이다.
OPTIMIZER_INDEX_COST_ADJ (Default = 100)
옵티마이저가 인덱스를 사용하는 위주의 플랜으로 풀릴 것인지 또는 가능한 사 용하지 않을 쪽으로 풀릴 것인지의 비중을 지정한다. CBO는RBO처럼 인덱스 를 사용하도록 플랜이 주로 만들어지게 되나, 인덱스가 있다고 해서RBO처럼 인덱스를 이용한 플랜으로 처리되는 것은 아니다. 인덱스를 이용하는 플랜 위 주로 하고자 한다면 100(%) 이하를, 가능한 인덱스를 사용하지 않고자 한다면 100 이상을 지정한다(1 ~ 10000). 이 파라미터는 기존의 RBO를 CBO로 전 환시 옵티마이저를RBO의 인덱스 위주 성향으로 보정하는 데 효과적이다.
WORKAREA_SIZE_POLICY (AUTO | MANUAL)
옵 티 마 이 저 가 [HASH|SORT|BITMAP_MERGE|CREATE_ BITMAP] *_AREA_SIZE를 자동으로 결정하는 PGA 자동 관리 방식으로, 인스턴스에 속한 모든PGA의 메모리의 합이PGA_AGGREGATE_TARGET에서 설정된 메모리를 가능한 넘지 않는 범위 내에서 Workarea(Sort, Hash, Bitmap 등)를 충분히 사용하고자 하는 방식이다. 플랜은 할당된 Workarea를 가지고 플랜을 결정하게 되므로 풍부한 메모리에 의해 Hash Join, Sort Merge Join등을 선 호하는 경향이 높다. 내부적으로 히든 파라미터로*_AREA_SIZE의값을가지 고 플랜을 결정할 수도 있으나 인위적인 설정 없이는 자동 할당된 메모리로 플 랜이 결정된다.
OPTIMIZER_DYNAMIC_SAMPLING (Default = 1(Oracle9i Database), 2(Oracle Database 10g))
더 나은 플랜을 결정하기 위한 목적으로 더 정확한 Selectivity & Cardinality 를 구하기 위한 방법으로 0 ~ 10 레벨이 있으며, 레벨이 높을수록 SQL 문장의 실행 시점에 통계정보를 만들기 위해 테이블의 데이타를 샘플링하기 위한 추가 적인 Recursive SQL이 발생된다. DYNAMIC_SAMPLING(0 ~ 10) 힌트를 통해서도 같은 기능을 할 수 있다. 그러나 내부적으로 추가적인 테이블 액세스 의 비용이 발생하므로OLTP에서는 주로 사용하지 않는다. 특히OLTP 환경에 서 레벨을 디폴트 값 이상 높여 놓지 않도록 한다. Oracle Database 10g의경 우 통계정보가 없다면‘다이나믹 샘플링’이 적용된다.
다음은 Oracle Database 10g의 플랜 및 다이나믹 샘플링의 예이다.
옵티마이저 모드의 종류 및 특징
• 인 스 턴 스 레 벨 : optimizer_mode = {Choose|Rule|First_rows| First_rows_n| All_rows}
• 세션 레벨 : 인스턴스 레벨에 우선
ALTER SESSION SET optimizer_mode =
{Choose|Rule|First_rows|First_rows_n|All_rows}
• 스테이트먼트 레벨 : 힌트를 사용하며, 인스턴스, 세션 레벨에 우선
• Oracle9i Database에서 FIRST_ROWS_n 옵티마이저 모드가 추가되었음(N : 1, 10, 100, 1000).
• Oracle Database 10g에서는CHOOSE, RULE 모드는 더 이상 지원되지 않으나, 기능은남아있다.
• OPTIMIZER_MODE=CHOOSE 일경우
통계정보가 없다면 기본적으로RBO로 플랜이 결정된다. 그러나, ‘RULE’, ‘DRIVING_SITE’힌트 이외의 힌트가 왔다면CBO로 결정된다(힌트는 룰 의 규정을 깨므로CBO로 동작됨).
• Parallel Degree, Partition Table, SAMPLE절등이있으면무조건CBO
• OPTIMIZER_MODE=First_rows|First_rows_n|All_rows일경우통계 정보의 존재 여부와 관계 없이 무조건CBO로 처리하려고 함.
통계정보가 없다면 Heuristics Value를 이용하거나, Oracle9i Database 이상일 경우는 다이나믹 샘플링의 레벨에 따라 테이블의 데이타를 샘플링 해서CBO로 플랜이 결정된다. 그러나, 플랜이 비효율적일 수 있다.
• 통계정보가 있으나 옵티마이저 모드가 RULE일 경우, 다른 힌트가 오지 않 은 경우와 Parallel Degree, Partition Table, SAMPLE절 등이 나오지 않 은경우는RBO로 처리된다.
CBO를 위한 통계정보 운영 방법
통계정보는 CBO의 플랜 결정에 사용되는 객체들의 물리적인 구성정보를 나타낸다. 즉, 테이블이 몇 블록으로 구성되어 있으며, 몇 건의 로우들을 가지고 있으며, 평균 로우 길이는 어느 정 도이며, 칼럼의 Min/Max 값의 분포, Distinct 값, 인덱스의 레벨, 키(key)당 Leaf Block 수 등의 정보들을 나타낸다. 이들 정보는 CBO의 플랜 결정의 기초 자료로 사용된다. 이들 통계정보를 생성하기 위해서는 ANALYZE 명령어를 이용하거나 DBMS_STATS 패키지를 이용하면 된다. 그러나 2개의 차이점에 주의해야 하며, DBMS_STATS를 지원하는 Oracle8i Database 이상부터는 DBMS_STATS를 사용하기를 권장하고 있다.
Analyze 명령어와DBMS_STATS의 차이점
● Analyze는 파티션의 통계정보를 각 파티션 테이블과 인덱스에 대해서 수집하 고, Global Statistics는 파티션 정보를 가지고 계산하므로, 비정확할 수 있다. 그러므로 파티션 또는 서브파티션이 있는 객체에는DBMS_STATS을 사용하 여야 한다.
● DBMS_STATS은 전체 클러스터에 대해서는 통계정보를 수집하지 않는다. 그 러므로 Analyze를 사용한다.
● DBMS_STATS은 CBO와 관련된 통계정보만을 수집한다. 즉, 테이블의 EMPTY_BLOCKS, AVG_SPACE,CHAIN_CNT 등은 수집되지 않는다.
● DBMS_STATS은 사용자가 지정한 통계정보 테이블에 수집된 통계정보를 저 장할수있고, 딕셔너리로 각 칼럼, 테이블, 인덱스, 스키마등을반영할수있다.
● DBMS_STATS은IMPORT/EXPORT 기능 및 추가적인 기능이 많다. 이기능 을 이용하여 운영 DB의 통계정보를 개발장비의 통계정보로 복사할 수 있으므로 개발장비의 플랜을 운영장비와 같 게 만들 수 있다(매뉴얼 참조).
다음은 Analyze 명령어에만 있는 기능이다.
analyze { index/table/cluster } (schema.){ index/table/cluster } validate structure (cascade) (into schema.table);
• Chained Rows 수집 기능
ANALYZE TABLE order_hist LIST CHAINED ROWS INTO
시스템 통계정보 (>= Oracle9i Database)
시스템 통계정보는 객체의 통계정보와 같이 사용되는 정보로서, 기존의 Oracle8i Database까지의 I/O 중심의 플랜 방식에 CPU와 디스크 I/O 속 도와 같은 시스템 자원의 효율을 반영하여 보다 효율적인 플랜을 결정하기 위한 방법으로, Oracle9i Database에서 처음 소개되었으며, 옵션 기능으로 DBA에 의해 사용될 수도 있고 사용하지 않을 수도 있었다. Oracle Database 10g에서는 시스템 통계정보가 기본적으로 수집되고 사용된다. 그 러므로 기존의 I/O 횟수만 가지고 플랜을 결정하던 부분이 Oracle Database 10g에서는 시스템의 자원의 성능도 고려되어 보다 정확한 플랜을 결정할 수 있게 되었다.
이들 시스템 통계정보는 DBMS_STATS 패키지를 이용해서 수집된다.
Oracle Database 10g에서는 기본적으로 수집되는 값들이 있으며, 또한 사용자가 수집해야 하는 항목도 있다. 다음은 Oracle Database 10g의 시 스템 통계정보의 수집형태를 보여주고 있다.
옵티마이저 원리에 바탕한 SQL 튜닝
지금까지 설명한 바와 같이 개략적으로나마 옵티마이저의 원리를 이해하기 위한 부분에 초점을 맞춰 설명하였다. 더 자세한 부분이 필요하면 오라클 매뉴얼인 [Database Performance Tuning Guide and Reference]를 권장하고 싶다. 다음 호엔 오라클이 사용하는 조인 방법들와 오라클이 제공하는 SQL 튜닝 방법에 대해 알아보도록 하자.
http://www.dbguide.net/blog/post/post_view.jsp?urlid=iparkhome&pnum=10393
출처명: 한국오라클
Partitioned hash-join 참고자료
1 Partitioned Hash-Join
|
|
The process of performing partitioned hash-join can be divided into two steps. During the first step, both source relations are partitioned into separate clusters according to the value of a hash function calculated over the join attribute. In the second, corresponding clusters are joined using any traditional join method - Figure 3.9. This solution proves effective when number of clusters is selected in such a way, that they are small enough to fit into cache memories.
Hash Join의 특징 및 사용기준
more..
대량의 데이터를 액세스하는 작업에서는 시스템 리소스를 많이 사용하는 대신 짧은 시간에 보장할 수 있어야 한다. 다음은 수행방법을 살펴보자
1. 두 테이블 중 적은 테이블을 선행 테이블로 결정한다.
2. 선행 테이블을 Hash Function을 이용하여 Hash Area에 Hash Table을 구성한다.(Build Input)
3. 만약 Hash Area만으로 생성 가능하다면 후행테이블은 크기에 상관없이 차례로 Hash Function을 이용하여 Hash Table과 조인(Probe Input)하면서 성공한 결과값을 운반단위로 이동한다.
4. 만약 Hash Area만으로 Hash Table 생성이 불충분 하다면 Hash Table Overflow가 발생하여 데이터를 나눠서 저장 할 Partition 수를 결정한다.(Fan -out)
5. 선행 테이블의 조인 컬럼과 Select List 컬럼을 메모리로 읽어 들여 첫번째 해쉬 함수를 이용하여 Partition을 Mapping하고, 두번째 해쉬 함수를 이용하여 해쉬 테이블 생성시 사용 할 해쉬 값을 생성한다.
6. 선행 테이블의 조인 컬럼의 유일 값만으로 Bit-Vector을 생성한다. -추후 Bit-Vector filtering에 사용하기 위함.
7. Partition에 데이터를 MOVE하고 채워진 Partition은 디스크로 내려간다.
8. 선행 테이블이 모두 읽혀지면 Partition 테이블을 완성하고, Partition 크기순으로 정렬한 후 작은 Partition N개를 메모리에 로드한다.
9. 후행 테이블을 읽으면서 조인컬럼으로 Bit-Vector와 Filtering에 성공하였다면, 첫번째 해쉬함수로 Partition을 결정하고, 두번째 해쉬 함수를 이용하여 메모리 상에 있는 선행테이블과 조인하고 성공하면 운반단위로 이동하고, 해당 Partition이 메모리에 존재하지 않는다면 해쉬 키값,조인컬럼, Select List를 디스크에 쓴다.
10. 후행 테이블이 모두 읽혀지면, Bit-Vector Filtering에 성공했지만, 조인에 성공하지 못해 미 처리된 선행 파티션과 후행 파티션을 메모리에 올려 차례로 반복수행 한다.
액세스해야 할 데이터가 많을 경우 Sort Merge Join은 Merge 단계에 들어 가기 위해 양쪽 테이블의 처리 범위가 SORT 되어야 하므로 SORT에 대한 부담이 크고, Nested Loop Join은 선행 테이블의 처리 범위가 넓을 경우 그 만큼 랜덤 액세스의 발생으로 수행속도를 보장할 수 없다.이해 반해 Hash Join은 다른 조인에 시스템 리소스를 가장 많이 사용하지만 Hash Function을 이용함으로써 Sort를 하지 않고, 각 테이블에 한번만 액세스하여 조인이 이루어 지므로 큰 테이블간이나, 큰 테이블과 적은 테이블의 조인에 효율적인 조인 방법이다. Hash Join의 특징 및 사용기준은 다음과 같다.
특징
1. 다른 테이블의 결과 값을 제공받지 않고, 자신에게 주어진 조건으로만 처리범위를 결정한다.(독립적).
2. 해쉬 함수를 이용 하게 되므로 부분범위 처리를 할 수 없으며 전체범위 처리를 하게 된다.(전체범위 처리)
3. 메모리 영역만으로 해쉬 테이블을 생성시 최적의 효과를 낼 수 있으므로 적은 테이블이 선행으로 온다.
4. 해쉬 함수를 이용하므로 결과값의 정렬을 보장 받을 수 없다.
사용기준
1. 대량의 데이터 액세스 시, 배치 처리, Full Table Scan 하면서 조인 해야 할 때 유리하다.
2. 비용은 많이 들지만 수행속도를 보장해야 하는 작업에 유용하다.(Parallel Query 사용)
3. 가능한 메모리 내에서 작업 가능하도록 init Parameter나 Session 정보를 변경하여 사용한다. (Hash_area_size, Hash_multiblock_io_count 등..)
수행속도는 시스템 리소스와 즉결된다. 사용자는 작업의 성격을 분명히 하고, 작업에 적합한 조인을 선택해야만 수행 속도를 보장할 수 있다. SQL작성 후 반드시 실행계획를 확인 하여 사용자가 원하는 액세스로 수행되는지를 확인하는 습관을 들어야 한다.
http://database.sarang.net/?inc=read&aid=541&criteria=dbms&subcrit=&id=&limit=20&keyword=&page=1
- 참고자료 -
계속 추가 ..
2009년 1월 16일 금요일
MATERIALIZED VIEW
작성날짜 : 2004-08-13
MATERIALIZED VIEW 활용방법
==========================
PURPOSE
이 문서는 Oracle 8i에서 materialized view 및 query rewrite를 사용하는
방법을 간략히 소개하고 있다.
SCOPE
8i~9i Standard Edition에서는 지원하지 않는다.
Explanation
1. MATERIALIZED VIEW
Materialized View(이하 MVIEW)는 DW 환경이나, replication 환경에
유용한 기능으로, inner-join, outer-join, equi-join 등 각종 view를
수동으로 갱신하거나, 주기적으로 자동 갱신을 할 수 있게 해 준다.
원격 데이터베이스의 테이블이 포함된 MVIEW는 양방향 replication을
지원한다. 또한 MVIEW는 사용자에게는 투명하게 cost optimization에
기반을 둔 qurey rewrite 기능을 제공한다. Query rewrite 기능을
제공하기 위해 Oracle 에서는 Dimension이라는 객체를 추가 했는데,
Dimension 객체는 대용량 데이터에 대한 질의를 집계성 데이터에 대한
질의로 자동 변환 해 주는 기능을 제공해 준다.
MVIEW는 질의 실행을 할 때마다 매번 대량의 join이나, aggregation
연산을 수행하지 않고, 미리 계산된 값을 질의하기 때문에 성능 향상을
가져올 수 있으며, optimizer는 MVIEW가 어느때 사용되는 것이
적절할지를 판단할 수 있게 설계되었다.
Query rewrite는 사용자에는 투명하다. 만약 환경이 적절히
셋업 되어 있다면, 대량 대이터에 대한 복잡한 질의 응답 속도를
획기적으로 개선할 수 있게 한다.
2. MVIEW 관련 파라미터
MVIEW와 관련된 파라미터 목록은 다음과 같다.
optimizer_mode
query_rewrite_enabled
query_rewrite_integrity
compatible
1) optimizer_mode
Query Rewrite 기능을 사용하기 위해서는 init.ora 파일의 optimizer
mode값은 "ALL_ROWS" 나 "FIRST_ROWS"로 지정하거나, "CHOOSE"인 상태에
서 모든 테이블을 ANALYZE 시켜 두어야 한다.
2) query_rewrite_enabled
파라미터 query_rewrite_enabled 의 값은 "TRUE"로 지정한다.
3) query_rewrite_integrity
파라미터 query_rewrite_integrity 는 선택적으로 지정할 수 있는
파라미터이지만, "STALE_TOLERATED", "TRUSTED", 또는 "ENFORCED"
으로 지정되어야 한다. 이 파라미터는 query rewrite의 정확성을 제어
하는 파라미터이다.
각각의 의미는 다음과 같다
TRUSTED : optimizer에서 MVIEW의 데이터가 정확하다고
간주하고 질의 수행. Integrity 확인을 하지않음.
ENFORCED: query_rewrite_integrity 의 기본값으로,
사용자가 integrity constraint를 확인하여야
한다. MVIEW는 fresh한 데이터를 포함하여야 한다.
STALE_TOLERATED : Optimizer에서 데이터가 stale 상태이거나
fresh 상태인 경우 모두 MVIEW 사용
3. MVIEW 사용에 필요한 권한
MVIEW를 사용하기 위한 권한은 base 테이블에 대한 사용자의 권한에
달려있다. 두개의 중요한 시스템 권한은 다음과 같다.
grant rewrite
grant global rewrite
1) grant rewrite
MVIEW의 base table이 모두 사용자 자신의 테이블일 경우,
자신이 선언한 MVIWE 사용 가능.
2) grant global rewrite
사용자가 어느 schema에 속한 MVIEW라도 사용 가능.
3) MVIEW 사용에 필요한 권한이 충족된 경우 다음 조건을 만족하여야 한
다.
a. 세션에 query rewrite 기능이 enable 되어 있음.
b. MVIWE 자체가 enable 되어 있음.
c. integrity level이 적절히 셋업 되어 있음.
d. MVIEW에 데이터가 존재함.
Example
다음과 같은 테이블이 있을 때,
Dealer (dealer_num, dealer_name, dealer_city, dealer_state)
Automobile (auto_num, auto_name, auto_year)
Shipping (shipping_num, shipping_day, shipping_month, shipping_time)
Summary (dealer_num, auto_num, shipping_num, auto_value)
MVIEW 생성
Create Materialized View test_mv
as
select d.dealer_num, d.dealer_name, v.auto_value, s.shipping_num,
s.shipping_day, v.dealer_num, v.rowed, s.rowid
from summary v, dealer d, shipping s
where v.shipping_num = s.shipping_num
and v.dealer_num = d.dealer_num
4. Query rewrite에서 MVIEW 사용 여부 판단 알고리즘
1) Full SQL Text Match
질의의 select 문장과 MVIEW를 만들때 사용한 select 문장 비교
2) Partial SQL Text Match
Full SQL Text Match가 실패할 경우 select 문장의 from 절 이하의
내용이 MVIEW를 만들때 사용한 내용과 일치하는지 비교
3) Generla Query Rewrite Method
1, 2 항에서 실패할 경우, optimizer에서 MVIEW 사용 가능 여부를 판단.
필요한 데이터가 MVIWE에서 제공하는 것 보다 적거나, 많거나, 변환 가능
한지를 판단하고, MVIWE 데이터가 충분한지 여부를 joing compatibility,
grouping compatibility, aggregate compatibility 등을 확인하여 판단
5. MVIEW와 Integrity Constraints
MVIEW는 DW 환경에서 유용하게 사용될 수 있는데, 대부분의 DW는
integrity constraint를 사용하지 않는다. 즉 DW는 원천 데이터에서
integrity가 보장되었다고 간주한다.
다른 한편으로 integrity constraint는 query rewrite에 유용하다.
이 모순되는 사항은 NOVALIDATE 와 RELY 옵션을 이용해 조율을 맞추어야 한다.
1) query_rewrite_enabled = enforced
데이터베이스의 constarint는 validate 상태로 두어야 한다.
2) query_rewrite_enabled = stale_tolerated | trusted
데이터베이스의 constraint를 nonvalidate, rely로 지정 해 준다.
6. Query Rewrite와 Hint 사용
Index 관련 Hint를 사용하는 것 처럼, query rewite 관련 Hint를 사용하여
제어할 수 있다.
NOREWRITE :
Select /*+NOREWRITE*/...
REWRITE:
Select /*+REWRITE(mv1)*/...
7. MVIEW 사용 예제
1) Full SQL Text Match
Select d.dealer_name, shiping_month, a.autonum, sum(v.auto_value) as sum_sales,
count(v.auto_value) as count_values
from summary f, dealer d, shippings, automobile a
where v.shipping_num = s.shipping_num
and v.dealer_num = d.dealer_num
and v.auto_num = a.auto_num
group by d.dealer_name, shipping_month, a.auto_num
위 SQL 문은 다음과 같이 미리 생성된 MVIEW를 이용하도록 rewrite 될 수 있다.
select dealer_name, shipping_month, auto_num, sum_value, count_value
from <MVIEW명>
2) Partial SQL Text Match
Select d.dealer_name, shiping_month, a.autonum, avg(v.auto_value0 as avg_sales,
count(v.auto_value) as count_values
from summary f, dealer d, shippings, automobile a
where v.shipping_num = s.shipping_num
and v.dealer_num = d.dealer_num
and v.auto_num = a.auto_num
group by d.dealer_name, shipping_month, a.auto_num
위 SQL 문장은 다음과 같이 미리 생성된 MVIEW를 사용할 수 있다.
select dealer_name, shipping_month, auto_num, sum_values/count_values as avg_values
from test_mv
Reference Documents
<note: 106024.1>
http://kr.forums.oracle.com/forums/thread.jspa?threadID=453711
Instead Of Trigger
Instead Of Trigger 를 설정하면 트리거가 DML 작업을 하는것이 아닌, 해당 이벤트 발생시 작성한
Instead Of Trigger 로 이동하여 그 안에 정의된 방식으로 DML 을 처리한다.
사용예 (Beginning Oracle Programming 예제) :
INSTEAD OF
update
on company_phone_book
begin
update employees ....
....
end;
설정후 해당 뷰에서 name 에 대한 update 발생시 트리거가 방생하고, 정의한 함수에서 이를 대신 처리하게 된다.
Instead Of Trigger 는 뷰에서 제약이 따르는 복잡한 DML 을 처리할때 유용하다.
ORACLE & ALTIBASE Starting UP Database
- 오라클 구동 단계 (ORACLE 9i ) -
1. NOMOUNT
1. spfileSID.ora
2. spfile.ora
3. initSID.ora
2) SGA 를 할당한다.
3) background process 를 구동시킨다 ( 인스턴스 구성 )
4) alertSID.log 파일에 기록한다.
2. MOUNT
2) control file 을 읽어서 이름과 데이터파일의 상태 , redo log file 을 확인한다.
( 경로만 확인 하지 존재여부와 내용의 수행 증명은 하지 않는다.)
* MOUNT 시에는 다음과 같은 수행이 가능하다.
1. data file 의 이름을 변경한다.
2. online redo log file archiving 옵션을 변경할수 있다.
3. 데이터베이스 전체 복구
3. OPEN
2) control file 에서 명시된 인스턴스들이 이때 Open 된다. (rodo log , datafile ...)
만일 파일이 없다면 OPEN 되지 않는다.
3) online redo log file 를 읽고 roll forward 를 발생 시킨다. ( commit 후 반영되지 않은 데이터 )
4) 마지막에 오라클은 data file 과 online redo log 파일의 무결성을 확인한다. 필요하다면
background process 는 인스턴스를 복구 시킨다. ( rollback )
- ORACLE STARTUP -
[OPEN [RECOVER] [database]
| MOUNT
| NOMOUNT ]
[FORCE] - 재시작 (shutdown abort + startup)
[RESTRICT] - 제한된 세션만 로그인
[PFILE-filename] - PFile 경로 직접지정
[OPEN RECOVER] - startup mount -> recover database -> alter database open ( 유실된 자료 복구 )
- 알티베이스 구동 단계 - ( 오라클과 방식은 유사함 )
2. Process - create database 및 프로퍼티 를 조회하고 변경할수 있는 단계
3. Control - database 파일을 로드하고 recovery 준비 단계
4. Meta - recovery 가 수행되며, meta data upgrade 가능, 온라인 로그 를 Reset 가능 단계.
5. Service - 사용자 로그인 가능
2009년 1월 15일 목요일
오라클 옵티마이저의 기본 원리
오라클 옵티마이저의 기본 원리
이상원 | (주)엑셈 연구소장 겸 성균관대학교 교수
현재 모든 관계형 DBMS에서는 사용자의 SQL
질의를 효율적으로 처리하기 위해 옵티마이저를 사용하고 있다. 개발자나 관리자들이 이 옵티마이저의 기본 동작 원리를 이해한다면,
여러 면에서 도움이 되리라는 생각에 이 글을 쓰게 되었다. 먼저, 옵티마이저에 대한 기본적인 이해를 구한 다음, 오라클
옵티마이저에 대해 알아보도록 한다.
본론에 앞서, 이 글은 관계형 데이타베이스 개념에 일정 정도 익숙한 독자들을
대상으로 작성되었기 때문에, 초보자는 이해하기가 난해할 수도 있다는 점과 오라클 옵티마이저가 워낙 광범위한 기술이라 모든
세부적인 내용을 다 다룰 수는 없으며, 필자도 오라클 옵티마이저의 모든 내부 동작 원리를 완전히 이해하고 있지는 못하다는 점에
양해를 바란다. 미진하고 잘못된 부분은 모두 필자의 책임임을 밝혀 둔다.
관계형 DBMS와 옵티마이저
21세기에 들어서도 여전히 관계형 DBMS가 데이타베이스 시장을 지배하고 있다.
그
리고, 관계형 DBMS에서 사용되는 핵심 언어는 SQL(Structured Query Language)이다. 이 SQL 언어의
가장 큰 특징은 사용자가 데이타베이스에서 자신이 원하는 데이타(What)만 지정하면, 그 데이타를 어떻게 구하는가(How)는
DBMS가 자동적으로 결정해서 처리해 준다는 점이다.
이런 면에서 SQL을 선언적(declarative) 언어(주
1)라고 부르며, 사용자는 데이타베이스의 물리적 구조의 변경에 상관 없이 항상 원하는 정확한 결과 데이타를 구할 수 있다.
이러한 물리적 데이타 독립성(physical data independence)이 관계형 DBMS를 상업적 성공으로 이끈 가장 큰
이유 중의 하나이다.
아무리 SQL이 이와 같은 장점을 갖고 있더라도, DBMS가 내부적으로 질의를 처리하는 방식이
비효율적이라면 관계형 DBMS는 누구도 사용하지 않을 것이다. 다행히도 현재의 모든 관계형 DBMS는 사용자의 SQL 질의를
효율적으로 수행하는 방법을 찾아내는 옵티마이저(Query Optimizer : 혹자는‘질의 최적화기’라고도 부르는데 이
글에서는‘옵티마이저’라 부르겠다.)를 제공하고 있다. 예를 들어, 다음과 같은 간단한 SQL 문을 보자.
Q1 : select ename, sal
from emp e, dept d
where e.deptno = d.deptno and d.loc = ‘SEOUL’
emp와 dept는 각각 deptno와 loc 칼럼에 대해 B 트리 인덱스가 있다고 가정한다. 이 같은 단순한 질의 Q1의 경우에도 질의 결과를 구하는 방법, 즉 실행 계획 (execution plan)은 다양할 수 있다.
< 그림 1>은 두 가지 실행 계획 P1과 P2를 보여주고 있다. P1은 우선 loc =‘SEOUL’조건을 만족하는 dept 레코드를 인덱스를 이용해서 찾고, 각 dept 레코드에 대해 eptno 값이 일치하는 emp의 레코드를 인덱스를 이용해서 찾아 값을 출력한다(Nested Loop 조인 방법 이용).
한 편, P2는 emp/dept 테이블을 Full Table Scan해서 이들을 Sort Merge 조인방식으로 조인해서 질의 처리를 수행한다. 주목할 점은, 이 두 실행 계획 모두 정확한 질의 결과를 구하지만, 두 방식의 수행 시간에는 차이가 많이 날 수도 있다는 점이다. 예를 들어, P1 방식은 1초에 원하는 결과를 구하는 반면, P2 방식은 1시간이걸릴 수도 있다.
P1, P2 이외에도 질의 Q1을 수행할 수 있는 많은 실행 계획이 있을 수 있다. 실행 계획이란, 여러 개 테이블들의 조인에 대해, 특정한 1) 조인 순서(join ordering), 2) 조인 방법(join method), 그리고 3) 테이블 액세스 방법(access method)을 선 택하는 것이다.
옵티마이저는 가능한 실행 계획들을 모두 검토하고, 이 중에서 가장 효과적으로, 즉 가장 빨리, Q1의 결과를 구할 수 있는 실행 계획을 결정한다. 이 글에서는, 옵티마 이저가 최적의 실행 계획을 찾는 과정을‘질의 최적화(Query Optimization)’또 는 단순히‘최적화’라고 부르겠다.
관계형 DBMS 옵티마이저의 핵심 기능
관계형 DBMS 옵티마이저의 핵심 기능은 다음과 같다.
? 실행 계획 탐색(Search Space Enumeration) : 주어진 SQL 질의를 처리할 수 있는 실행 계획들을 나열(P1, .., Pn) ?
? 비용 산정(Cost Estimation) : 각 실행 계획의 예상 비용을 계산 많은 실행 계획들 중에서 최종적으로 가장 비용이 적게 드는 실행 계획 Pi를 선택해서 SQL을 실행하고 결과를 사용자에게 보여 준다. 다.
실행 계획 탐색
예를 들어, 3개의 테이블, T1, T2, T3에 대해 조인을 수행하는 SQL 문이 있다고 가정하자. 그럼 이 질의를 수행할 수 있는 가능한 실행 계획은 몇 가지일까? 우리는 앞에서 조인 순서, 조인 방법, 그리고 테이블 액세스 방법에 따라 서로 다른 실행계획이 만들어진다고 했다. 그렇다면, 3개의 테이블 T1, T2, T3에 대한 조인 순서는 3!, 즉 6개의 조인 순서가 있다.
(T1§_T2)§_T3, (T1§_T3)§_T2, (T2§_T1)§_T3, (T2§_T3)§_T1, (T3§_T1)§_T2, (T3§_T2)§_T1
그리고, 하나의 조인 순서에는 2개의 조인을 포함하는데, 이용 가능한 조인 방법이
Nested Loop, Sort Merge, Hash Join의 세 가지가 있다면, 각 조인 순서에 대해 총 32, 즉 9개의
조합이 가능하다. 그리고, 이 각각의 경우 테이블을 접근하는 액세스 방법이 Full Table Scan과 Index Scan의
두 가지가 있다면 23, 즉 8개의 서로 다른 조합이 가능하다.
따라서, 3! x 32 x 23 = 432가지의 실행
계획이 가능하다. 그런데, 옵티마이저가 고려해야 할 실행 계획의 개수는 SQL에 포함된 테이블의 개수가 증가함에 따라
기하급수적으로 늘어나게 된다. 만일 from 절의 테이블의 개수가 5개인 경우, 5! x 35 x 25 = 933,120개가
가능해진다.
그리고, 여기서 각 실행 계획의 예상 비용을 계산하는 데 걸리는 시간이 0.01초라고 가정했을 때, 모든
실행 계획의 예상 비용을 구하는 데 약 9,300초(약 2시간 36분)이 걸린다. 만일 테이블의 개수가 10개라고 가정하면,
아마도 모든 실행 계획의 예상 비용을 계산하는 데만도 몇 년이 걸릴지도 모른다.
21세기 IT 환경에서는 하나의 SQL 문에 5 ~ 10개 정도의 테이블이 포함되는 경우가 일반적이다. 그런데, 옵티마이저가 실행 계획을 선정하는 데 걸리는 시간이 이와같다면, 옵티마이저는 차라리 없는 것이 더 나을지도 모른다.
따
라서, 옵티마이저는 모든 가능한 실행 계획을 다 고려할 수는 없다. 즉, 질의 최적화에 걸리는 시간을 줄이기 위해 어떤 실행
계획들은 아예 비용 계산에서 제외해야 할 필요도 있다. 옵티마이저는 모든 가능한 실행 계획 조합들을 탐색하는 방법 - 즉 어떤
실행 계획을 먼저 고려하고, 어떤 순서로 다음 실행 계획을 찾고, 어떤 실행 계획은 제외할 것인가? - 을 갖고 있어야 한다.
비용 산정
앞의 실행 계획 탐색 단계에서 만들어내는 각각의 실행 계획에 대해, 그 실행 계획을 실제로 수행할 때 비용 - 단순하게는 시간이 얼마나 걸릴지? - 을 예측해서, 가장 비용이 적은 실행 계획을 선택해야 한다.
이를 위해서 옵티마이저는 데이타베이스 내의 데이타들에 대해 갖고 있는 통계정보와 비용을 예측하는 다양한 모델을 사용해서 각 실행 계획의 비용을 계산할 수 있어야 한다.
여
기서 주목할 점은, 옵티마이저가 실행 계획들을 비교할 때 사용하는 기준은‘예상비용’이라는 점이다. 앞의 예에서 P1과 P2
방법을 실제로 수행해 보고 더 좋은 방법을 결정하는 것이 아니라, 옵티마이저가 갖고 있는 통계정보를 활용해서 P1과 P2로
수행했을 때 어느 실행 계획의 예상 비용이 작은가를 보고서 이를 실제로 수행하게 되는 것이다.
Selinger 스타일의 옵티마이저
필자가 아는 한에서, 현재의 모든 상용 관계형 DBMS의 옵티마이저는 IBM
DB2의 모태인 System-R 프로토타입 시스템을 개발할 당시에 고안된 아키텍처에 기반하고 있다<참고자료 4>. 이
아키텍처를 주도적으로 제안한 IBM의 여성 전산학자 Pat. Selinger의 이름을 따서, ‘Selinger 스타일
옵티마이저’라 부른다.
참고로 Pat. Selinger의 논문은 아직까지도 데이타베이스 분야에서 가장 많이 인용되는 논문 중의 하나이고, Pat. Selinger는 이 한 편의 논문으로 데이타베이스 연구 분야에서 슈퍼스타의 반열에 올라섰다.
이
Selinger 스타일 옵티마이저 아키텍처의 가장 큰 두 가지 특징은 1) 동적 프로그래밍 기반에 의한 실행 계획
탐색(Search Space Pruning based on Dynamic Programming)과 2) 비용 기반
최적화(Cost-Based Optimization)인데, 각각 위에서 나열한 옵티마이저 핵심 기능의 첫째, 둘째 기능에
해당된다.
옵티마이저의 이상과 현실
가장 이상적인 옵티마이저는, 모든 질의에 대해 옵티마이저가 선택한 실행 계획이 실제로 수행될 때도 가장 좋은 수행 속도를 보장하는 경우이다.
그
러나, 현재의 옵티마이저는 비록 대부분의 경우에 상대적으로 아주 좋은 실행 계획<주 2>을 선택하지만, 실제로는 아주
나쁜 실행 계획을 선택하는 경우도 있다. 현재의 옵티마이저의 한계, 원인 그리고 앞으로의 개선 방향에 대해서는 뒤에서 자세히
설명하겠다.
옵티마이저는 30년 이상 축적된 기술을 포함하고 있는, 인간의 지능이 가장 많이 녹아 있는 복잡한
소프트웨어이다. 이 세상의 어떤 누구도 상용 관계형 DBMS 옵티마이저의 복잡한 내부 동작 원리를 완전히 이해하고 있는 사람은
없다고 단언할 수 있다.
실제로 필자도 이 글에서 주로 설명할 오라클 옵티마이저에 대해서 어쩌면 기본적인 지식밖에
없다고 할 것이다. 다만, 많은 오라클 개발자나 관리자들이 현대의 옵티마이저의 가장 기본적인 동작 원리를 쉽게 이해했으면 하는
마음으로 이 글을 썼다.
옵티마이저의 아키텍쳐
이제까지는 현재 보편적으로 사용되고 있는 관계형 DBMS 옵티마이저의 기능, 간단한
역사적 배경, 그리고 동작 원리에 대해 간략히 알아보았다. 이제부터는 이러한 배경 지식을 바탕으로 Oracle DBMS의
옵티마이저의 기본 구조와 동작 원리에 대해 알아보겠다.
오라클은 RBO(Rule-Based Optimization :
규칙기반 최적화)와 CBO(Cost-Based Optimization : 비용기반 최적화)를 모두 지원하고 있다. CBO의
경우, 1992년 Oracle 버전 7부터 도입되었는데, 향후 이 글에서 오라클 옵티마이저라 함은 CBO를 지칭하는 것이다.
RBO는 간단한 규칙 위주로 최적화를 수행하는 방법으로, 앞으로는 널리 사용되지 않을 것이며, 오라클도 공식적으로 CBO 사용을
권장하고 있다.
<그림 2>는 오라클 옵티마이저의 아키텍처를 보여주고 있는데, 사용자의 SQL 질 의는 크게 다음 4단계를 거쳐서 수행된다.
1. 파싱(Parser)
2. 옵티마이저(Query Optimizer)
3. 로우소스 생성(Row Source Generator)
4. SQL 실행(SQL Execution Engine)
파싱(Parser) 단계는 SQL은 구문(syntax)과 의미(semantics) 검사를 수행한다.
예를 들어, SQL 구문이 정확한지를 검사하고, 참조된 테이블에 대해 사용자의 접근 권한 등을 검사한다. 이 단계가 끝나면, SQL 문은 파싱 트리(parsed tree) 형태로 변형되어 옵티마이저에게 넘겨진다.
옵티마이저(Query Optimizer) 단계는 앞에서 넘겨받은 파싱 트리를 이용해서 최적의 실행 계획을 고른다.
<그림 2>에서 점선 형태의 사각형으로 표시된 부분이 옵티마이저의 주요 구성 요 소를 보여주고 있는데, 뒤에서 각 구성 요소의 역할에 대해 자세히 설명하겠다.
로
우소스 생성(Row Source Generator) 단계는 옵티마이저에서 넘겨받은 실행 계획을 내부적으로 처리하는 자세한 방법을
생성하는 단계이다. ‘로우 소스’란 실행 계획을 실제로 구현하는 인터페이스 각각을 지칭하는 말로, 테이블 액세스 방법, 조인
방법, 그리고 정렬(sorting) 등을 위한 다양한 로우 소스가 제공된다. 따라서, 이 단계에서는 실행 계획에 해당하는 트리
구조의 로우 소스들이 생성된다.
마지막으로, SQL 실행(SQL Execution Engine) 단계는 위에서 생성된 로우 소스를 SQL 수행 엔진에서 수행해서 결과를 사용자에게 돌려주는 과정이다.
여
기서 한 가지 주목할 점은, 소프트 파싱(soft parsing)과 하드 파싱(hard parsing)은 크게 옵티마이저 단계의
포함 여부에 따른 차이이다. 즉, 소프트 파싱은 이미 최적화를 한 번 수행한 SQL 질의에 대해 옵티마이저 단계와 로우 소스
생성 단계를 생략하는 것이고, 하드 파싱은 이 두 단계를 새로 수행하는 것이다. 따라서, 하드 파싱은 통계정보 접근과 실행 계획
탐색 때문에 시간이 많이 걸린다. 이 차이가 주로 SQL 튜닝 전문가들이 가급적이면 하드 파싱을 피하라고 권하는 이유이다.
오라클 옵티마이저의 동작 원리
이제부터는 오라클 옵티마이저의 각 구성 요소의 기능에 대해 좀 더 자세히 알아 보자. <그림 2>에서 보듯이, 오라클 옵티마이저는 크게 다음 3가지 모듈로 구 성된다.
? 질의 변환(Query Rewriter)
? 실행 계획 생성(Plan Generator)
? 비용 산정(Estimator)
질의 변환 모듈
질의 변환(Query Rewriter 또는 Transformer) 단계는 파싱 트리(parsed tree) 를 받아들여서 질의 변환을 수행한다. 이 변환 과정을 통해서 의미적으로 같은 결과 를 수행하지만, 더 나은 실행 계획을 찾을 수 있는 SQL 문으로 변환함으로써 질의 의 수행 처리 속도를 높이는 데 그 목적이 있다. 오라클 옵티마이저가 수행하는 질 의 변환은 크게 다음 두 종류로 구분할 수 있다.
? 휴리스틱(Heuristic based) 질의 변환 : 이
변환의 종류로는 크게 View Merging, Subquery Unnesting, Predicate Push Down,
Partition Pruning 등이 있는데, 이들 변환은 가능한 경우에 항상 질의 변환을 수행 한다. 왜냐하면, 이와 같은
변환은 경험적으로 거의 항상 원래 질의보다 더 빠른 수행 속도를 보장하기 때문이다.
? 비용 기반(Cost based) 질의 변환 : 이
변환의 예로는, MV Rewrite, Star Query Transformation, OR-expansion 등을 들 수 있다.
그런 데, 이 방법을 사용해서 변환된 SQL 문이 원래 SQL 문보다 속도가 더 빠르다는 보장이 없다. 따라서, 변환 전/후의
두 SQL 문에 대해 각각 최 선의 실행 계획을 구하고, 이들의 비용을 비교해서 더 효율적인 실행 계획 을 최종적으로 선택한다.
오라클의 질의 변환 모듈에서 지원하는 다양한 변환의 종류와 내용에 대해서는 <참고자료 2, 3>을 참고하기 바란다.
질
의 변환 단계가 끝나면, 오라클 옵티마이저는 실행 계획 생성과 비용 산정 모듈을 수행하기 앞서, 질의에서 사용된 모든 테이블들과
각 테이블에 정의된 인덱스들에 관한 기본적인 통계정보들(예를 들어, 테이블의 블록 개수, 로우 평균 길이, 인덱스 의 높이,
인덱스 리프 블록의 개수 등)과 각 테이블에 대한 다양한 액세스 경로(예 를 들어, Full Table Scan, Index
Scan 등)에 대한 비용 정보를 미리 구해 둔다.
옵티마이저의 아키텍쳐
실행 계획 생성 모듈
이 모듈은 옵티마이저가 새로운 실행 계획을 만드는 것이다. 오라클 옵티마이저는 제일
먼저 각 테이블의 레코드 수를 기준으로 오름차순으로 결정한다. 예를 들어, SQL 질의의 from 절에서 T1, T2, T3
순서로 참조한 경우, 각 테이블의 카디널러 티(cardinality : 테이블의 튜플 수)가 T1 > T2 > T3
순이라면 제일 처음 고려하 는 조인 순서는 (T3§_T2)§_T1 이 된다. 이 조인 순서에 대해서 다음 단계인 비용 산정
모듈을 호출해서 이 조인 순서에 따르는 실행 계획들과 각 실행 계획의 비용을 구한다.
그리고, 더 이상의 새로운 조인
순서가 없을 때까지 계속 새로운 조인 순서를 만들 어서 비용을 계산한다. 이 모듈은 지금까지 찾아낸 가장 좋은 실행 계획과 그
비용 을 저장하고 있다. 이 단계는 최종적으로 구해진 최적의 실행 계획을 | 그림 2 |의 로우 소스 생성 단계에 넘겨준다.
오
라클의 실행 계획 생성 모듈은 테이블 개수가 정해진 값(디폴트 5)보다 작은 경우 에는 모든 조인 순서에 대해 고려하지만,
테이블 개수가 이 값을 넘어서면 where 절에 명시적으로 조인 조건이 테이블들을 앞에 포함하는 조인 순서만 고려한다. 예 를
들어, 6개의 테이블 T1, T2, ... , T6에 대한 조인을 수행하는 SQL 문에서 조 인 조건이 T1, T2, T5,
T6에 대해서만 주어졌다면, T1, T2, T5, T6가 먼저 조인 되고, T3, T4는 항상 나중에 조인되는 조인 순서만
고려한다.
그런데, 이런 경우에도, 앞에서 언급한 것처럼, 테이블의 개수가 많으면 가능한 조 인 순서의 조합이
기하급수적으로 늘어나게 된다. 이렇게 되면 옵티마이저 시간이 너무 많이 걸리기 때문에, 옵티마이저는 일정한 수(디폴트로는 최대
80,000)의 조인 순서에 대해서만 비용을 계산하고, 이 중에서 가장 최선의 실행 계획을 찾게 된다. 즉, 모든 가능한 조인
순서 조합들 중에서 일부분만 비용을 계산하고, 나머지는 고 려하지 않는 것이다. 이를 실행 계획 탐색에
대한‘가지치기(pruning)’또는‘컷오 프(cutoff)’라 부른다.
그런데, 고려되지 않은 조인 순서 중에서 실제로
최선의 실행 계획이 포함되어 있을 수 있다. 옵티마이저가 제일 처음 고려하는 조인 순서를 테이블의 레코드 수의 오름 차순 순서로
정하는 이유는 경험적으로 이 순서 근처에 실제로 최적의 실행 계획이 존재하기 때문이다. 이와 같이 초기 조인 순서를 선택하는
휴리스틱(heuristic)을 사용함으로써 임의로 조인 순서를 시작했을 때 최적의 좋은 실행 계획이 컷오프되는 것을 막을 수
있다.
오라클 옵티마이저 실행 계획 생성 모듈(Oracle9i부터 도입된)의 또 다른 특징은, 조인 순서를 바꾸어가면서 지금까지 구한 최적의 실행 계획의 예상 비용이 그리 크 지 않은 경우, 최적화 단계를 일찍 끝내버린다.
예
를 들어, 어떤 질의에 대해 10초 동안 최적화를 수행해서 찾은 최적 실행 계획의 예상 수행 시간이 1분이면, 남은 조인 순서가
더 있더라도 옵티마이저 단계를 종료 한다. 반면에, 지금까지 구한 최적 예상 수행 시간이 2시간이면, 더 나은 실행 계획 을
찾기 위해 새로운 조인 순서에 대해 계속 탐색할 필요가 있다. 이를‘적응적 탐 색 전략(adaptive search
strategy)’이라 부른다.
비용 산정 모듈
자, 그럼 다음으로 비용 산정 모듈에 대해 알아보자. 실행 계획 생성 모듈에서 넘겨
받은 특정 조인 순서의 각 조인에 대해 Nested Loop, Sort Merge, Hash Join 방식과 각 테이블의 다양한
액세스 방법을 반복 적용하면서 각 단계별로 비용을 계 산해서 궁극적으로 해당 조인 순서에서 찾을 수 있는 최선의 실행 계획과 그
예상 비용을 구해서 실행 계획 생성 모듈에게 넘겨 준다.
현재의 조인 순서에 대해 중간 단계까지의 수행 비용이 실행
계획 생성 모듈에서 지 금까지 구한 최선의 예상 비용보다 더 크다면, 해당 조인 순서에 대해서는 더 이상 비용 산정을 수행하지
않고 끝낸다. 예를 들어, T1, T2, T3에 대해 (T1§_T2)§_T3 순서에 대해 비용이 1000이었는데,
(T§_T3)§_T2 순서의 (T1§_T3) 비용이 1200이 었다면 더 이상 비용을 계산할 필요가 없다.
<그림
2>에 나와 있는 것처럼, 옵티마이저는 실행 계획의 비용을 계산하기 위한 비용 모델(Cost Model)을 갖고 있고, 이
비용 모델은 Oracle Data Dictionary에 서 관리하는 다양한 통계정보를 기반으로 크게 다음과 같은 세 가지
값(measure)의 예상치를 계산한다.
? 선택도(Selectivity) : Where 절에 있는 다양한 조건들의 선택도 계산
? 카디널러티(Cardinality) : 실행 계획상의 각각의 연산의 결과 카디널러 티 수 계산
? 비용(Cost) : 실행 계획상의 각각의 연산을 수행하는 데 소요되는 시간 비 용 계산
이 비용 산정을 위한 통계정보를 저장하는 Data Dictionary 테이블들은
DBA_TABLES, DBA_INDEXES, DBA_TAB_COL_STATISTICS, DBA_ HISTOGRAMS 등이다. 이
통계정보는 앞에서 언급한 테이블의 액세스 경로의 비 용 정보를 결정하는 데도 사용된다.
이들 테이블의 정보는 사용자가
ANALYZE 명령어나 DBMS_STATS 패키지를 이 용해서 관리하게 되는데, 이들 테이블에서 관리하는 통계정보의 종류와
통계정보를 수집/관리하고, 통계정보를 확인하는 자세한 방법은 이 글에서는 설명하지 않겠다< 참고자료 5>.
만일에 테이블과 인덱스에 대한 통계정보가 존재하지 않는 경우, 옵티마이저는 해당 테이블과 인덱스에 대해 디폴트로 가정하는 값들이 있다<참고자료 5>.
선택도
우선 선택도(selectivity)의 개념을 예로 들자.
앞에서 예로 든 질의 Q1에서 d.loc = ‘SEOUL’이라는 조건의 선택도는 dept 테이블 전체 중에서 loc의 값이
‘SEOUL’인 레코드의 비율을 일컫는다. 옵티마이저는 선택도 계산을 통해서 해당 조건을 만족하는 레코드가 몇 건 정도가
되는지를 예측하게 된다.
옵티마이저는 만일 DBA_TABLES에 dept 테이블의 loc 칼럼의 distinct
column values가 10이라면 옵티마이저는 선택도가 0.1이라고 판단하게 된다. 이때 선택도 를 이와 같이 정하는 이유는
dept 테이블이 loc 칼럼들에 골고루 분포되어 있다고 가정할 때 성립한다. 그러나, 실제로 loc 칼럼의 값들이
skew되어서 분포할 수도 있다.
예를 들어, 전체 레코드의 50%가 loc 값으로‘SEOUL’을 갖는다면 잘못된
선택도 값을 얻게 된다. 이와 같이 데이타 분포가 skew되어 있는 경우, 해당 칼럼에 대한 히스토그램 정보를
DBA_HISTOGRAM 테이블에 만들어 주어야 정확한 선택도 값 을 계산할 수 있다(이 경우는 0.5). 오라클 옵티마이저는
다양한 조건식의 종류에 대해 선택도를 통계정보에 기반해서 계산하는 수식을 내부적으로 갖고 있다. 그렇지만, 만일 dept
테이블이 아직 분석되지 않아서 통계정보가 없는 경우, 옵티마 이저는 내부적으로 갖고 있는 디폴트 값을 선택도로 지정한다(예를
들어, 0.01).
카디널러티
앞의 dept 테이블의 전체 레코드 건수가 1000일
때, 앞에서 설명한 loc = ‘SEOUL’의 선택도가 0.1로 계산되었을 때, 조건을 만족하는 레코드 건수는 1000 x
0.1, 즉 100개로 예상할 수 있다. 이와 같이 어떤 연산을 수행한 결과로 나오는 레코드
건수를‘카디널러티(cardinality)’라 하는데, 정확한 카디널러티를 계산하는 것은 좋은 실행 계획을 만드는 데 굉장히
중요하다.
예를 들어, (T1§_T2)§_T3 순서로 테이블을 조인할 때 (T1§_T2)의 결과와 T3를 조 인할 때
어떤 조인 방법을 선택하는 것이 좋을지를 결정하기 위해서는 (T1§_T2)의 크기를 정확하게 알아야 한다. 이를 위해서는
(T1§_T2) 조인의 결과 레코드가 몇 개인지를 예상할 수 있어야 한다.
이를 위해 오라클 옵티마이저는 다양한 연산의
결과 레코드의 카디널러티를 통계정 보와 수식에 의해서 계산한다. T1과 T2의 조인 조건이 T1.c1 = T2.c2(이를‘P’라
표기)라 했을 때, 앞에서 설명한 선택도 계산 공식에 의해 이 조건식의 선택도 Sel(P)를 먼저 계산한 후, 이 조인의 결과
카디널러티는 Card(T1) x Card(T2) x Sel(P)가 된다.
예를 들어, T1, T2의 튜플 수가 각각
1000, 5000이고 Sel(P)가 0.01이면, 조인의 결과로 생기는 튜플 수는 1000 x 5000 x 0.01 =
5000이 된다. 그런데, Sel(P)가 조금이라도 틀리면 이후의 전체적인 비용 산정이 잘못되게 된다. 오라클 옵티마이저 는
다양한 종류의 연산에 대해 내부 공식을 사용해 카디널러티를 계산한다.
비용
비용(cost)은 테이블 액세스, 조인 등을 수행하는 데 걸리는 시간을 의미하는데, 시 간은 주로 디스크 I/O 수와 CPU 사용시간을 고려한다. 비용은 앞에서 계산한 통계 정보와 내부 계산식에 의해 계산된다.
예
를 들어, T1§_T2를 Nested Loop 방식으로 조인할 경우 조인비용은 (T1의 데이 타 블록수) + ((T1의 레코드
건수)*(T2의 액세스 비용))이 된다. 이처럼 오라클 옵 티마이저는 모든 연산에 대해 소요되는 비용을 계산하는 수식을 갖고
있다. 오라클 옵티마이저는 이 세 가지 예상 값(measure)을 기반으로, 현재의 실행 계획 의 예상 비용을 구한다.
오라클 옵티마이저와 관련한 몇 가지 유용한 기능
이상에서 오라클 옵티마이저의 내부 동작 원리를 살펴보았다. 옵티마이저와 관련해 서 오라클에서 제공하는 몇 가지 유용한 기능들에 대해서 간단히 알아보자(이들 기 능에 대한 자세한 설명은 <참고자료 3>을 보기 바란다).
이 기능들은 크게 두 가지 - 즉, 옵티마이저가 사용할 통계정보를 수집/관리하는 기 능과 옵티마이저의 활동을 자세히 추적할 수 있는 기능 - 로 구분할 수 있다.
먼
저, 통계정보 수집/관리 기능으로는 ANALYZE 명령과 DBMS_ STATS 패키지 를 들 수 있다. 비용 계산이 최적의 실행
계획을 구하는 데 중요한 역할을 하기 때 문에, 이 기능을 사용해서 1) 어떤 테이블이나 칼럼에 변경사항이 많이 발생해서 새 로
통계정보를 분석해야 하는지, 2) 어떤 칼럼에 대해 히스토그램을 만들어야 하는 지에 대한 도움을 받을 수 있다.
다음으로
옵티마이저의 활동 추적과 관련해서, 1) 주어진 질의에 대해서 어떠한 최 적화 과정을 거쳤는지, 2) 최종적으로 어떤 실행
계획을 선택했는지, 그리고, 선택 된 실행 계획대로 수행했을 때 걸리는 시간과 자원이 얼마나 소요되었는지를 확인하 는 기능들이
제공된다. 우선 옵티마이저의 최적화 과정을 확인하려면, ‘Event 10053’을 이용하면 된다. 이를 위해서는
SQL*Plus에서 다음 alter 명령을 수행하 면 된다.
SQL> alter session set events ‘10053 trace name context
forever’;
다음으로, 단순히 옵티마이저가 최종적으로 선택한 실행 계획만 확인하고 싶으면, EXPLAIN 명령어나 SQL*Plus에서 제공되는 Autotrace 기능을 이용하면 된다. 그리고, 옵티마이저가 선택해서 수행한 실행 계획의 자세한 성능 정보를 알고 싶으 면, SQL Trace, TKPROF 등의 기능을 이용하면 된다.
오라클 옵티마이저의 한계와 그 원인
오라클 옵티마이저를 포함한 현재의 관계형 DBMS의 옵티마이저는 항상 최적의 실 행 계획을 고르지는 못한다. 옵티마이저의 한계는 실행 계획 생성 모듈과 비용 산정 모듈의 동작 원리에 그 원인이 숨어 있다.
실행 계획 생성 모듈의 제약
우선, 실행 계획 생성 모듈에서
최적화를 위해 사용할 수 있는 시간이 제한적이라는 점이다. 앞서 살펴보았지만, 10개 이상의 테이블 조인을 포함하는 질의의 경우
최적 의 실행 계획을 구하기 위해 옵티마이저가 고려해야 할 탐색 공간이 너무 많기 때문 에 다양한 형태의 컷오프를 수행한다. 이
과정에서 실제로 최적의 실행 계획이 고려 되지 않고 잘려나갈 수 있다.
비용 산정 모듈의 불완전성
비용 산정 모듈에서는 특정 실행
계획의 비용을 통계정보와 내부적인 비용 산정 모 델을 사용해서 계산한다. 그런데, 옵티마이저에서 사용하는 통계정보와 비용 산정
모델이 불완전하다. 따라서, 옵티마이저는 불완전한 정보를 바탕으로 일종의 추측 (또는 가정)을 하는 것이다.
예를
들어, d.loc = ‘SEOUL’의 선택도를 구하는 과정을 보자. d.loc에 대한 히스 토그램 통계정보가 없으면,
옵티마이저는‘모든 값들이 골고루 분포되어 있다’는 가 정하에 d.loc 칼럼의 distinct value 개수(이를 NDV라
하자)를 기준으로 해당 조 건의 선택도를 1/NDV로 계산한다. 그러나, 실제로는 d.loc에 대해 skew된 분포를 보이면
옵티마이저의 실행 계획 비용 산정이 틀려지게 된다.
비용 산정 모듈이 한계를 갖게 되는 또 다른 예로, SQL에서
바인드 변수(bind variables)의 사용을 들 수 있다. 질의 Q1에서 d.loc = ‘SEOUL’대신에 d.loc =
:loc_name 조건이 사용되었으면, 데이타베이스에 loc 칼럼에 아무리 정확한 통계치 를 갖고 있어도 선택도에 대해서는
일정한 비율을 가정할 수밖에 없다. 다른 예로서, 다음 질의를 살펴보자.
Q2:
select *
from emp
where job_title = ‘vice_president’and salary < 40000
데 이타베이스에는 job_title, salary 칼럼 모두에 대해 정확한 히스토그램 정보를 유 지하고 있고, job_title = ‘vice_president’의 선택도가 0.05이고, salary < 40000 의 선택도는 0.4였다. 이때 옵티마이저는 emp 테이블에서 where 절의 조건의 전 체 선택도를 0.05 x 0.4, 즉 0.02로 계산한다. 이는‘각 칼럼의 값들의 분포는 서 로 독립적이다’는 가정에 기반하다.
그러나, 부사장이면서 연봉이 40,000 이하인 경우는 거의 없기 때문에 실제 선택도 는 0에 가까울 것이다. 결국 앞의 가정은 이와 같이 서로 밀접한 상관관계가 있는 두 칼럼에 대한 선택도를 구할 때 문제가 되는 것이다.
마 지막 예로, 조인 연산에 대한 비용을 예측할 때, 이 조인을 수행할 수 있는 메모 리 공간을 고정 크기로 가정하고, Nested Loop, Sort Merge, Hash Join의 비용 을 산정한다. 그러나, 질의를 수행할 때 실제 비용은 이용 가능한 메모리의 양에 따 라 크게 차이가 날 수 있다.
결론적으로, 옵티마이저의 정확도는 비용 계산의 정확도에 따라 좌우되는데, 참고하 는 통계정보가 부족하거나 계산 과정의 몇 가지 가정들이 실제 데이타 분포와 실행 계획의 런타임 환경과 차이가 있기 때문에 정확도에 문제가 발생하는 것이다.
힌트 기능을 이용한 옵티마이저 동작 제어
이와 같은 현재의 옵티마이저의 한계를 보완하기 위해, 오라클에서는 SQL에 힌트 (hint)를 추가해서 사용자가 옵티마이저가 선택하는 실행 계획에 영향을 줄 수 있도 록 하고 있다.
옵
티마이저가 최선이 아니 차선의 실행 계획을 선택하는 경우, 사용자가 SQL에 힌트 를 주어서 실행 계획을 베스트 플랜으로
만들도록 하는 것이 목적이다. 힌트를 제공 하는 것이 옵티마이저의 기능이 떨어지는 것을 의미하는 것은 아니다. 어떤 DBMS의
옵티마이저도 완전할 수는 없기 때문에, 힌트 기능의 제공은 반드시 필요하다.
옵티마이저의 실행 계획은 결국 조인 순서, 조인 방법, 테이블 액세스 경로를 결정 하는 것이기 때문에, 힌트의 종류도 크게 이 세 가지를 제어하는 것으로 구분할 수 있다.
다
음 예는 Q1에 대해 힌트를 사용한 예를 보여주고 있는데, 처음 ‘ordered’는 from 절에 나와 있는 순서대로 조인
순서를 정하는 것이고, ‘use_nl’의 경우 dept 테이블을 inner table로 사용할 때 Nested Loop 방식만을
사용하도록 지정하고, full(e)는 emp 테이블은 항상 Full Table Scan을 통해서 액세스하도록 지정하는 것이다.
오라클에서 제공하는 다양한 힌트의 종류와 자세한 의미에 대한 설명은 <참고자료 5>를 보기 바란다.
Q1 :
select /*+ ordered use_nl(d) full(e) */ ename, sal
: Oracle 힌트 기능 사용 예
from emp e, dept d
where e.deptno = d.deptno and d.loc = ‘SEOUL’
오 라클 SQL에서 힌트를 제공하는 또 다른 목적은, 사용자가 다양한 실행 계획을 수 행해 봄으로써 어떤 데이타 액세스 경로가 도움이 되는지를 테스트해 볼 수도 있다. 힌트의 사용은 아주 불가결한 경우 말고는 사용을 조심해야 한다. 실제로 이 힌트 기능이 남용되는 경우가 많다.
힌트를 사용하게 됨으로써 데이타베이스 환경의 변화(예를 들어, 테이블의 크기 변 화, 인덱스의 추가/삭제)가 발생할 때 옵티마이저가 더 나은 실행 계획을 선택하는 것을 방해할 수도 있다. 실제로 Oracle E-Business Suite 11i의 경우 포함된 27만 개의 SQL 중에서 0.3%만이 힌트를 포함하고 있다고 한다.
SQL 튜닝과 옵티마이저의 관계
SQL 튜닝은 특정 SQL 질의의 수행 시간을 단축하기 위해 사용자가 취하는 다양한 방법을 통칭한다. SQL 튜닝의 범위는 굉장히 포괄적인데, 옵티마이저와 관련한 방 법으로는 SQL 재작성, 힌트 사용, 새로운 인덱스 추가, 통계 데이타의 추가/갱신 등을 통해서 옵티마이저가 더욱더 효율적인 실행 계획을 생성하도록 하는 것이다.
SQL 재작성
사용자가 원하는 데이타를 질의하는 방법은 실제로
매우 다양할 수 있다. 극단적인 예로, C. J. Date는 한 SQL 문을 50가지 이상의 다른 SQL 문으로 표현이 가능함
을 보여 준다(http://www.dbpd.com/vault/9807xtra.htm 참조). SQL 재작성 을 통한 SQL
튜닝은 원래의 SQL 문을, 같은 결과를 내지만, 옵티마이저가 더 효과 적인 실행 계획을 생성할 수 있는 SQL 문으로 바꾸는
방법이다.
힌트 사용
앞에서 언급한 것처럼, 힌트 기능을 사용해서 옵티마이저가 선택하는 실행 계획을 바꾸는 방법이다.
새로운 인덱스 추가
SQL 문의 효율적인 처리를 위해서는 특정
테이블의 특정 칼럼 값을 이용해서 해당 데이타를 빨리 찾아야 하는데, 인덱스가 없기 때문에 옵티마이저가 어떤 실행 계획 을
선택하더라도 그 SQL 문은 느릴 수밖에 없는 경우가 있다. 이와 같은 상황에서 는 새로운 인덱스 생성을 통해서 옵티마이저가
해당 인덱스를 이용하는 새로운 실행 계획을 선택하도록 할 수 있다.
통계 데이타의 추가/갱신
앞에서 설명한 것처럼, 오라클 옵티마이저의 비용 산정 모듈에서는 테이블, 칼럼, 인덱스 등에 대한 통계정보를 이용해서 선택도, 카디널러티 등을 구하고 이를 통해 서 궁극적으로 실행 계획의 비용을 계산한다.
그런데, 만일 특정 테이블/칼럼에 대한 통계정보가 없거나, 오래 전에 만들어진 경 우는 비용 계산이 부정확하게 되고, 따라서 옵티마이저가 선택하는 실행 계획이 실 제로는 안 좋은 실행 계획일 수가 있다.
이
를 해결하기 위해서는 특정 통계정보를 추가하거나 새로 갱신해 주어서 옵티마이 저가 정확한 비용 산정을 통해서 더 나은 실행
계획을 선택하도록 해주는 방법이다. 옵티마이저 기술의 발달은 궁극적으로 SQL 튜닝 관련 직종을 없앨 수도 있다. 그 러나,
다행인지 불행인지 몰라도, 향후 10년 사이에 이런 일이 벌어지지는 않을 것 같다.
향후 옵티마이저의 기술의 발전 방향
비록 옵티마이저 기술은 지난 30년간 꾸준히 발전해오면서 인간이 만든 가장 지능적 인 소프트웨어이지만, 앞으로도 끊임없이 기술 발전이 필요한 분야이다.
사
용자가 사용하는 SQL 질의가 점점 더 복잡해지고, 데이타베이스에서 다루는 데이 타 양이 엄청난 속도로 늘어나고, 새로운
데이타의 종류를 데이타베이스에서 다루어 야 하기 때문에, 옵티마이저 기술의 중요성은 더욱 더 커질 것이다.
향후 옵티마이저 기술의 주요 발전 방향은 다음의 분야가 될 것이다. 여기서 나열한 분야는 주로 학계에서 연구가 활발히 진행중이거나 많은 진전이 있는 분야를 중심으 로 판단한 필자의 개인적인 의견이다.
질의 변환
오라클 옵티마이저와 관련해서 간략히 설명했지만,
현재의 옵티마이저가 처리하는 질의변환의 형태는 상대적으로 정형화되고 단순한 형태의 질의 변환만을 주로 수행 한다. 단일 SQL
블록(Select-From-Where)에 대한 질의변환 이외에, 복잡한 중첩 질의(nested query)를 단일 질의로
변환하는 방법, 중첩질의 내의 각 SQL 블록을 결합한 효과적인 실행 계획 생성이 가능해질 것이다.
비용 산정을 위한 정확한 통계정보 관리
실행 계획에 대한 정확한 비용 산정이 옵티마이저 기술의 핵심이다. 따라서, 지금 옵티마이저가 참고하는 통계정보보다 더 정교하고 복잡한 통계정보의 관리/유지 기 법들이 도입될 것이다.
예를 들어, 애트리뷰트 값들의 분포가 서로 독립적이라는 가정 대신에 상호 연관성 이 깊은 칼럼들에 대한 히스토그램 정보를 효과적으로 수집/활용하는 기술이 도입될 수도 있다.
런타임시 동적 질의 최적화
현재의 옵티마이저 기술은, 질의 수행
환경에 상관없이 옵티마이저가 선택한 실행 계획을 그대로 실행한다는 측면에서 정적(static) 질의 최적화 방법이다. 앞으로는
고정된 실행 계획을 그대로 수행하는 것이 아니라, SQL 실행 단계의 상황에 따라 실행 계획을 융통성 있게 바꾸는 기술이 개발될
것이다. 실제로 Oracle9i의 경우 초 보적인 형태의 동적 질의 최적화 기능을 제공하고 있다.
현재의 옵티마이저 기술은 통계정보의 변화나 사용자의 힌트가 없다면, 같은 SQL 질의에 대해서는 항상 똑같은 실행 계획을 선택할 것이다. 옵티마이저가 특정 질의 에 대해 생성한 실행 계획을 실제 수행했을 때, 예상과 달리 좋지 않은 성능을 보이 면, 옵티마이저가 다음 번에 최적화를 수행할 때는 이전의 실행 계획을 제외한 다른 대안을 찾게 되는 것이다.
DBguide.net