12.1.0.0.2에서는 (NL <-> HASH), 병렬 조인 쿼리에서의 분배 방법(Broadcast, Range , Hash, Hybrid)에 대해서만 관여
Statistics Collector는 Adaptive Plan 사용 시에 옵티마이저에 의해서 생성되는 Row Source Operation의 일종이다. 최초 Plan 생성 시에 삽입되고 로우 버퍼링, 통계 정보 비교, 대안 PLAN 결정을 수행한다.
dbms_xplan.display_cursor
를 이용하여 조회 가능SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'all +note +adaptive'));
SQL> select substr(sql_text, 1, 50), is_resolved_adaptive_plan from v$sql
2 where is_resolved_adaptive_plan = 'Y'
SUBSTR(SQL_TEXT,1,50) I
---------------------------------------------------------------------------------------------------- -
SELECT /*+ CONNECT_BY_FILTERING */ s.privilege# FR Y
SELECT COUNT(*) FROM DBA_SCHEDULER_WINDOWS A, DBA_ Y
-- is_resolved_adaptive_plan 컬럼을 통해 SQL문이 기존의 계획인 Adaptive Plan인지를 파악하는데 도움 될 수 있음
SQL> create table testing_p(id number, the_value varchar2(30));
SQL> create index ix_testingP on testing_p(id);
SQL> create table testing_c(id_p number, id_t number, another_value varchar2(30));
SQL> create index ix_testingC_01 on testing_c(id_p);
SQL> create index ix_testingC_02 on testing_c(id_t);
SQL> create index ix_testingC_03 on testing_c(id_p, id_t);
SQL> select a.table_name, a.index_name, b.num_rows table_rows, a.num_rows index_rows
2 from user_indexes a, user_tables b
3 where a.table_name = b.table_name
4 and a.table_name like 'TESTING%';
TABLE_NAME INDEX_NAME TABLE_ROWS INDEX_ROWS
------------------------------ ------------------------------ ---------- ----------
TESTING_C IX_TESTINGC_02 0
TESTING_C IX_TESTINGC_03 0
TESTING_C IX_TESTINGC_01 0
TESTING_P IX_TESTINGP 0
SQL> select count(*)
2 from (
3 select b.id_t, b.id_p, b.another_value
4 from testing_P a, testing_c b
5 where a.id = b.id_p and a.id between 10001 and 10003);
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'all +note +adaptive'));
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 (100)|
| 1 | SORT AGGREGATE | | 1 | 26 | |
| 2 | NESTED LOOPS | | 1 | 26 | 0 (0)|
|* 3 | INDEX RANGE SCAN| IX_TESTINGP | 1 | 13 | 0 (0)|
|* 4 | INDEX RANGE SCAN| IX_TESTINGC_01 | 1 | 13 | 0 (0)|
--------------------------------------------------------------------------
-----
- dynamic statistics used: dynamic sampling (level=2)
-- 통계 정보를 수행하지 않은 상태에서 data insert
SQL> truncate table testing_p;
SQL> truncate table testing_c;
SQL> declare
2 begin
3 for tt in 1..1000 loop
4 insert into testing_p values(tt, 'www.testing.com');
5 commit;
6 end loop;
7 for tt in 1..1000 loop
8 insert into testing_p values(10001, 'www.testing.com');
9 commit;
10 end loop;
11 end;
12 /
SQL> declare
2 begin
3 for aa in (select id from testing_p)
4 loop for zz in 1..3 loop
5 insert into testing_c values(aa.id, zz, 'www.testing.com');
6 end loop;
7 end loop;
8 commit;
9
10 for zz in 4..1000
11 loop
12 insert into testing_c values(1001, zz, 'www.testing.com');
13 end loop;
14 commit;
15 end;
16 /
SQL> select count(*)
2 from (
3 select b.id_t, b.id_p, b.another_value
4 from testing_P a, testing_c b
5 where a.id = b.id_p and a.id between 10001 and 10003);
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'all +note +adaptive'));
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 (100)|
| 1 | SORT AGGREGATE | | 1 | 26 | |
| 2 | NESTED LOOPS | | 1 | 26 | 0 (0)|
|* 3 | INDEX RANGE SCAN| IX_TESTINGP | 1 | 13 | 0 (0)|
|* 4 | INDEX RANGE SCAN| IX_TESTINGC_01 | 1 | 13 | 0 (0)|
--------------------------------------------------------------------------
-----
- dynamic statistics used: dynamic sampling (level=2)
-- 통계 정보를 수행하지 않은 상태에서 data insert
SQL> truncate table testing_p;
SQL> truncate table testing_c;
SQL> declare
2 begin
3 for tt in 1..1000 loop
4 insert into testing_p values(tt, 'www.testing.com');
5 commit;
6 end loop;
7 for tt in 1..1000 loop
8 insert into testing_p values(10001, 'www.testing.com');
9 commit;
10 end loop;
11 end;
12 /
SQL> declare
2 begin
3 for aa in (select id from testing_p)
4 loop for zz in 1..3 loop
5 insert into testing_c values(aa.id, zz, 'www.testing.com');
6 end loop;
7 end loop;
8 commit;
9
10 for zz in 4..1000
11 loop
12 insert into testing_c values(1001, zz, 'www.testing.com');
13 end loop;
14 commit;
15 end;
16 /
SQL> select count(*)
2 from (
3 select b.id_t, b.id_p, b.another_value
4 from testing_P a, testing_c b
5 where a.id = b.id_p and a.id between 10001 and 10003);
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'all +note +adaptive'));
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 (100)|
| 1 | SORT AGGREGATE | | 1 | 26 | |
| 2 | NESTED LOOPS | | 1 | 26 | 0 (0)|
|* 3 | INDEX RANGE SCAN| IX_TESTINGP | 1 | 13 | 0 (0)|
|* 4 | INDEX RANGE SCAN| IX_TESTINGC_01 | 1 | 13 | 0 (0)|
--------------------------------------------------------------------------
-----
- dynamic statistics used: dynamic sampling (level=2)
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'TESTING_P', CASCADE => TRUE);
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'TESTING_C', CASCADE => TRUE);
SQL> SET AUTOTRACE ON
SQL> select count(*)
2 from (
3 select b.id_t, b.id_p, b.another_value
4 from testing_P a, testing_c b
5 where a.id = b.id_p and a.id between 10001 and 10003);
COUNT(*)
----------
3000000
Elapsed: 00:00:00.24
Execution Plan
----------------------------------------------------------
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 10 | 22 (55)|
| 1 | SORT AGGREGATE | | 1 | 10 | |
|* 2 | HASH JOIN | | 3000K| 28M| 22 (55)|
| 3 | NESTED LOOPS | | 3000K| 28M| 22 (55)|
| 4 | STATISTICS COLLECTOR | | | | |
|* 5 | INDEX RANGE SCAN | IX_TESTINGP | 1000 | 5000 | 3 (0)|
|* 6 | INDEX RANGE SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)|
|* 7 | INDEX FAST FULL SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)|
--------------------------------------------------------------------------------
Note
-----
- 'PLAN_TABLE' is old version
* 원래 위에서 "This is an Adaptive Plan"이라는 정보가 표시되어야 하는데, 안나옴
SQL> select count(*)
2 from (
3 select b.id_t, b.id_p, b.another_value
4 from testing_P a, testing_c b
5 where a.id = b.id_p and a.id between 10001 and 10003);
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'all +note +adaptive'));
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 22 (100)| |
| 1 | SORT AGGREGATE | | 1 | 10 | | |
| * 2 | HASH JOIN | | 3000K| 28M| 22 (55)| 00:00:01 |
|- 3 | NESTED LOOPS | | 3000K| 28M| 22 (55)| 00:00:01 |
|- 4 | STATISTICS COLLECTOR | | | | | |
| * 5 | INDEX RANGE SCAN | IX_TESTINGP | 1000 | 5000 | 3 (0)| 00:00:01 |
|- * 6 | INDEX RANGE SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)| 00:00:01 |
| * 7 | INDEX FAST FULL SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Note
-----
- statistics feedback used for this statement
- this is an adaptive plan (rows marked '-' are inactive)
{code:xml}
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'TESTING_P', CASCADE => TRUE);
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'TESTING_C', CASCADE => TRUE);
SQL> SET AUTOTRACE ON
SQL> select count(*)
2 from (
3 select b.id_t, b.id_p, b.another_value
4 from testing_P a, testing_c b
5 where a.id = b.id_p and a.id between 10001 and 10003);
COUNT(*)
----------
3000000
Elapsed: 00:00:00.24
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 10 | 22 (55)|
| 1 | SORT AGGREGATE | | 1 | 10 | |
|* 2 | HASH JOIN | | 3000K| 28M| 22 (55)|
| 3 | NESTED LOOPS | | 3000K| 28M| 22 (55)|
| 4 | STATISTICS COLLECTOR | | | | |
|* 5 | INDEX RANGE SCAN | IX_TESTINGP | 1000 | 5000 | 3 (0)|
|* 6 | INDEX RANGE SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)|
|* 7 | INDEX FAST FULL SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)|
--------------------------------------------------------------------------------
Note
-----
- 'PLAN_TABLE' is old version
* 원래 위에서 "This is an Adaptive Plan"이라는 정보가 표시되어야 하는데, 안나옴
SQL> select count(*)
2 from (
3 select b.id_t, b.id_p, b.another_value
4 from testing_P a, testing_c b
5 where a.id = b.id_p and a.id between 10001 and 10003);
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'all +note +adaptive'));
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 22 (100)| |
| 1 | SORT AGGREGATE | | 1 | 10 | | |
| * 2 | HASH JOIN | | 3000K| 28M| 22 (55)| 00:00:01 |
|- 3 | NESTED LOOPS | | 3000K| 28M| 22 (55)| 00:00:01 |
|- 4 | STATISTICS COLLECTOR | | | | | |
| * 5 | INDEX RANGE SCAN | IX_TESTINGP | 1000 | 5000 | 3 (0)| 00:00:01 |
|- * 6 | INDEX RANGE SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)| 00:00:01 |
| * 7 | INDEX FAST FULL SCAN | IX_TESTINGC_01 | 3000 | 15000 | 7 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Note
-----
- statistics feedback used for this statement
- this is an adaptive plan (rows marked '-' are inactive)
SQL> select count(*) from
2 (select /*+ parallel(4) full(a) full(b) */
3 b.id_t, b.id_p, b.another_value
4 from testing_p a, testing_c b
5 where a.id = b.id_p
6 and a.id between 10001 and 10003);
----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| TQ |IN-OUT| PQ Distrib |
----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 10 | 8 (38)| | | |
| 1 | SORT AGGREGATE | | 1 | 10 | | | | |
| 2 | PX COORDINATOR | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10002 | 1 | 10 | | Q1,02 | P->S | QC (RAND) |
| 4 | SORT AGGREGATE | | 1 | 10 | | Q1,02 | PCWP | |
|* 5 | HASH JOIN | | 3000K| 28M| 8 (38)| Q1,02 | PCWP | |
| 6 | PX RECEIVE | | 1000 | 5000 | 2 (0)| Q1,02 | PCWP | |
| 7 | PX SEND HYBRID HASH | :TQ10000 | 1000 | 5000 | 2 (0)| Q1,00 | P->P | HYBRID HASH|
| 8 | STATISTICS COLLECTOR | | | | | Q1,00 | PCWC | |
| 9 | PX BLOCK ITERATOR | | 1000 | 5000 | 2 (0)| Q1,00 | PCWC | |
|* 10 | TABLE ACCESS FULL | TESTING_P | 1000 | 5000 | 2 (0)| Q1,00 | PCWP | |
| 11 | PX RECEIVE | | 3000 | 15000 | 3 (0)| Q1,02 | PCWP | |
| 12 | PX SEND HYBRID HASH (SKEW)| :TQ10001 | 3000 | 15000 | 3 (0)| Q1,01 | P->P | HYBRID HASH|
| 13 | PX BLOCK ITERATOR | | 3000 | 15000 | 3 (0)| Q1,01 | PCWC | |
|* 14 | TABLE ACCESS FULL | TESTING_C | 3000 | 15000 | 3 (0)| Q1,01 | PCWP | |
----------------------------------------------------------------------------------------------------------------
IS_REOPTIMIZABLE
이라는 컬림 추가되어 Reoptimization 여부를 확인할 수 있다.PARALLEL_DEGREE_POLICY
파라미터를 ADAPTIVE로 설정하면 옵티마이저가 쿼리가 병렬로 실행되어야 하고 이를 선택했다면 쿼리에서 사용되는 병렬도의 값을 결정하는 설정이다.DBMS_SPD.FLUSH_SQL_PLAN_DIRECTIVE
는 메모리에서 영구적인 스토리지로 내려 쓸 수 있다.DBMS_APD.DROP_SQL_PLAN_DIRECTIVE
프로시저를 사용해서 SQL PLAN Directive를 삭제 할 수 있다.- 강좌 URL : http://www.gurubee.net/lecture/4269
- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^
- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.