Oracle/Tuning2019. 8. 26. 11:17

오라클은 데이터베이스는 실행계획을 식별할 수 있는 plan hash value를 제공하고 있다. 이번 글에서는 plan hash value가 어떤 값으로 계산되는지 살펴보자.


테스트를 위해 아래와 같이 테이블을 생성하자.

-- 1
DROP TABLE t1 PURGE;
CREATE TABLE t1 (c1 NUMBER, c2 NUMBER);
CREATE INDEX t1_x1 ON t1 (c1);
CREATE INDEX t1_x2 ON t1 (c2);

아래 예제는 동일한 쿼리를 11.2, 12.2 버전에서 수행했다. 1번 오퍼레이션이 달라 다른 plan hash value가 계산된 것을 확인할 수 있다. 2-3번 쿼리는 힌트를 통해 11.2 버전과 동일한 실행계획을 생성했다. plan hash value가 2-1번 쿼리와 동일한 확인할 수 있다. plan hash value는 버전과 무관하게 operation, options 값과 그 순서에 따라 계산된다고 추정할 수 있다.

-- 2-1 : 11.2
SELECT * FROM t1 WHERE c1 = 1;

SQL_ID  0n68umt5jgcbr, child number 0
-------------------------------------

Plan hash value: 1955407900

---------------------------------------------
| Id  | Operation                   | Name  |
---------------------------------------------
|   0 | SELECT STATEMENT            |       |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1    | -- !
|*  2 |   INDEX RANGE SCAN          | T1_X1 |
---------------------------------------------

-- 2-2 : 12.2
SELECT * FROM t1 WHERE c1 = 1;

SQL_ID  0n68umt5jgcbr, child number 0
-------------------------------------

Plan hash value: 711837727

-----------------------------------------------------
| Id  | Operation                           | Name  |
-----------------------------------------------------
|   0 | SELECT STATEMENT                    |       |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T1    | -- !
|*  2 |   INDEX RANGE SCAN                  | T1_X1 |
-----------------------------------------------------

-- 2-3 : 12.2
SELECT /*+ NO_BATCH_TABLE_ACCESS_BY_ROWID(T1) */ * FROM t1 WHERE c1 = 1;

SQL_ID  1p5ct9bw1rj4x, child number 0
-------------------------------------

Plan hash value: 1955407900

---------------------------------------------
| Id  | Operation                   | Name  |
---------------------------------------------
|   0 | SELECT STATEMENT            |       |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1    | -- !
|*  2 |   INDEX RANGE SCAN          | T1_X1 |
---------------------------------------------

-- 2-4 : 12.2
SELECT   sql_id, plan_hash_value, operation, options
    FROM v$sql_plan
   WHERE sql_id IN ('0n68umt5jgcbr', '1p5ct9bw1rj4x')
     AND operation = 'TABLE ACCESS'
ORDER BY sql_id;

SQL_ID        PLAN_HASH_VALUE OPERATION    OPTIONS
------------- --------------- ------------ ----------------------
0n68umt5jgcbr       711837727 TABLE ACCESS BY INDEX ROWID BATCHED
1p5ct9bw1rj4x      1955407900 TABLE ACCESS BY INDEX ROWID

2 행이 선택되었습니다.

아래 3-1번 쿼리는 c2 = 1 조건절을 기술했다. plan hash value가 1468581797로 변경되었다. 인덱스 명을 변경한 후 쿼리를 다시 수행해보면 값이 2327017684로 변경된다. plan hash value가 오브젝트 ID가 아닌 오브젝트 명에 따라 계산된다고 추정할 수 있다.

-- 3-1
SELECT * FROM t1 WHERE c2 = 1;

SQL_ID  a7dwm1zyqkjc6, child number 0
-------------------------------------
Plan hash value: 1468581797

---------------------------------------------
| Id  | Operation                   | Name  |
---------------------------------------------
|   0 | SELECT STATEMENT            |       |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1    |
|*  2 |   INDEX RANGE SCAN          | T1_X2 | -- !
---------------------------------------------

-- 3-2
ALTER INDEX t1_x2 RENAME TO t1_x3;

인덱스가 변경되었습니다.

-- 3-3
SELECT * FROM t1 WHERE c2 = 1;

SQL_ID  a7dwm1zyqkjc6, child number 0
-------------------------------------
Plan hash value: 2327017684

---------------------------------------------
| Id  | Operation                   | Name  |
---------------------------------------------
|   0 | SELECT STATEMENT            |       |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1    |
|*  2 |   INDEX RANGE SCAN          | T1_X3 | -- !
---------------------------------------------

다른 계정에서 동일한 쿼리를 수행한 후 V$SQL 뷰를 조회해보면 커서가 하나 더 생성된 것을 확인할 수 있다. plan hash value가 동일하므로 오브젝트 소유자는 plan hash value 계산과 무관하다고 추정할 수 있다.

-- 4-1
SELECT * FROM t1 WHERE c1 = 1;

SQL_ID  0n68umt5jgcbr, child number 0
-------------------------------------

Plan hash value: 1955407900

---------------------------------------------
| Id  | Operation                   | Name  |
---------------------------------------------
|   0 | SELECT STATEMENT            |       |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1    |
|*  2 |   INDEX RANGE SCAN          | T1_X1 |
---------------------------------------------

-- 4-2
SELECT sql_id, child_number, plan_hash_value, parsing_schema_name
  FROM v$sql
 WHERE sql_id = '0n68umt5jgcbr';

SQL_ID        CHILD_NUMBER PLAN_HASH_VALUE PARSING_SCHEMA_NAME
------------- ------------ --------------- -------------------
0n68umt5jgcbr            0      1955407900 TUNA
0n68umt5jgcbr            1      1955407900 SCOTT

2 행이 선택되었습니다.

-- 4-3
SELECT   child_number, operation, options, object_owner, object_name 
    FROM v$sql_plan
   WHERE sql_id = '0n68umt5jgcbr'
     AND object_owner IS NOT NULL
ORDER BY child_number, id;

CHILD_NUMBER OPERATION        OPTIONS        OBJECT_OWNER OBJECT_NAME
------------ ---------------- -------------- ------------ -----------
           0 TABLE ACCESS     BY INDEX ROWID TUNA         T1
           0 INDEX            RANGE SCAN     TUNA         T1_X1
           1 TABLE ACCESS     BY INDEX ROWID SCOTT        T1
           1 INDEX            RANGE SCAN     SCOTT        T1_X1

4 행이 선택되었습니다.

아래 5-1, 5-2번 쿼리는 조건절을 달리 기술했지만 plan hash value가 1955407900로 동일한 것을 확인할 수 있다. predicates 정보는 역시 plan hash value 계산과 무관하다고 추정할 수 있다.

-- 5-1
SELECT * FROM t1 WHERE c1 = 1;

SQL_ID  0n68umt5jgcbr, child number 0
-------------------------------------

Plan hash value: 1955407900

---------------------------------------------
| Id  | Operation                   | Name  |
---------------------------------------------
|   0 | SELECT STATEMENT            |       |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1    |
|*  2 |   INDEX RANGE SCAN          | T1_X1 |
---------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("C1"=1)

-- 5-2
SELECT * FROM t1 WHERE c1 = 2;

SQL_ID  ag73xyk6dgdmh, child number 0
-------------------------------------

Plan hash value: 1955407900

---------------------------------------------
| Id  | Operation                   | Name  |
---------------------------------------------
|   0 | SELECT STATEMENT            |       |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1    |
|*  2 |   INDEX RANGE SCAN          | T1_X1 |
---------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("C1"=2)

-- 5-3
SELECT sql_id, plan_hash_value, access_predicates
  FROM v$sql_plan
 WHERE sql_id IN ('0n68umt5jgcbr', 'ag73xyk6dgdmh')
   AND access_predicates IS NOT NULL;

SQL_ID        PLAN_HASH_VALUE ACCESS_PREDICATES
------------- --------------- -----------------
0n68umt5jgcbr      1955407900 "C1"=1
ag73xyk6dgdmh      1955407900 "C1"=2

결론적으로 plan hash value는 V$SQL_PLAN 뷰를 기준으로 operation, options, object_name 값과 그 순서에 의해 계산된다고 추정할 수 있다.

'Oracle > Tuning' 카테고리의 다른 글

NO FPD 뷰 성능 개선 방안  (0) 2019.09.02
고유 값에 대한 상관 서브 쿼리 성능 개선 방안  (0) 2019.08.28
MERGE JOIN CARTESIAN  (0) 2019.08.23
날짜 조회 안티 패턴 #3  (0) 2019.08.16
날짜 조회 안티 패턴 #2  (0) 2019.08.16
Posted by 정희락_