Oracle Histogram (直方圖)

--====================

-- Oracle histogram 

--====================

直方圖意義:在oracle數據庫中,CBO會默認認爲目標列的數據量在其最小值和最大值之間是均勻分佈的(最小值最大值不準確會導致謂詞越界),並且會按照這個均勻分佈原則來計算對目標列事假的where查詢條件後的可選這率及結果集的cardinality,進而據此來計算成本值並選擇執行計劃。但是,目標列的數據是均勻分佈的按照這個原則選擇執行計劃是正確的;如果目標數據列分佈不均勻,甚至是嚴重傾斜,分佈極度不均勻,那麼這個按照這個原則選擇執行計劃就不合適,甚至是錯誤的,爲此我們需要對那些數據分佈不均勻的列進行直方圖收集。

直方圖是一種列的特殊的統計信息,主要用來描述列上的數據分佈情況。當數據分佈嚴重傾斜時,直方圖可以用小的提升cardinality評估的準確度。構造直方圖最主要的原因就是幫助優化器在表中數據驗證傾斜是做出最好的選擇。例如,表中的某個列上數據佔據了整個表的80%(數據分佈傾斜),相關的索引就可能無法幫助減少滿足查詢所需的I/O數量。創建直方圖可以讓基於成本的優化器知道何時使用索引才最合適。

直方圖實際存儲在數據字典sys.histgrm$中,可以通過數據字典dba_tab_historgrams,dba_part_histograms和dba_subpart_histograms來分別查看錶,分區表的分區和分區表的子分區的直方圖信息。

直方圖使用場景:

(1) 當where字句引用了列值分佈存在傾斜時。當這種偏差相當明顯時,以至於where字句中的值將會使優化器選擇不同的執行計劃。這時應該使用直方圖來幫助優化器來修正執行路徑。(如查詢不使用該列,則在該列上創建直方圖也沒有意義)

(2)當列值導致不正確的判斷時,這種情況通常發生在多表鏈接時。當多表鏈接查詢時,oracle會自動判斷哪些結果集作爲驅動表,如果傾斜列上有直方圖可以幫助優化器做出正確的選擇。

 

直方圖分類:

Oracle數據庫裏的直方圖使用了一種稱爲Bucket(桶)的方式來描述目標列的數據分佈。bucket是一個邏輯上的概念,相當於分組,每個bucket就是一組,每個bucket裏會存儲一個或多個目標的數據。oracle會用兩個維度來描述一個bucket,這兩個維度是endpoint_number和endpoint_value,oracle會將每個bucket的這兩個維度記錄在數據字典基表sys.histgrm$中。列的直方圖類型可以通過查詢dba_tab_col_statistics的histogram列來獲取,一般情況下包含3類,NONE,FREQUENCY(頻率直方圖,也叫等頻直方圖),height balanced(高度平衡直方圖,也叫等高直方圖)。在12c中 新添了頂級頻率直方圖(Top Frequency Histogram)和 混合直方圖(Hybrid Histogram)。

 

(1)頻率直方圖(Frequency,Freq):

在oracle 12c之前,在目標列的數據分佈是傾斜的情況下(即存儲在數據字典裏的目標的distinct值得數量小於目標表的記錄數),如果存儲在數據字典裏描述目標直方圖的bucket數量等於目標列的distinct值得數量,那麼這種類型的直方圖就是頻率(frequency)直方圖。

頻率直方圖只適用於目標列的distinct值小於或者等於254的情形。在12c中,直方圖的bucket數量可以大於254。對於頻率直方圖而言,目標列直方圖的bucket數量就等於列的distinct數值,此時目標咧有多少distinct值,oracle在數據字典dba_tab_histograms,dba_part_histograns,dba_subpart_histograms(分別對應表,分區別,子分區表直方圖信息)中就會存儲多少條信息,每一條記錄就代表了對其中的一個bucket的描述,上述數據字典中的字段endpoint_value記錄了這些distinct值,而字段endpoint_number則記錄了到此distinct值爲止總共有多少條記錄。需要注意的是,對頻率直方圖而言,endpoint_number是一個累加值,可以使用一條記錄的endpoint_number 值減去它上一條記錄的endpoint_number值來得到這條記錄本身所對應的endpoint_value值記錄數,

select deptno,count(1),sum(count(1)) over(order by deptno range unbounded preceding) idg from dept group by deptno;
SQL> select table_name,column_name,endpoint_number,endpoint_value,nvl((endpoint_number-(lag(endpoint_number) over (order

by endpoint_value))),endpoint_number) counts from dba_tab_histograms where table_Name='DEPT';

TABLE_NAME COLUMN_NAM ENDPOINT_NUMBER ENDPOINT_VALUE COUNTS

---------- ---------- --------------- -------------- ----------

DEPT DEPTNO 0 10 0

DEPT DEPTNO 1 40 1

DEPT DNAME 0 3.3886E+35 -1

DEPT LOC 0 3.4430E+35 0

DEPT LOC 1 4.0641E+35 1

DEPT DNAME 1 4.3229E+35 0



已選擇6行。

 

關於直方圖的一些注意事項:

(1)對於超過32個字符的列,超出的部分無法在直方圖中收集,這個缺陷會影響CBO優化器對結果集的評估。

(2)數字和日期在直方圖上被精確表示。

(3)如果目標列的數據是均衡分佈的(主鍵列,唯一索引),那麼就不需要對相關列收集直方圖。

(4)對於那些沒有在where 中出現的列,對其收集直方圖統計信息沒有任何用途。oracle只會對那些在謂詞中使用過的列收集直方圖。sys.col_usage$中記錄相關列的使用記錄( INTCOL#記錄),如果沒有相關記錄則目標列沒有使用過,就不會對其收集直方圖。

(5)如果某個列在where條件中從未出現,那麼在sys.col_usage$中就不會有這個列的使用記錄,那麼數據庫默認就不會去收集這個列的直方圖信息

(6)如果目標的distinct值得數量跟目標表的記錄數量相同(數據不傾斜),即使該目標列在sys.col_usage$中有使用記錄,那麼oracle默認也不會去收集該列的直方圖統計信息。

(7)oracle是怎麼來判斷某個列的數據是否傾斜呢?oracle採用了一種很簡單的方法來判斷某列是否傾斜,就是判斷該列的DISTINCT值得數量是否很表記錄數是否相同,相差較大就是嚴重傾斜。

(8)如果目標列的Distinct值的數量跟目標表記錄是相同(數據不傾斜),即使該列在sys.col_uage$中有使用記錄,那麼oracle默認也不會收集次列的直方圖信息。

(9)“在手工收集直方圖統計的時候,如果手動指定buclet的數據量等於目標咧的distinct值的數量,且這個值小於254的話,那麼oracle收集的直方圖信息的類型就是頻率直方圖(Frequence)”。

(10)在12c之前的版本,對於頻率直方圖,目標列的distinct值不能大於254。

(11)需要注意的是 直方圖收集會影響cursor sharing的共享。

 

 

模擬環境:

 

SQL> select name,count(1) from t1 group by name;

 

NAME COUNT(1)

-------------------- ----------

1 10000

2 1

 

刪除列直方圖:

exec dbms_stats.gather_table_stats('plat2','t1',method_opt=>'for solumns name size 1',cascade=>true);

 

刪除表直方圖信息

SQL> exec dbms_stats.delete_table_stats('PLAT2','T1');

 

PL/SQL 過程已成功完成。

 

SQL> select table_name,column_name,endpoint_number,endpoint_value from dba_tab_histograms where table_name='T1';

 

未選定行

 

SQL> select column_name,num_distinct,num_nulls,num_buckets,histogram from dba_tab_col_statistics where table_name='T1';

 

未選定行

 

列對象使用情況

SQL> select INTCOL#,EQUALITY_PREDS,obj# from sys.col_usage$ where obj#=74602;

 

INTCOL# EQUALITY_PREDS OBJ#

---------- -------------- ----------

2 3 74602

 

收集列對象直方圖

SQL> exec dbms_stats.gather_table_stats('PLAT2','T1',method_opt=>'for columns name size auto',cascade=>true);

 

PL/SQL 過程已成功完成。

 

使用列對象

SQL> select * from t1 where name=2;

 

ID NAME

---------- --------------------

2 2

 

 

列對象使用情況

SQL> select INTCOL#,EQUALITY_PREDS,obj# from sys.col_usage$ where obj#=74602;

 

INTCOL# EQUALITY_PREDS OBJ#

---------- -------------- ----------

2 4 74602

 

查詢列對象直方圖信息

SQL> select column_name,num_distinct,num_nulls,num_buckets,histogram from dba_tab_col_statistics where table_name='T1';

 

COLUMN_NAM NUM_DISTINCT NUM_NULLS NUM_BUCKETS HISTOGRAM

---------- ------------ ---------- ----------- ------------------------------

NAME 2 0 2 FREQUENCY

 

以上可見,直方圖收集起了作用,而且收集的直方圖列對象必須在謂詞where中使用過才能收集。

 

SQL> select table_name,column_name,endpoint_number,endpoint_value from dba_tab_histograms where table_name='T1

 

TABLE_NAME COLUMN_NAM ENDPOINT_NUMBER ENDPOINT_VALUE

---------- ---------- --------------- --------------

T1 NAME 10001 2.5961E+35

T1 NAME 10000 2.5442E+35

 

 

(2)高度平衡直方圖(Height Balanced,HtBal)

如果存儲在數據字典描述目標列直方圖的bucket的數量小於目標列的distinct值的數量,這種類型的直方圖就稱之爲高度平衡直方圖(Height Balanced)。當distinct值大於254,那麼只能使用高度平衡直方圖。

如果目標列distinct值大於254,收集直方圖的時候會自動轉化成平衡直方圖。

在dba_tab_histograms視圖中,endpoint_number代表桶,qie自動省略去endpoint_value值相同切endpoint_number相鄰的桶的值。

endpoint_value表示每一個桶的最大值,

 

SQL> select column_name,num_distinct,num_nulls,num_buckets,histogram from dba_tab_col_statistics where table_name='T3';

 

COLUMN_NAM NUM_DISTINCT NUM_NULLS NUM_BUCKETS HISTOGRAM

---------- ------------ ---------- ----------- ------------------------------

ID 2000 0 254 HEIGHT BALANCED

 

 

 

收集直方圖信息:

默認情況下,數據庫會爲實例收集基本的統計信息,但是不會收集直方圖信息。

通常使用 dbms_stats包method_opt參數來創建直方圖。

method_opt參數:

for all [indexd | hidden ] columns [size_number]

for columns [size_number] column|attribute [szie_number] [,column|attrobute [size_number].....]

其中size_num 必須符合一下格式:

skewonly :只對數據分佈不均衡的列收集直方圖統計信息

repeat(重複):只對已經存在的直方圖統計信息的列收集直方圖

auto :自行決定對那些列收集直方圖統計信息

integer:直方圖的bucket數量,在1-254範圍內,1表示刪除該目標上的直方圖統計信息

method_opt默認參數是 " for all columns size auto"," for all columns size 1"表示刪除所有列的直方圖統計信息。

如下是一些常用收集方式:

for all indexed columns size auto:對錶所有索引的列自動收集直方圖統計信息

for columns size auto a b:對錶的列a列b自動收集直方圖統計信息

for columns size 10 a b :對錶列 a列b 收集直方圖統計信息,同時制定bucket爲10

for columns a size 10 b size 5 :對錶列 a列b 收集直方圖統計信息,指定列a bucket位10,列b bucket爲5

for columns a size 1:刪除列a的直方圖統計信息

dbms_stats.delete_columns_stats('SH','SALES','sal_id',col_stat_type=>'histogram');刪除表列直方圖統計信息,其中col_stat_type默認值爲ALL。

 

 

 

 

 

 

 

 

 

複合直方圖:

收集表外部信息:

由於oracle系統默認表中各列之間的關係不變。但是日常工作中,收集多列的直方圖信息,各列的關係會隨着條件改變而改變,創建擴展信息就是收集指定列之前的關係,從而判斷出結果集傾斜的結果,做出正確的判斷。

select dbms_stats.create_extended_stats('sh’,‘SALES','(cust_city,cust_state_province,country_id)') from dual;

 

收集列直方圖:

exec dbms_stats.gather_table_stats('hr','sales',method_opt=>'for columns (cust_city,cust_state_province,country_id) size auto )';

 

收集表統計信息:

exec dbms_stats.gather_table_stats(’sh',‘sales',cascade=>true);

 

查詢列直方案圖信息:

select column_name,num_buckets,histogram from dba_tab_col_statistics where table_name='T1' ;

 

直方圖實例:

create table t1 (id number,name varchar2(10));

 

 

SQL> declare

cnt number(5) :=1;

begin

loop

insert into t1 values(1,1);

if cnt=10000 then

exit;

end if;

cnt :=cnt+1;

end loop;

insert into t1 values(2,2);

commit;

end;

/

 

數據分佈:

SQL> select name,count(1) from t1 group by name;

 

NAME COUNT(1)

-------------------- ----------

1 10000

2 1

 

 

SQL> select sql_id,sql_text from v$sql where sql_text like '%t1%';

 

SQL_ID

--------------------------

SQL_TEXT

--------------------------------------------------------------------------------

4jt9tpc59uqgq

select * from t1 where name='2'

 

 

SQL> select * from table(dbms_xplan.display_cursor('4jt9tpc59uqgq',0,'advanced'));

 

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

SQL_ID 4jt9tpc59uqgq, child number 0

-------------------------------------

select name,count(1) from t1 group by name

 

Plan hash value: 136660032

 

--------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

--------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | | | 7 (100)| |

|* 1 | TABLE ACCESS FULL| T1 | 5001 | 25005 | 7 (0)| 00:00:01 |

 

Query Block Name / Object Alias (identified by operation id):

-------------------------------------------------------------

 

1 - SEL$1

2 - SEL$1 / T1@SEL$1

 

Outline Data

-------------

 

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

 

/*+

BEGIN_OUTLINE_DATA

IGNORE_OPTIM_EMBEDDED_HINTS

OPTIMIZER_FEATURES_ENABLE('11.2.0.1')

DB_VERSION('11.2.0.1')

ALL_ROWS

OUTLINE_LEAF(@"SEL$1")

FULL(@"SEL$1" "T1"@"SEL$1")

USE_HASH_AGGREGATION(@"SEL$1")

END_OUTLINE_DATA

 

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

*/

 

Column Projection Information (identified by operation id):

-----------------------------------------------------------

 

1 - "NAME"[VARCHAR2,10], COUNT(*)[22]

2 - "NAME"[VARCHAR2,10]

 

 

已選擇41行。

從執行計劃可以看到,走了全表掃描,這顯然是不正確的,‘name=2’只有一條記錄,此時走索引掃描最高效。由於CBO在分析的時候,name列只有2個值,所以選擇率10001的1/2,也就是5001超過了總數一半,所以此時oracle認爲返回數據超過了總數一半,走全表掃描最有效。其實,實際上,對name列的分佈嚴重不均勻,評估嚴重失誤,從以上執行計劃可以走全表掃描返回基數5001,其實應該是1,這個時候我們可以對列name收集直方圖,來讓CBO統計的時候可以讀取列分佈情況。

 

SQL> exec dbms_stats.gather_table_stats('PLAT2','T1',method_opt=>'for columns name size auto',cascade=>true);

 

SQL> select * from table(dbms_xplan.display_cursor(null,0,'advanced'));

 

 

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

SQL_ID ab1q9qzvfj12d, child number 0

-------------------------------------

select * from t1 where name=2

 

Plan hash value: 3617692013

 

--------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

--------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | | | 7 (100)| |

|* 1 | TABLE ACCESS FULL| T1 | 1 | 5 | 7 (0)| 00:00:01 |

 

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

--------------------------------------------------------------------------

 

Query Block Name / Object Alias (identified by operation id):

-------------------------------------------------------------

 

1 - SEL$1 / T1@SEL$1

 

Outline Data

-------------

 

/*+

 

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

BEGIN_OUTLINE_DATA

IGNORE_OPTIM_EMBEDDED_HINTS

OPTIMIZER_FEATURES_ENABLE('11.2.0.1')

DB_VERSION('11.2.0.1')

ALL_ROWS

OUTLINE_LEAF(@"SEL$1")

FULL(@"SEL$1" "T1"@"SEL$1")

END_OUTLINE_DATA

*/

 

Predicate Information (identified by operation id):

 

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

---------------------------------------------------

 

1 - filter(TO_NUMBER("NAME")=2)

 

Column Projection Information (identified by operation id):

-----------------------------------------------------------

 

1 - "T1"."ID"[NUMBER,22], "NAME"[VARCHAR2,10]

 

 

已選擇42行。

 

以上可見,收集直方圖之後,執行計劃按照預想方式執行。

 

系統會默認採集直方圖信息,爲了保持系統穩定性我們建議對已存在的直方圖信息才收集,其餘的手動收集。

查看系統默直方圖收集範圍,修改爲對已經存在的直方圖收集信息:

SQL> select dbms_stats.get_param('method_opt') from dual;

 

DBMS_STATS.GET_PARAM('METHOD_OPT')

--------------------------------------------------------------------------------

FOR ALL COLUMNS SIZE AUTO

修改成對已存在的直方圖收集

SQL> exec dbms_stats.set_param('method_opt','for all columns size repeat');

 

PL/SQL procedure successfully completed.

 

SQL> select dbms_stats.get_param('method_opt') from dual;

 

DBMS_STATS.GET_PARAM('METHOD_OPT')

--------------------------------------------------------------------------------

FOR ALL COLUMNS SIZE REPEAT

 

注意:

使用綁定變量會導致直方圖統計信息被忽略。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章