ORACLE動態採樣分析

動態採樣概念 css

 

動態採樣(Dynamic Sampling)是在ORACLE 9i Release 2中開始引入的一個技術,引入它的目的是爲了應對數據庫對象沒有分析(統計信息缺失)的狀況下,優化器生成更好的執行計劃。簡單的說,在數據庫段(表、索引、分區)對象沒有分析的狀況下,爲了使CBO優化器獲得足夠多的信息以保證優化器作出正確執行計劃而發明的一種技術。它會分析必定數量段對象上的數據塊獲取CBO須要的統計信息。動態採樣技術僅僅是統計信息的一種補充,它不能徹底替代統計信息分析。html

Dynamic sampling first became available in Oracle9i Database Release 2. It is the ability of the cost-based optimizer (CBO) to sample the tables a query references during a hard parse, to determine better default statistics for unanalyzed segments, and to verify its 「guesses.」 This sampling takes place only at hard parse time and is used to dynamically generate better statistics for the optimizer to use, hence the name dynamic sampling.
 
The purpose of dynamic sampling is to improve server performance by determining more accurate estimates for predicate selectivity and statistics for tables and indexes. The statistics for tables and indexes include table block counts, applicable index block counts, table cardinalities, and relevant join column statistics. These more accurate estimates allow the optimizer to produce better performing plans.
 

動態採樣在Oracle 11g以前稱爲 Dynamic Sampling, ORACLE 12c以後更名爲Dynamic Statistic. 數據庫

 

動態採樣介紹 express

若是要理解動態採樣,最好從鮮活的例子開始,向來理論都是枯燥乏味的。建立一個test表,總共有50319行數據。以下所示併發

SQL> create table test
  2  as 
  3   select owner, object_type
  4    from dba_objects;
 
Table created.
 
SQL> select count(1) from test;
 
  COUNT(1)
----------
     50319

咱們使用dynamic_sampling(test 0)提示(hints)來禁用動態採樣(稍後動態採樣級別中介紹),從下面的執行計劃能夠看出,在表對象沒有作分析狀況下,若是禁用了動態採樣,CBO優化器惟一可使用的信息爲該表存儲在數據字典的一些信息,好比多少個extent,多少個block等,這些信息每每不夠。此時優化器估計表test的行數爲11027(以下所示), 跟實際的表記錄行數50319仍是有蠻大的誤差。在複雜環境下,就頗有可能致使CBO優化器作出錯誤的執行計劃。oracle

SQL> set autotrace traceonly explain;
SQL> select /*+ dynamic_sampling(test 0) */ * from test;
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1357081020
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 11027 |   301K|    31   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| TEST | 11027 |   301K|    31   (0)| 00:00:01 |
--------------------------------------------------------------------------
SQL> set autotrace off;

clip_image001

若是啓用動態採樣(默認狀況下,動態採樣級別爲2),優化器根據動態採樣獲得一些數據信息猜想、估計表TEST的記錄行數爲48054,已經接近實際記錄行數50319了。比不作動態採樣分析要好不少了。固然你不能期望動態採樣獲取徹底準確的信息,由於它只是採樣了一些數據塊。app

SQL> set autotrace traceonly explain;
SQL> select * from test;
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1357081020
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 48054 |  1313K|    32   (4)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| TEST | 48054 |  1313K|    32   (4)| 00:00:01 |
--------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement
 
SQL> set autotrace off;

clip_image002

若是咱們將動態採樣的級別提升爲3,以下所示,發現優化器根據動態採樣獲得的信息比默認(默認狀況下,動態採樣級別爲2)狀況得到的信息更準確。優化器估計表TEST的行數爲51463,比48054又接近實際狀況一步了。ide

 
SQL> set autotrace traceonly explain;
SQL> select /*+ dynamic_sampling(test 3) */ * from test;
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1357081020
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 51463 |   703K|    32   (4)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| TEST | 51463 |   703K|    32   (4)| 00:00:01 |
--------------------------------------------------------------------------
SQL> set autotrace off;

clip_image003

在Tom的這篇文章中提到,在沒有動態採樣的狀況下,若是刪除了該表數據,CBO優化器估算的結果集和沒有刪除以前是同樣的。這是由於當一個表的數據被刪除後,這個表所分配的extent和block是不會自動回收的(高水位線不變),因此CBO若是沒有采樣數據塊作分析,只是從數據字典中獲取extend等信息,就會誤認爲任然還有那麼多數據。優化

SQL> delete from test;
 
50319 rows deleted.
 
SQL> commit;
 
Commit complete.
 
SQL> set autotrace traceonly explain;
SQL> select /*+ dynamic_sampling(test 0) */ * from test;
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1357081020
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 11027 |   301K|    31   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| TEST | 11027 |   301K|    31   (0)| 00:00:01 |
--------------------------------------------------------------------------
SQL> select * from test;
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1357081020
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |    28 |    31   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| TEST |     1 |    28 |    31   (0)| 00:00:01 |
--------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement
 
SQL> 

clip_image004

 

何時使用動態採樣? this

以下所示,咱們使用包dbms_stats.gather_table_stats收集表Test的統計信息事後,你會發現「dynamic sampling used for this statement」不見了,其實也就是說優化器發現有表TEST有分析過,它就不會使用動態採樣技術。其實開篇的時候已經敘說過「應對數據庫對象沒有分析(統計信息缺失)的狀況下,纔會用到動態採樣技術「

SQL> set autotrace trace exp;
SQL> select * from test;
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1357081020
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |    28 |    31   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| TEST |     1 |    28 |    31   (0)| 00:00:01 |
--------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement
 
SQL> exec dbms_stats.gather_table_stats(user, 'test');
 
PL/SQL procedure successfully completed.
 
SQL> select * from test;
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1357081020
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |    28 |    31   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| TEST |     1 |    28 |    31   (0)| 00:00:01 |
--------------------------------------------------------------------------

clip_image005

第二種狀況:當表TEST即便被分析過,若是查詢腳本里麪包含臨時表,就會使用動態採樣技術。由於臨時表是不會被分析,它是沒有統計信息的。以下所示

SQL> drop table test;
SQL> create table test
  2  as 
  3    select owner, object_type
  4   from dba_objects;
 
Table created.
 
SQL> exec  dbms_stats.gather_table_stats(user, 'test');
 
PL/SQL procedure successfully completed.
 
 
SQL> create global temporary table tmp
  2  (object_type varchar2(19));
 
Table created.
 
SQL> insert into tmp
  2  select distinct object_type from dba_objects;
 
41 rows created.
 
SQL> commit;
 
Commit complete.
 
SQL>  set autotrace traceonly explain;
 
SQL>  select t.owner, l.object_type
  2  from test t inner join tmp l on t.object_type =l.object_type;
 
Execution Plan
----------------------------------------------------------
Plan hash value: 19574435
 
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |    25 |    35   (6)| 00:00:01 |
|*  1 |  HASH JOIN         |      |     1 |    25 |    35   (6)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| TMP  |     1 |    11 |     2   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| TEST | 49422 |   675K|    32   (4)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("T"."OBJECT_TYPE"="L"."OBJECT_TYPE")
 
Note
-----
   - dynamic sampling used for this statement
 
SQL> 

clip_image006

動態採樣還有一個獨特能力,能夠對不一樣列之間的相關性作統計。表統計信息都是相對獨立的。當查詢涉及列之間的相關性時,統計信息就顯得有些不足了,請看Tom的例子

建立一個特殊的表t,而後對字段flag一、flag2建立索引t_idx,而後分析收集統計信息

SQL> create table t
  2     as select decode(mod(rownum,2),0,'N', 'Y') flag1,
  3               decode(mod(rownum,2),0,'Y', 'N') flag2, a.*
  4  from all_objects a;
 
Table created.
 
SQL> create index t_idx on t(flag1, flag2);
 
Index created.
 
SQL> begin
  2   dbms_stats.gather_table_stats(user, 'T',      
  3        method_opt =>'for all indexed columns size 254');
  4  end;
  5  /
 
PL/SQL procedure successfully completed.

關於表t的行數狀況以下所示,你們先不要糾結爲何查詢獲取NUM_ROWS數據

SQL> select num_rows, num_rows/2, num_rows/2/2
  2  from user_tables
  3  where table_name='T';
 
  NUM_ROWS NUM_ROWS/2 NUM_ROWS/2/2
---------- ---------- ------------
     49875    24937.5     12468.75

首先看看對flag1過濾條件的SQL語句,CBO優化器猜想、估計的行數24757, 至關接近24937.5記錄數了。

SQL> set autotrace traceonly explain;
SQL> select * from t where flag1='N';
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 24757 |  2345K|   161   (2)| 00:00:02 |
|*  1 |  TABLE ACCESS FULL| T    | 24757 |  2345K|   161   (2)| 00:00:02 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("FLAG1"='N')

首先看看對flag2過濾條件的SQL語句,CBO優化器猜想、估計的行數25118, 至關接近24937.5記錄數了。

SQL> select * from t where flag2='N';
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 25118 |  2379K|   161   (2)| 00:00:02 |
|*  1 |  TABLE ACCESS FULL| T    | 25118 |  2379K|   161   (2)| 00:00:02 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("FLAG2"='N')

若是條件flag1 = 'N' and flag2 = 'N',咱們根據邏輯推理判斷這樣的記錄確定是不存在的,這也是苦心構造這個特例的初衷。下面看看CBO優化器怎麼探測、預測的

SQL> select * from t where flag1 = 'N' and flag2 = 'N';
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 12468 |  1181K|   160   (2)| 00:00:02 |
|*  1 |  TABLE ACCESS FULL| T    | 12468 |  1181K|   160   (2)| 00:00:02 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("FLAG1"='N' AND "FLAG2"='N')

CBO估計的記錄數爲12468,和實際狀況相差很是遠。實際上是CBO優化器這樣估算來的:

flag1=‘N' 的記錄數佔總數的1/2

flag2= 'N' 的記錄數佔總數的1/2

根據NUM_ROWS/2/2 =12468.這樣顯然是不合理的。下面咱們經過提高動態採樣級別,來看看動態採樣是否能避免CBO的錯誤

SQL> select /*+ dynamic_sampling(t 3) */ * from t where flag1 = 'N' and flag2 = 'N';
 
Execution Plan
----------------------------------------------------------
Plan hash value: 470836197
 
-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |     4 |   388 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     |     4 |   388 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | T_IDX |     4 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("FLAG1"='N' AND "FLAG2"='N')
 
Note
-----
   - dynamic sampling used for this statement
 
SQL> 

clip_image007

 

動態採樣級別

ORACLE爲動態採樣劃分了11個級別,詳情請見ORACLE 11g官方文檔http://download.oracle.com/docs/cd/E11882_01/server.112/e10821/stats.htm#PFGRF94760

Table 13-10 Dynamic Statistics Levels

Level

When the Optimizer Uses Dynamic Statistics

Sample Size (Blocks)

0

Do not use dynamic statistics

不作動態採樣分析

n/a

1

Use dynamic statistics for all tables that do not have statistics, but only if the following criteria are met:

· There is at least 1 nonpartitioned table in the query that does not have statistics.

· This table has no indexes.

· This table has more blocks than the number of blocks that would be used for dynamic statistics of this table.

Oracle 對沒有分析的表進行動態採樣,但須要同時知足如下3個條件。

(1) SQL中至少有一個未分析的表(非分區表)

(2) 未分析的表沒有索引

(3) 未分析的表佔用的數據塊要大於動態採樣的數據塊(32個)

32

2

Use dynamic statistics if at least one table in the statement has no statistics. This is the default setting.

對全部的未分析表作分析,動態採樣的數據塊是默認數據塊數爲64

64

3

Use dynamic statistics if any of the following conditions is true:

·

· The statement meets level 2 criteria.

·

· The statement has one or more expressions used in the WHERE clause predicates, for example, WHERE SUBSTR(cust_last_name,1,3).

採樣的表包含知足Level 2定義的全部表,同時包括,那些謂詞有可能潛在地須要動態採樣的表,這些動態採樣的數據塊爲默認數據塊,對沒有分析的表,動態採樣的默認塊爲默認數據塊數量。

64

4

Use dynamic statistics if any of the following conditions is true:

·

· The statement meets level 3 criteria.

·

· The statement uses complex predicates (an OR or AND operator between multiple predicates on the same table).

採樣的表包含知足Level 3定義的表,同時還包括一些表,他們包含一個單表的謂詞會引用另外的2個列或者更多的列;採樣的塊數是動態採樣默認數據塊數;對沒有分析的表,動態採樣的數據塊爲默認數據塊的1倍。

64

5

Use dynamic statistics if the statement meets level 4 criteria.

採樣的表包含知足Level 4定義的表,同時分別使用動態採樣默認數據塊的2倍的數量來作動態分析。

128

6

Use dynamic statistics if the statement meets level 4 criteria.

採樣的表包含知足Level 4定義的表,同時分別使用動態採樣默認數據塊的4倍的數量來作動態分析。

256

7

Use dynamic statistics if the statement meets level 4 criteria.

採樣的表包含知足Level 4定義的表,同時分別使用動態採樣默認數據塊的8倍的數量來作動態分析。

512

8

Use dynamic statistics if the statement meets level 4 criteria.

採樣的表包含知足Level 4定義的表,同時分別使用動態採樣默認數據塊的32 倍的數量來作動態分析。

1024

9

Use dynamic statistics if the statement meets level 4 criteria.

採樣的表包含知足Level 4定義的表,同時分別使用動態採樣默認數據塊的128倍的數量來作動態分析。

4086

10

Use dynamic statistics if the statement meets level 4 criteria.

採樣的表包含知足Level 4定義的表,同時分別使用動態採樣對全部數據塊作動態分析。

All blocks

11

Use dynamic statistics automatically whenever the optimizer deems it necessary.

當優化器探測到須要的採樣時,對段段對象自動採樣

Automatically determined

採樣級別越高,採樣的數據塊越多,獲得的分析數據就越接近於真實,但同時伴隨着資源消耗的開銷也增長了。這時一個須要權衡考慮的東西。ORACLE 10 g & 11g的默認採樣級別都爲2,以下所示,通常使用在會話中使用dynamic_sampling提示來修改動態採樣級別。

SQL> show parameter optimizer_dynamic_sampling
 
NAME                               TYPE        VALUE
------------------------------ ----------- -----------
optimizer_dynamic_sampling         integer     2
SQL> 

另一個方式就是經過提示hints裏修改動態採樣的級別。這個很是靈活、有用。

 

動態採樣注意事項

凡事有利必有弊,動態採樣也不是神器。它採樣的數據塊越多,系統開銷就越大,這樣會增長SQL硬解析的時間,若是是數據庫倉庫(DW、OLAP)環境,SQL執行時間至關長,硬解析時間只佔整個SQL執行時間的一小部分,那麼能夠適當的提升動態採樣級別,這樣是有利於優化器獲取更加正確的信息。通常設置爲3或4比較合適。

可是在併發比較嚴重的OLTP系統中,每秒中有成千上萬的SQL語句執行,它要求SQL語句短小、執行時間短,因此在OLTP系統中應該減低動態採樣級別或不用動態採樣。能夠參考下面Tom的原文

 

When should I use dynamic sampling?」 is a tricky question. As with any other feature, there are times to use it and times to avoid it. So far I’ve concentrated on the 「goodness」 of dynamic sampling, and based on that, it seems that you should set the level to 3 or 4 and just let the optimizer always use dynamic sampling to validate its guesses.

That makes sense in an environment in which you spend most of your time executing SQL and very little of your overall time hard-parsing the SQL. That is, the SQL you are executing runs for a long time and the parse time is a small portion of the overall execution time, such as in a data warehousing environment. There, dynamic sampling at levels above the default makes complete sense. You are willing to give the optimizer a little more time during a hard parse (when sampling takes place) to arrive at the optimal plan for these complex queries.

That leaves the other classic type of environment: the online transaction processing (OLTP) system. Here, in general, you are executing queries thousands of times per second and spend very little time executing a given query—the queries are typically small and fast. Increasing the parse time in an OLTP system might well cause you to spend more time parsing than executing SQL. You do not want to increase the parse times here, so higher levels of dynamic sampling would not be advisable

 

參考資料

http://www.oracle.com/technetwork/issue-archive/2009/09-jan/o19asktom-086775.html

https://blogs.oracle.com/optimizer/entry/dynamic_sampling_and_its_impact_on_the_optimizer

[讓Oracle跑得更快]---譚懷遠

相關文章
相關標籤/搜索