優化器革命之-Dynamic Sampling(二)

wei-xh發表於2014-07-18
說了這麼多,我們先來看一下動態取樣的威力到底如何?
create table t as
select mod(num, 100) c1, mod(num, 100) c2, mod(num, 75) c3, mod(num, 30) c4
  from (select level num from dual connect by level <= 10001);
 
建立了一張表T,欄位C1和C2的值保持聯動。
select c1,c2 from t where rownum<20;


        C1         C2
---------- ----------
         1          1
         2          2
         3          3
         4          4
         5          5
         6          6
         7          7
         8          8
         9          9
        10         10
        11         11
        12         12
        13         13
        14         14
        15         15
        16         16
        17         17
        18         18
        19         19
表不收集統計資訊,我們先來看看只查詢C1列的情況: 
select count(*) from t where c1 = 10;


  COUNT(*)
----------
       100


1 row selected.




Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522


---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |    13 |     8   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |    13 |            |          |
|*  2 |   TABLE ACCESS FULL| T    |   100 |  1300 |     8   (0)| 00:00:01 |
---------------------------------------------------------------------------


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


   2 - filter("C1"=10)


Note
-----
   - dynamic sampling used for this statement (level=2)

非常好,動態取樣已經非常精準的估計出了返回的基數為100,跟實際值之間絲毫無差。當然你的環境下可能會遭遇些許的誤差,這是正常的。
我們收集一下表的統計資訊:   
begin
  dbms_stats.gather_table_stats(ownname          =>'test',
                                tabname          => 't',
                                no_invalidate    => FALSE,
                                estimate_percent => 100,
                                force            => true,
                                degree         => 5,
                                method_opt       => 'for  all  columns size 1',
                                cascade          => true);
end;
/

select count(*) from t where c1 = 10 and c2=10;


COUNT(*)
----------
       100


Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522


---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     6 |     8   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |     6 |            |          |
|*  2 |   TABLE ACCESS FULL| T    |     1 |     6 |     8   (0)| 00:00:01 |
---------------------------------------------------------------------------

其實通過兩個謂詞過濾後,有100條的記錄符合條件,但是優化器根據資料字典裡的統計資訊評估後,認為只返回1條記錄。
因為優化器並不知道C1,C2的值是聯動的,它只會傻傻的按照公式來計算基數:
cardinality = (selectivity c1) *???? (selectivity c2) *???? # rows in table
           =  1/100   * 1/100  * 10001 = 1
我們把取樣級別設定為4(在設定為4的情況下,由於我們的查詢是多謂詞查詢,即使表上有統計資訊,也會使用到動態取樣)
alter session set optimizer_dynamic_sampling=4;


Session altered.


set autotrace on


select count(*) from t where c1 = 10 and c2=10;


  COUNT(*)
----------
       100


1 row selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522


---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     6 |     8   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |     6 |            |          |
|*  2 |   TABLE ACCESS FULL| T    |   100 |   600 |     8   (0)| 00:00:01 |
---------------------------------------------------------------------------


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


   2 - filter("C1"=10 AND "C2"=10)


Note
-----
   - dynamic sampling used for this statement (level=4)

GOOD,優化器使用動態取樣後,精確的估計出了基數值。如果你使用的是11G之後的版本,還可以通過擴充套件的統計資訊收集來解決多列之間有資料依賴的問題:

select dbms_stats.create_extended_stats(ownname=>user,
tabname => 'DEPEND_TEST', extension => '(c1, c2)' ) AS c1_c2_correlation
from dual ;
   
begin
  dbms_stats.gather_table_stats(ownname          =>'test',
                                tabname          => 't',
                                no_invalidate    => FALSE,
                                estimate_percent => 100,
                                force            => true,
                                degree         => 5,
                                method_opt       => 'for  all  columns size 1',
                                cascade          => true);
end;
/


alter session set optimizer_dynamic_sampling=2;


select count(*) from t where c1 = 10 and c2=10;


  COUNT(*)
----------
       100


1 row selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522


---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     6 |     8   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |     6 |            |          |
|*  2 |   TABLE ACCESS FULL| T    |   100 |   600 |     8   (0)| 00:00:01 |
---------------------------------------------------------------------------


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


   2 - filter("C1"=10 AND "C2"=10)

我們看到通過11G的擴充套件統計資訊收集也解決了多列之間有資料依賴情況下,基數計算不準的問題,但是,但是擴充套件的統計資訊只對於做等值查詢有效,我們看看下面的情況:
select c1,c2 from t where c1 = 10 and c2>10;


no rows selected

Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873


--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |    90 |   540 |     8   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T    |    90 |   540 |     8   (0)| 00:00:01 |
--------------------------------------------------------------------------


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


   1 - filter("C1"=10 AND "C2">10)

雖然我們收集了擴充套件的統計資訊,但是由於C2做的是非等值查詢,擴充套件的統計資訊失效了。這個時候我們依然只能求助於動態取樣了。
alter session set optimizer_dynamic_sampling=4;

select c1,c2 from t where c1 = 10 and c2>10;


no rows selected




Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873


--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     6 |     8   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T    |     1 |     6 |     8   (0)| 00:00:01 |
--------------------------------------------------------------------------


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


   1 - filter("C1"=10 AND "C2">10)


Note
-----
   - dynamic sampling used for this statement (level=4)

動態取樣總是那麼好使,依然為我們計算出了精準的基數。

需要注意的是,手工設定統計資訊的表,不管是在session/system級,還是在cursor級,還是在segment級別啟用動態取樣,都將不會有效:

begin
  dbms_stats.set_table_stats(ownname => user,
                             tabname => 't',
                             numrows => 100);
end;
/
上面對錶T進行了行數的統計資訊設定。

select /*+ DYNAMIC_SAMPLING(10) */
               count(*) as cnt
    from
               t
    where
               attr1 = 1
    and        id > 0
    ;


       CNT
----------
    100000

Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522


---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |    26 |    24   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |    26 |            |          |
|*  2 |   TABLE ACCESS FULL| T    |     1 |    26 |    24   (0)| 00:00:01 |
---------------------------------------------------------------------------


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


   2 - filter("ATTR1"=1 AND "ID">0)
取樣了級別為10,由於查詢條件是多個謂詞,即使表上存在統計資訊也符合動態取樣的條件(前面已經有過論述),但是執行計劃的輸出表示,這個查詢沒有使用到動態取樣。10053的跟蹤結果也說明了這一點。這裡不再貼出。
我們重新收集統計資訊看看:


test@DLSP>begin
  2    dbms_stats.gather_table_stats(ownname          =>'test',
  3                                  tabname          => 't',
  4                                  no_invalidate    => FALSE,
  5                                  estimate_percent => 100,
  6                                  force            => true,
  7                                  degree         => 5,
  8                                  method_opt       => 'for  all  columns size 1',
  9                                  cascade          => true);
 10  end;
 11  /


PL/SQL procedure successfully completed.


test@DLSP>set autotrace on
test@DLSP>select /*+ DYNAMIC_SAMPLING(10) */
  2                 count(*) as cnt
  3      from
  4                 t
  5      where
  6                 attr1 = 1
  7      and        id > 0
  8      ;




       CNT
----------
    100000


Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522


---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     8 |  3537   (1)| 00:00:43 |
|   1 |  SORT AGGREGATE    |      |     1 |     8 |            |          |
|*  2 |   TABLE ACCESS FULL| T    |   100K|   781K|  3537   (1)| 00:00:43 |
---------------------------------------------------------------------------


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


   2 - filter("ATTR1"=1 AND "ID">0)


Note
-----
   - dynamic sampling used for this statement (level=10)

可以看到優化器已經使用了動態取樣,執行計劃輸出的Note部分表明了這一點。我們看看能不能騙過ORACLE,在統計資訊收集後,再手工設定統計資訊,看看能不能動態取樣。


test@DLSP>begin
  2    dbms_stats.set_table_stats(ownname => user,
  3                               tabname => 't',
  4                               numrows => 100);
  5  end;
  6  /


PL/SQL procedure successfully completed.


test@DLSP>select /*+ dynamic_sampling(5) */
  2                 count(*) as cnt
  3      from
  4                 t
  5      where
  6                 attr1 = 1
  7      and        id > 0
  8      ;


       CNT
----------
    100000


1 row selected.




Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522


---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     8 |  3519   (1)| 00:00:43 |
|   1 |  SORT AGGREGATE    |      |     1 |     8 |            |          |
|*  2 |   TABLE ACCESS FULL| T    |    10 |    80 |  3519   (1)| 00:00:43 |
---------------------------------------------------------------------------


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


   2 - filter("ATTR1"=1 AND "ID">0)
可以看到沒有使用到,ORACLE不是那麼好騙的。
這一點要引起注意,如果你的統計資訊是手工設定的,動態取樣技術將不能為你服務。
文章有點長了,請關注下一篇!!

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22034023/viewspace-1222339/,如需轉載,請註明出處,否則將追究法律責任。

相關文章