'2018/01/27'에 해당되는 글 3건

  1. 2018.01.27 DB 튜닝. hint 목록
  2. 2018.01.27 Oracle-실행계획 개별
  3. 2018.01.27 Oracle-튜닝.수행내역 이력을 파일로 저장



출처 : http://theone79.tistory.com/entry/오라클-hint-사용법 


힌트의 사용법
 
{SELECT | INSERT | UPDATE | DELETE} /*+ hint [text] [hint [text]] ... */
혹은
{SELECT | INSERT | UPDATE | DELETE} --+ hint [text] [hint [text]] ...
 
-         이러한 힌트의 사용은 SQL 전체가 아닌 쓰여진 SQL 블럭에만 적용됩니다.
 
 
힌트의 종류 별 분류
Optimization Goals and Approaches
             ALL_ROWS 혹은 FIRST_ROWS
             CHOOSE
             RULE
 
Acess Method Hints
             AND_EQUAL
             CLUSTER
             FULL
             HASH
             INDEX 혹은 NO_INDEX
             INDEX_ASC 혹은 INDEX_DESC
             INDEX_COMBINE
             INDEX_FFS
             ROWID
 
Join Order Hints
             ORDERED
             STAR
 
Join Operation Hints
             DRIVING_SITE
             HASH_SJ, MERGE_SJ 혹은 NL_SJ
             LEADING
             USE_HASH 혹은 USE_MERGE
             USE_NL
Parallel Execution Hints
             PARALLEL 혹은 NOPARALLEL
             PARALLEL_INDEX
             PQ_DISTRIBUTE
             NOPARALLEL_INDEX
 
Query Transformation Hints
             EXPAND_GSET_TO_UNION
             FACT 혹은 NOFACT
             MERGE
             NO_EXPAND
             NO_MERGE
             REWIRTE 혹은 NOREWRITE
             STAR_TRANSFORMATION
             USE_CONCAT
 
Other Hints
             APPEND 혹은 NOAPPEND
             CACHE 혹은 NOCACHE
             CURSOR_SHARED_EXACT
             DYNAMIC_SAMPLING
             NESTED_TABLE_GET_REFS
             UNNEST 혹은 NO_UNNEST
             ORDERED_PREDICATES
  
힌트의 설명 및 사용법
 
ALL_ROWS
             /*+ ALL_ROWS */
-         최소한의 자원을 사용하여 결과값의 전체를 추출하게 합니다.
 
AND_EQUAL
             /*+ AND_EQUAL (table index index [index] [index] [index] ) */
-         복수의 단일 컬럼을 스캔하여 머지 방식으로 처리하게 합니다.
 
APPEND_HINT
             /*+ APPEND */
-         직렬 모드 데이터베이스에서 Direct INSERT를 실행하게 합니다.
-         Enterprise Edition 이 아닌 데이터베이스의 기본 모드는 직렬 모드입니다. 이러한 직렬 모드 데이터 베이스에서의 INSERT 작업은 Conventional를 기본값으로 하고 병렬 처리 시에는 Direct INSERT를 기본값으로 합니다.
 
CACHE_HINT
             /*+ CACHE (table) +/
-         풀 테이블 스캔의 사용 시, 테이블에서 읽어온 블럭을 버퍼의 LRU 리스트 의 MRU 쪽에 위치시킵니다. 작은 테이블의 사용 시 유용합니다.
 
CHOOSE_HINT
             /*+ CHOOSE +/
-         Rule-Based 와 Cost-Based 방식 간의 선택을 유도합니다. 선택 기준은 사용 객체의 분석 정보 존재 여부이며, 사용되는 객체들중 하나라도 분석 정보가 존재한다면 Cost-Based 방식을 사용하게 됩니다.
 
CLUSTER_HINT
             /*+ CLUSTER (table) +/
-         지정 테이블의 클러스터 스캔을 유도합니다. 클러스터된 객체에만 사용할 수 있습니다.
 
CURSOR_SHARING_EXACT
             /*+ CURSOR_SHARING_EXACT +/
-         바인드 변수 값의 교체를 불가능하게 합니다.
-         기본적으로 CURSOR_SHARING 파라미터를 사용하여, 안전하다고 판단될 시 SQL 내의 바인드 변수 값을 교체할 수 있게 되어 있습니다.
 
DRIVING_SITE
             /*+ DRIVING_SITE (table) +/
-         오라클이 선택한 SITE 대신, 지정한 SITE를 사용하여 쿼리를 실행합니다. Rule-Based 와 Cost-Based, 두 모드 다 사용 가능합니다.
 
DYNAMIC_SAMPLING
             /*+ DYNAMIC_SAMPLING ( [table] n ) +/
-         해당 객체의 Selectivity 와 Cardinality 에 대한 보다 자세한 정보를 자동으로 생성시켜 실행합니다.
-         값은 0 부터 10 까지 지정할 수 있으며, 높을 수록 보다 자세한 정보를 생성하게 됩니다. 테이블에 해당 값을 지정하지 않았을 경우, 기본 값은 CURSOR 레벨의 값이 쓰여집니다.
 
EXPAND_GSET_TO_UNION
             /*+ EXPAND_GSET_TO_UNION +/
-         GROUP BY GROUPING SET 혹은 GROUP BY ROLLUP 등과 같은 구문을 포함하는 쿼리에 사용할 수 있습니다.
-         이 힌트는 기존의 쿼리를 개별적인 그룹 생성 후, UNION ALL 방식으로 실행되게 유도합니다.
 
FACT_HINT
             /*+ FACT (table) +/
-         스타 변형 구문에서 사용되며 해당 테이블이 FACT 테이블로 사용되게 유도합니다.
 
FIRST_ROWS
             /*+ FIRST_ROWS (n) +/
-         전체 결과값의 반환 대신 지정한 숫자만큼 로우의 결과값을 반환하는데 집중하게 유도합니다.
 
FULL_HINT
             /*+ FULL (table) */
-         지정한 테이블에 대해 풀 테이블 스캔을 유도합니다.
 
HASH_HINT
             /*+ HASH (table) */
-         지정한 테이블에 대해 hash 스캔을 수행하도록 유도합니다.
-         클러스터 테이블 만을 대상으로 합니다.
 
HASH_AJ
             /*+ HASH_AJ */
-         EXISTS 구문 뒤에 오는 서브 쿼리에 사용되며 HASH_SJ, MERGE_SJ 혹은 NL_SJ 등을 사용할 수 있습니다.
-         HASH_SJ 은 hash semi-join 이고, MERGE_SJ 은 sort merge semi-join 이며 NL_SJ 은 nested loop semi-join 입니다.
 
INDEX
             /*+ INDEX (table index [index] [index] ... ) */
-         지정한 테이블의 인덱스 스캔을 실행하도록 유도합니다.
-         Domain, B-tree, bitmap, bitmap join 인덱스 등이 사용될 수 있으나, bitmap 인덱스 들의 사용 시, INDEX 힌트보다는 INDEX_COMBINE 힌트 사용이 추천됩니다.
 
INDEX_ASC
             /*+ INDEX-ASC (table [index] [index] ... ) +/
-         해당 테이블의 인덱스를 순차적 방식으로 스캔하게 합니다.
-         해당 쿼리가 인덱스 범위 스캔의 사용 시, 인덱스 값의 순차적 방식으로 읽게 됩니다.
 
INDEX_COMBINE
             /*+ INDEX_COMBINE (table [index] [index] ... ) +/
-         해당 테이블에 Bitmap 인덱스의 존재 시, Bitmap 인덱스를 통한 액세스를 유도합니다.
-         힌트 내에 인덱스의 이름이 쓰여지지 않을 시, 해당 인덱스의 Boolean 값을 사용하여 최적의 Cost를 산출하여 실행하게 됩니다.
 
INDEX_DESC
             /*+ INDEX_DESC (table [index] [index] ... ) +/
-         지정한 인덱스에 대해 인덱스 스캔을 역순으로 실행합니다.
-         해당 쿼리가 인덱스 범위 스캔의 사용 시, 인덱스 컬럼의 값을 사용하여 역순으로 실행합니다.
-         파티션 인덱스에서는 파티션 별 개별적인 실행이 이루어집니다.
 
INDEX_FFS
/*+ INDEX_FFS (table [index] [index] ... ) +/
-         풀 테이블 스캔 대신에 빠른 풀 테이블 스캔의 실행을 유도합니다.
 
LEADING_HINT
             /*+ LEADING (table) +/
-         테이블 간의 조인 시에 지정한 테이블을 먼저 수행하도록 유도합니다.
-         두 개 이상의 LEADING 힌트의 사용 시, 힌트 자체가 사용되어 지지 않습니다.
-         ORDERED 힌트와 더불어 사용시, LEADING 힌트는 사용되지 않습니다.
 
MERGE
             /*+ MERGE (table) +/
-         각 쿼리의 결과값을 머지합니다.
-         해당 쿼리 내에 GROUP BY 절의 사용 이나 SELECT 구문에 DISTINCT 가 사용되었을 시, 머지의 실행이 가능할 경우에만 힌트가 실행됩니다.
-         IN 과 서브 쿼리의 사용 시, 서브 쿼리와 상위 쿼리 간의 상호 관계가 없을 때에만 머지의 실행이 가능합니다.
-         이 힌트는 Cost-based 가 아닙니다. 따라서 액세스하는 실행 쿼리 블럭에 MERGE 힌트가 반드시 명시되어야만 합니다. 그렇지 않을 경우 옵티마이저는 다른 실행 계획을 수립합니다.
 
MERGE_AJ
             HASH_AJ 를 참조하십시요.
 
MERGE_SJ
             HASH_AJ 를 참조하십시요.
 
NL_AJ
             HASH_AJ 를 참조하십시요.
 
NL_SJ
             HASH_AJ 를 참조하십시요.
 
NOAPPEND
             /*+ NOAPPEND +/
-         병럴 모드에서의 INSERT 작업을 Conventional 방식으로 수행합니다.
-         병렬 모드에서는 Direct-path INSERT 가, 직렬 모드에서는 Conventional INSERT가 기본값입니다.
 
NOCACHE
             /*+ NOCACHE (table) +/
-         풀 테이블 스캔의 사용 시, 테이블에서 읽어온 블럭을 버퍼의 LRU 리스트 의 LRU 쪽에 위치시킵니다. 기본 모드입니다.
 
NO_EXPAND
             /*+ NO_EXPAND +/
-         실행 쿼리 내에 OR 나 WHERE 절의 IN 이 사용되었을 시, Cost-Based 옵티마이저가 쿼리 처리를위해 OR 를 사용한 확장을 사용하는 것을 방지합니다.
-         일반적으로 옵티마이저는 위와 같은 경우 OR – 확장의 가격이 확장을 사용하지 않는 것보다 적을 시, 확장 방식으로 수행합니다.
 
NO_FACT
             /*+ NO_FACT (table) +/
-         Star 변형 시, 해당 테이블의 FACT 테이블로서의 사용을 방지합니다.
 
NO_INDEX
             /*+ NO_INDEX (table [index] [index] ... ) +/
-         지정 테이블의 인덱스 사용을 방지합니다.
 
NO_MERGE
             /*+ NO_MERGE (table) +/
-         머지 처리 방식의 사용을 방지합니다.
 
NOPARALLEL
             /*+ NOPARALLEL (table) +/
-         지정한 테이블의 병렬 처리를 방지합니다.
-         테이블의 지정된 PARALLEL 값에 대해서 우선권을 가집니다.
-         중첩 테이블에 대해서는 병렬 처리를 할 수 없습니다.
 
NOPARALLEL_INDEX
             /*+ NOPARALLEL_INDEX (table [index] [index] ... ) +/
-         인덱스 스캔 작업의 병렬 처리를 방지합니다.
-         인덱스에 지정된 PARALLEL 값에 우선권을 가집니다.
 
NO_PUSH_PRED
             /*+ NO_PUSH_PRED (table) +/
-         결과값에 대한 조인 방식 서술의 강제적 수행을 방지합니다.
 
NO_PUSH_SUBQ
             /*+ NO_PUSH_SUBQ +/
-         서브 쿼리의 결과값을 머지하지 않는 실행 계획이 실행 계획 설립 단계에서 제일 마지막으로 참조되는 것을 방지합니다.
-         일반적으로 서브 쿼리의 Cost 가 높거나, 처리 로우의 갯수를 크게 줄여주지 못할 때에는 서브 쿼리를 마지막에 참조하는 것이 성능 향상에 도움이 됩니다.
 
NOREWRITE
             /*+ NOREWRITE +/
-         해당 쿼리 블럭의 쿼리 재생성의 실행을 방지합니다.
-         QUERY_REWRITE_ENALBE 파라미터에 대해 우선권을 가집니다.
-         NOREWRITE 힌트의 사용 시, Function-Based 인덱스의 사용이 금지됩니다.
 
NO_UNNEST
             /*+ NO_UNNEST +/
-         해당 서브 쿼리 블럭의 UNNESTING 설정의 사용을 방지합니다.
 
ORDERED
             /*+ ORDERED +/
-         FROM 절에 나열된 테이블의 순서대로 조인 작업을 실행합니다.
 
ORDERED_PREDICATE
             /*+ ORDERED_PREDICATE +/
-         옵티마이저에 의한 조인 관계의 Cost를 산출하기 위해 미리 정해둔 조인 관계 별 실행 순서의 사용을 방지합니다.
n         인덱스 키를 사용한 조인 관계들은 제외됩니다.
-         이 힌트는 쿼리의 WHERE 절에 사용하십시요.
 
PARALLEL
             /*+ PARALLEL (table [ [, n |, DEFAULT |, ] [, n | DEFAULT ] ] ) +/
-         병렬 처리에 사용될 서버 프로세스의 갯수를 설정합니다.
-         병렬 처리 조건에 위배될 시, 힌트는 사용되지 않습니다.
-         임시 테이블에 대한 PARALLEL_HINT 사용 시, 힌트는 사용되지 않습니다.
 
PARALLEL_INDEX
             /*+ PARALLEL_INDEX (table [ [index] [, index]...]
[ [, n |, DEFAULT |, ] [, n | DEFAULT ] ] ) +/
-         파티션 인덱스의 인덱스 범위 스캔 작업의 병렬 처리에 할당될 서버 프로세스의 갯수를 지정합니다.
 
PQ_DISTRIBUTE
             /*+ PQ_DISTRIBUTE (table [,] outer_distribution, inner_distribution) +/
-         병렬 조인 시, Producer 프로세스와 Consumer 프로세스 간의 데이터 전달 방식을 지정합니다.
 
PUSH_PRED
             /*+ PUSH_PRED (table) +/
-         결과값에 대한 조인 방식 서술의 강제적 수행을 실행합니다.
 
PUSH_SUBQ
             /*+ PUSH_SUBQ +/
-         머지가 불가능한 서브 쿼리들의 우선 실행 계획을 실행 계획 수립시 먼저 참조하도록 합니다.
-         서브 쿼리의 사용 객체가 Remote 테이블이거나, 머지 조인의 사용 시 힌트는 실행되지 않습니다.
 
REWRITE
             /*+ REWRITE [ ( [materialized_view] [materialized_view]...) ] +/
-         실행 계획의 가격에 상관없이 Materialized View 를 사용하여 쿼리 재생성을 하도록 합니다.
-         Materialized View 를 지정할 시, 지정한 Materialized View 의 가격에 상관없이 무조건 쿼리 재생성을 실행합니다.
-         Materialized View 를 지정하지 않을 시, 오라클은 사용 가능한 모든 Materialized View 를 참조하여 그 중 가장 가격이 낮은 Materialized View 를 사용하여 쿼리 재생성을 합니다.
-         Materialized View 를 지정하지 않는 힌트의 사용이 권장됩니다.
 
ROW_ID
             /*+ ROWID (table) +/
-         지정한 테이블의 스캔을 ROWID 방식으로 수행하게 합니다.
 
RULE
             /*+ RULE +/
-         실행 계획을 Rule-Based 방식으로 실행하게 합니다.
-         해당 쿼리 블럭에 다른 힌트 또한 사용되었을 경우, 다른 힌트들은 사용되지 않습니다.
 
STAR
             /*+ STAR +/
-         Star 쿼리 계획이 사용 가능하다면, 실행하게 합니다.
-         Star 쿼리 계획이란 가장 큰 테이블이 마지막 순서로 조인되며, 조인될 시 가장 큰 테이블 내의 Concatenated 인덱스에 대해 Nested Loop 조인 방식으로 실행되는 것을 말합니다.
-         최소한 세개 이상의 테이블이 사용되며, 제일 큰 테이블의 Concatenated 인덱스의 생성에 최소한 세 개 이상의 컬럼이 사용되어야 하며, 액세스나 조인 방식에 충돌이 없어야만 이 힌트는 사용됩니다.
 
STAR_TRANSFORMATION
             /*+ STAR_TRANSFORMATION +/
-         옵티마이저가 Star 변형 작업에 최적화된 실행 계획을 수립, 실행하도록 합니다.
-         힌트를 사용하지 않을 시, 옵티마이저는 일반적인 작업에 최적화된 실행 계획을 수행합니다.
-         힌트를 사용하였어도 변형 작업에 맞추어진 실행 계획을 실행한다는 보장은 없습니다. 다른 일반적인 힌트의 사용과 마찬가지로 비교 분석 후, 오라클의 판단에 따라 다른 실행 계획이 실행될 수 있습니다.
 
UNNEST
             /*+ UNNEST +/
-         서브 쿼리 블럭에 대해 인증성 만을 검사하게 합니다.
-         인증이 되었다면 그 이상의 검증 작업없이 서브쿼리에 대한 UNNESTING 의 설정을 가능하게 합니다.
 
USE_CONCAT
             /*+ USE_CONCAT +/
-         WHERE 절의 OR 조인 을 UNION ALL 로 변경하여 수행하게 합니다.
-         일반적으로 이러한 변경은 결과값의 병합 수행의 가격이 수행하지 않을 시의 가격 보다 낮을 때에만 실행됩니다.
 
USE_HASH
             /*+ USE_HASH (table [table]...) +/
-         Hash 조인 방식으로 각 테이블을 조인하게 합니다.
 
USE_MERGE
             /*+ USE_MERGE (table [table]...) +/
-         Sort-Merge 방식으로 각 테이블을 조인하게 합니다.
 
USE_NL
             /*+ USE_NL (table [table]...) +/
- Nested-Loop 방식으로 각 테이블을 조인하게 합니다.



출처: http://luckys.tistory.com/342 [Lucky's...]

'(DB) Oracle 튜닝 > 쌍용튜닝교육' 카테고리의 다른 글

자주 사용하는 SQL  (0) 2018.02.10
Oracle- Clustered Table  (0) 2018.02.10
Oracle-실행계획 개별  (0) 2018.01.27
Oracle-튜닝.수행내역 이력을 파일로 저장  (0) 2018.01.27
Oracle-튜닝-index , block  (0) 2018.01.20
Posted by 농부지기
,

[ Oracle-실행계획 ]


1. INLIST ITERATOR 

   - IN 절이나 동일한 열에 OR로 연결된 여러 등식(=)이 있는 경우

   - 열거된 값 리스트를 반복하고 모든 값은 별도로 실행

   - 동일한 인덱스를 사용하는 여러 개의 OR 절이 있는 경우 옵티마이저는 

      효율상CONCATENATION 또는 UNION ALL이 아닌 이 연산을 선택

   - 

     


3. INDEX FULL SCAN (MIN/MAX)

   - SELECT MAX(sal) FROM EMP;   --sal에 index가 존재 시


   - SELECT /*+ INDEX_DESC (EMP, IDX_EMP_SAL) */ SAL

        FROM EMP

      WHERE SAL > 0

          AND ROWNUM = 1;



2. SORT GROUP BY

   - 10.2이전 버전인 경우 실행계획


2. HASH GROUP BY 

   - 10.2버전부터 존재

   - 10g부터 group by 시 느린성능의 Sort group by가 사라지고 빠른성능의 Hash Group by가 등장

   - SELECT JOB, DEPTNO, SUM(SAL) AS SAL_TOT, COUNT(*) AS CNT

        FROM EMP

      WHERE DEPT_NO BETWEEN 10 AND 20 

      GROUP BY JOB, DEPTNO;

    - 


오라클 10.2를 시작할때, Group by와 Order by를 동일한 칼럼에 결합하여 사용할때, 성능과 관련하여 괄목할 만한 성능 절감을 발견하게 될 것이다. 

오라클 10.2에서는 Hash 기반의 Group By에 대해서 소개했다. 이전 Group by를 수행하면 연관된 칼럼의 소팅을 포함해서 처리했었고, 이러한 수행결과를 모아서 결과로 반환했다. Hash Group By 메소드는 소팅 없이 이러한 결과를 생성한다. 즉, 이 처리방식은 Sort-Based Group by보다 항상 낳은 성능을 발휘한다. 불행하게도 Order By 절을 동일 칼럼에 Group By절과 함께 사용한다면 Sort-Based Group by로 변경하며, 응답속도는 떨어질 것이다. 그러나 요청한 순서대로 데이터를 획득하고 싶다면 Hash-Based Group by를 이용하면 조금더 이점은 있다.

 http://neokido.tistory.com/entry/%EC%98%A4%EB%9D%BC%ED%81%B4-102-%EB%B2%84%EC%A0%BC%EB%B6%80%ED%84%B0-%EB%8B%AC%EB%9D%BC%EC%A7%84-Group-by%EC%99%80-Order-by%EC%9D%98-%EC%98%B5%ED%8B%B0%EB%A7%88%EC%9D%B4%EC%A7%95



치명적인 Hash Group By 버그

10g 부터 group by 시에 느린성능의 Sort Group by 가 사라지고 빠른성능의 Hash Group By 가 등장 했다.

하지만 현재시점(10.2.0.3)에서 항상 Hash Group By 가 동작되는 것은 아니다.

성능면에서 배치 SQL 혹은 Migration 작업시에 몇억건의 데이터를 sort 하게되면 견딜수 없다.

주로 Sort Group by 는 insert - select - group by 상황에서 발생한다.

opt_param 힌트로도 해결되지 않았으며 Only select 문또는

CTAS (Create table as Select)문에서는 발생하지 않는걸로 확인됬다.

아래는 간단한 테스트를 진행 하고 현상황에서 Sort Group by 를 피할수 있는 해법을 제공한다.


오라클은 group by와 order by절에 같은 컬럼이 지정될 경우, group by를 hash기법이 아닌 sort기법을 사용하게 된다. 이를 피하고, hash기법을 적용하여 group by로 처리하도록 하려면 

해당 sql문장중 order by를 제외한 모든 문장을 서브쿼리로 작성하고 힌트로 /*+ NO_MERGE */를 지정한다.

그런다음 그 겱과를 ORDER BY로처리. 데이터량이 많을 경우 이 방법이 가장 빠를 듯.

참고로, GROUP BY를 강제로 SORT기법을 쓰도록 하려면

/*+ OPT_PARAM('_GBY_HASH_AGGREGATION_ENABLED', 'false') */하면 됨





원본 출처는 Science of Database 블로그 SQL 튜닝방법론 입니다.

온라인 Select문 튜닝 방법론

온라인 SQL의 튜닝방법은 여러 가지가 있을 수 있습니다. 하지만 그 중에서 가장 기초적이고, 기본적인 방법입니다. 아래의 7가지 항목을 점검하고 약한 곳을 보강하면 됩니다. 이글은 SQL 튜닝책을 두 권정도 본 사람들을 위한 것입니다. 튜닝에 자신있는 사람들은 볼 필요가 없습니다.


1. 적절한 인덱스를 사용하여 Block I/O를 최소화 하라

조인이 없는 경우는 적절한 인덱스를 사용하는 것만으로도 상당한 효과를 볼 수 있습니다. 조인이 있는 경우는 특히 Driving(선행) 집합에 신경을 써야합니다. 왜냐하면 Nested Loop 조인을 사용했고, 선행집합의 건수가 많다면, 후행집합의 조인의 시도횟수가 증가하므로 성능이 느려지게 됩니다. 따라서 적절한 인덱스를 이용하여 선행집합의 건수를 줄인다면, 혹은 가장 적은 집합을 선행으로 놓는다면, 후행집합으로의 조인건수는 줄어들게 됩니다. 물론 이때에도 후행집합의 적절한 인덱스는 필수 조건입니다. Driving 집합의 Block I/O를 줄이기 위하여 최적화된 인덱스가 없다면 생성하고, 있다면 그것을 사용하십시오. 다시 말해 최적의 Access Path를 만드는 것이 좋습니다.  


운영중인 시스템이라면 최적의 Access Path를 위해 인덱스를 변경하거나 생성할 때는 주의해야 합니다. 현재 튜닝하고 있는 SQL에 최적화된 인덱스를 생성하더라도 다른 SQL에 악영향을 줄 수 있기 때문입니다. 인덱스를 생성하거나 변경할 때는 그 테이블을 사용하는 다른 SQL의 실행계획이 변경되지 않는지 각별히 신경써야 합니다. 이런 이유 때문에 개발과정에서 효율적인 인덱스 설계가 중요시 됩니다.


2. 조인방법과 조인순서를 최적화 하라.

온라인에서 사용하는 Select문은 좁은 범위를 검색하는 경우가 많습니다. 이럴때는 대부분 Nested Loop Join이 유리합니다. 그러므로 조인건수가 소량인 SQL에 Hash Join이나 Sort Merge Join이 발견되면 Nested Loop Join으로 변경하는 것이 더 유리한지 검토해야 합니다. 물론 여기서도 Nested Loop 조인에 관해서만 다룹니다.


Nested Loop 조인에서 가장 중요한 것은 조인 순서입니다. From절에 테이블(집합)이 두 개라면 후행집합의 관점에서는 적절한 인덱스만 존재한다면 그것으로 족합니다. 만약 From절에 테이블(집합)이 세 개 이상이라면 조인순서를 변경할 수 있는지에 대한 두 가지 원리를 사용하면 좋습니다. 두 가지 원리는 아래의 단락에서 소개됩니다. 아무리 조인할 집합이 많다고 하더라도 이 두 가지의 원리는 동일하게 적용될 수 있습니다. 두 가지 원리를 이용할 때 필요하다면 Leading 힌트를 사용해야 합니다.


첫번째, 후행집합에 적절한 인덱스가 없는 경우에 조인순서를 바꾸면, 최적의 인덱스를 사용할 수 있는 경우가 많습니다. 예컨대, 튜닝전의 조인순서가 A → B → C라고 하면, 중간 집합인 B에 적절한 인덱스가 없고 오히려 C에 적절한 인덱스가 존재하는 경우가 있습니다. 이럴 때는 B에 인덱스를 무작정 생성하지 말고, 조인순서를 A → C → B로 바꿀 수 있는지, 바꾸는 것이 더 효율적인지 검증하면 좋습니다. 조인 순서만 바꿔주어도 일량이 획기적으로 줄어드는 경우가 많습니다. 만약 조인순서를 바꿀 수 없거나, C를 중간집합으로 하는 것이 비효율적이라면, B를 중간집합으로 유지하고 적절한 인덱스를 사용해야 합니다.


두번째, 조인되는 집합 중 특정 인덱스에서 Block I/O가 증가하는 경우에 조인순서의 변경을 검토하면 됩니다. 이때 10046 Trace나 DBMS_XPLAN.Display_Corsor를 이용하면 조인집합들의 Block I/O량을 관찰할 수 있습니다. 예를 들어, 튜닝전에 조인순서가 A → B → C라고 하고, 집합 B에서 Block I/O량이 증가하면 A → C → B로 바꾸면 일량이 줄어드는 경우가 많습니다. C를 먼저 조인(Filter)하여 선행집합(B의 입장에서는 C가 선행이다)의 건수를 줄이고 B에 조인하면 성능이 향상됩니다.


3. Table Access(Random Access)를 최소화하라.

Random Access란 rowid로 테이블을 액세스하는 것을 말합니다. 1번과 2번을 최적화했다면 Random Access도 자동으로 많이 줄어들었을 것입니다. 하지만 그것이 끝은 아닙니다. 여전히 성능이 만족스럽지 못한다면 Random Access 횟수를 줄이는 것을 간과해서는 안됩니다.


인덱스를 사용하면 rowid가 자동으로 획득됩니다. 만약 인덱스에 없는 칼럼을 Select 해야 한다면 rowid로 테이블을 액세스 해야합니다. 이때 테이블로액세스해야 할 건수가 많고, 인덱스 컬럼순으로 테이블이 sort되어 있지 않다면 성능이 매우 저하됩니다. 왜냐하면 테이블이 인덱스 기준으로 sort되어 있지 않기 때문에 테이블을 방문할 때마다 서로 다른 블럭을 읽어야 하기 때문입니다.


비유적으로 설명해보겠습니다. 우리가 심부름을 할 때 세 군대의 상점(A, B, C)을 들러야 한다고 치겠습니다. 그 상점들이 모두 한 건물 내부에 존재한다면 얼마나 좋겠습니까? 그 심부름은 매우 빠른 시간에 끝날 것입니다. 하지만 반대로 상점 A는 부산에 있고 상점 B는 대구에 있고, 상점 C는 서울에 있다면? 만약 당신의 성격이 매우 좋아서 그 심부름을 한다고 해도 시간이 많이 걸릴 것입니다. Random Access도 마찬가지 입니다. 인덱스의 rowid로 테이블을 방문할 때 테이블이 인덱스기준으로 sort되어 상점처럼 다닥다닥 붙어있다면 성능은 매우 빠르고, 흩어져 있을수록 성능은 느려집니다. (오라클에서는 테이블이 인덱스 기준으로 sort되어 있는 정도를 Clustering Factor라고 합니다) 바로 이런 이유 때문에 index scan 보다는 Table Scan이 느린 것입니다. 따라서 우리는 Random Access의 부하를 최소화해야 합니다.


Random Access의 부하를 줄이는 방법은 네가지 입니다. 첫번째, 테이블의 종류를 변경하는 방법입니다. IOT나 클러스터를 이용하면 Clustering Factor가 극단적으로 좋아집니다. 또한 파티션을 이용하면 같은 범위의 데이터를 밀집시킬 수 있습니다. 두 번째, 효율적인 인덱스를 사용하거나 조인방법과 순서를 조정하여 Table Access를 최소화하는 방법입니다. 이 방법은 1번과 2번에서 이미 설명되었습니다. 세번째, 인덱스에 칼럼을 추가하여 Table Access를 방지하는 방법입니다. 예를 들어 Select절의 특정 칼럼 때문에 테이블이 액세스 된다면, 인덱스의 마지막에 그 컬럼을 추가하면 됩니다. 네 번째, 인덱스만 액세스하고 테이블로의 액세스는 모든 조인을 끝내고 마지막에 시도하여 Random Access의 횟수를 줄이는 방법입니다.



4. Sort나 Hash 작업을 최소화하라.

1,2,3번을 통하여 최적의 Access Path와 Join을 사용했다면, Block I/O의 관점에서는 튜닝이 끝난 것입니다. 하지만 1,2,3번이 모두 해결되었다 해도 Order by나 Group By 때문에 성능이 저하될 수 있습니다. 특히 결과가 많은 경우, sort는 치명적입니다.


인덱스가 sort 되어 있다는 특성을 이용하면 order by 작업을 대신할 수 있습니다. Group By도 sort가 발생하는데 group by 단위와 인덱스의 컬럼이 동일하다면 sort는 발생하지 않습니다. 최적의 인덱스를 사용하면 Access Path를 개선하는 효과뿐만 아니라 Sort의 부하도 없어집니다.


Union All을 제외한 집합연산(Union, Minus, Intersect)를 사용하면 Sort Unique 혹은 Hash Unique가 발생하게 됩니다. Union은 Union All로 바꿀수 없는지 검토해야 하고, Minus는 Not Exists 서브 쿼리를 이용하여 Anti Join으로 바꿀 수 없는지 고려해야 합니다. Intersect는 교집합이므로 조인으로 바꿀 수 있는지 검토해야 합니다아주 가끔 Distinct를 사용한 SQL이 눈에 띄는데 이 또한 Sort unique 혹은 Hash Unique를 발생시킵니다. 모델러나 설계자의 문의하여 Distinct를 제거할 방법이 없는지 문의해야 합니다.


Oracle 10g 부터는 Hash Group By가 발생할 수 있는데, 이미 적절한 인덱스를 사용하는 경우라면 Hash Group By를 사용할 필요는 없습니다. 이런 경우 NO_USE_HASH_AGGREGATION 힌트를 사용하면 Sort Group By 로 바꿀수 있습니다. 이렇게 해주면 실행계획에 "SORT GROUP BY NOSORT" Operation이 발생하며, Sort나 Hashing 작업이 전혀 발생하지 않습니다. Group By의 부하를 해결하는 또 하나의 방법은 스칼라 서브쿼리를 사용하는 것입니다. 조인을 사용하면 Sum 값을 구하기 위해 Group By가 필수적입니다. 하지만 스칼라 서브쿼리를 사용하면 Group By를 사용하지 않고도 sum이나 Min/Max 값을 구할 수 있습니다. 또한 분석함수의 Ranking Family(rank, dens_rank, row_number)를 최적화된 인덱스와 같이 사용하면 Group By나 Sort를 하지 않고도 Min/Max 값을 구할수 있습니다. 이때는 실행계획에 "WINDOW NOSORT" Operation이 발생합니다.



5. 한 블록은 한번만 Scan하고 끝내라.

같은 데이터를 반복적으로 Scan하는 SQL이 의외로 많습니다. 대표적인 경우가 Union All로 분리되었지만 실제로는 그럴 필요가 없는 경우입니다. 예를 들어 Where 절에 구분코드가 1일 때, 2일 때, 3일 때 별로 SQL이 나누어져 있는 경우 입니다. Where 절을 구분코드 in (1, 2, 3)으로 처리하고, Select절에서 Decode나 Case 문을 사용하여 구분코드별로 처리해 준다면 Union All은 필요 없습니다. Unon All을 사용하는 또 한가지의 경우는 Sub Total(소계)와 Grand Total(총계)를 구해야 하는 경우입니다. 이 경우도 Rollup/Cube나 Grouping Sets를 Group By절에 사용한다면 소계나 총계를 위한 별도의 Select문을 실행시킬 필요는 없습니다. 1~4번의 과정은 SQL문의 변경이 없거나 최소화됩니다. 하지만 5번의 경우는 SQL을 통합시켜야 하기 때문에 시간이 많이 소모되며, 많은 사고가 요구되는 창조적인 과정입니다. 여기까지 했다면 진행되었다면 원본 SQL 자체의 튜닝은 완료된 셈입니다.



6. 온라인의 조회화면이라면 페이징 처리는 필수다.

부분범위 처리를 해야합니다. 물론 전체 건을 처리해야 하는 경우는 있을 것입니다. 하지만 조회화면이라면 몇 십만건 혹은 몇 만건이나 되는 결과를 모두 볼 수 없습니다. 따라서 볼 수 있는 단우로 끊어서 출력해야 합니다. 예를 들어 결과 건수가 10만건이라고 해도 최초의 50건을 화면에 먼저 뿌린다면 1,2,3,4 번에서 설명했던 모든 부하(Block I/O의 부하, 조인의 부하, Random Access의 부하, Sort의 부하)를 한꺼번에 감소시킬 수 있습니다. 따라서 가능하면 개발자를 설득하거나 책임자를 설득하여 페이징처리를 하는 것이 바랍직합니다.


페이징 처리를 해도 효과를 볼 수 없는 몇가지 예외가 있습니다. 분석함수를 사용하건, Connect By + Start With를 사용한다면 페이징 처리의 효과는 없습니다. 분석함수의 경우 인라인뷰의 외부로 뺄 수 있다면 부분범위 처리가 가능합니다. Connect By + Start With를 사용한 경우는 부분범위처리가 불가능합니다. 인덱스의 구성을 적절히 하여 Sort를 대신해야 합니다.



7. 답이 틀리면 안된다. SQL을 검증하라

7번은 SQL 자체를 튜닝하는 것은 아닙니다. 하지만 7번을 튜닝 방법에 추가한 이유는 있습니다. 튜닝을 하였음에도 답이 틀린다면, 튜닝을 하지 않은 것보다 못합니다. 그러므로 튜닝 후에 답이 옳은지 항상 검증해야 합니다. 1번~7번 중에 가장 중요한 것이 7번입니다.



방법론 정리

1. 적절한 인덱스를 사용하여 Block I/O를 최소화하라.

2. 조인방법과 조인순서를 최적화 하라.

3. Table Access(Random Access)를 최소화하라.

4. Sort나 Gash 작업은 최소화하라.

5. 한 블록은 한번만 Scan하고 끝내라.

6. 온라인의 조회화면이라면 페이징처리는 필수이다.

7. 답이 틀리면 안된다. SQL을 검증하라.



만약 1~7번을 모두 적용할 수 있는 경우임에도 불구하고 하나라도 빠진다면 그것은 최적화된 SQL이 아닙니다. 물론 튜닝을 할때 위의 1~6번을 항상 적용할 수 있는 것은 아닙니다. 경우에 따라서는 하나만 적용될 수도 있고, 두 개만 적용할 수 있는 SQL도 있습니다. 하지만 1~6을 모두 적용할 수 있는지 꼼꼼히 살펴야 합니다.


위 항목들은 튜닝의 기본 중에 기본입니다. 이것들만 알아도 온라인 조회화면에 사용하는 SQL을 튜닝하는데 어려움이 없을 것입니다. 다시 말해 90%는 해결할 수 있습니다. 그렇다면 나머지 10%는 상황별로 다르게 처리돕니다. 또한 그것들은 책이나 매뉴얼에 나와있지 않기 때문에 경험치이거나 실험과 연구의 결과로 알아내는 것들입니다.






'(DB) Oracle 튜닝 > 쌍용튜닝교육' 카테고리의 다른 글

Oracle- Clustered Table  (0) 2018.02.10
DB 튜닝. hint 목록  (0) 2018.01.27
Oracle-튜닝.수행내역 이력을 파일로 저장  (0) 2018.01.27
Oracle-튜닝-index , block  (0) 2018.01.20
Oracle-실행계획  (0) 2018.01.16
Posted by 농부지기
,

[ Oracle-튜닝-수행내역 이력을 파일로 저장 ]


1. save

   - 정의 : 방금 수행한 sql 명령어를 c:\ind.sql 파일로 저장

    


2. @ind

   - 정의 : c:\ind.sql 파일을 실행하기

   


3. spool

   - 정의 : 다음부터 수행되는 sql문장들을 파일로 저장

   


   - 파일에 저장된 내용  

   




99. 파일로 관리함 좋은 명령어들

    - index 목록 조회, 파일 생성, drop index 실행 하기


remark   현재 상태의 화경을 저장(아래쪽에서 다시 on을 할 필요가 없음)

store set saved_settings replace


prompt tbl '테이블명입력:'


remark '구','신' 이라는 라인 조회 되지 않음

set verify off


remark  모든 메세지가 조회되지 않음

remark set termout off


remark  첫줄에 컬럼 heading안보이게 처리

set heading off  


remark 처리 결과 레코드수 안보이게 처리

set feedback off  


set autotrace off 

spool C:\drop_index.sql


SELECT 'drop index ' || index_name || ';'

  FROM user_indexes

 WHERE TABLE_NAME = '&tbl' AND UNIQUENESS = 'NONUNIQUE';


spool off;


remark set heading on

remark set feedback on

remark set autotrace on


remark 위쪽에서 저장된 환경설정을 다시 on 처리

@C:\saved_settings


remark drop_index.sql 파일을 실행

@C:\drop_index.sql

'(DB) Oracle 튜닝 > 쌍용튜닝교육' 카테고리의 다른 글

DB 튜닝. hint 목록  (0) 2018.01.27
Oracle-실행계획 개별  (0) 2018.01.27
Oracle-튜닝-index , block  (0) 2018.01.20
Oracle-실행계획  (0) 2018.01.16
튜닝-sql*plus 명령어 모음  (0) 2018.01.16
Posted by 농부지기
,