一、 簡介
1. 引入原因
在普通的統計信息中,CBO會默認目標列數據在其最小與最大值間是均勻分佈的,並以此爲依據預估條件選擇率及結果集cardinality,進而選擇執行計劃。但在實際中,顯然有些數據不是平均分佈的,會出現所謂的“數據傾斜”,此時產生的執行計劃很可能就不是最佳的。
2. 直方圖是什麼
直方圖是一種特殊的列統計信息,它詳細描述了列的數據分佈情況。直方圖收集後,CBO不會再認爲列上是均勻分佈的,可以根據實際情況預估條件選擇率及結果集cardinality,選擇正確的執行計劃。
直方圖使用Bucket(桶)的方式描述目標列的數據分佈,每個Bucket就是一組,存放一個或多個列目標中的數據。oracle採用兩個維度描述一個Bucket —— endpoint number和endpoint value(後文介紹)。兩個維度信息,即直方圖數據存儲在HISTGRM$基表中,可以通過數據字典dba_tab_histgrams,dba_part_histgrams,dba_subpart_histgrams來查看錶、分區及子分區的直方圖信息。
二、 直方圖的類型
12c之前,oracle有Frequency和Balanced兩類直方圖,12c又引入了Top-Frequency和Hybrid兩類直方圖。下面分別來看:
1. Frequency直方圖
如何能詳細描述列的數據分佈情況?最簡單的方法當然是統計每個列distinct值有多少並存儲,實際上這就是oracle中的Frequency類型直方圖。
對於這類直方圖,列中有多少distinct值,數據字典中就會存儲多少條記錄。endpoint value記錄這些distinct值,endpoint number則記錄到此distinct值爲止共有多少條記錄。此時endpoint number是一個累加值,可以用一條記錄的endpoint number減去前一條記錄的endpoint number得到這條記錄對應的endpoint value值的記錄數。
這種方法簡單易想,但是對於大表而言,它是不切實際的。12c之前,oracle要求Frequency類型直方圖對應的Bucket數不超過254,即目標列distinct值不能超過254(12c去除了該限制)。
爲了處理distinct值特別多的大表,oracle引入了Height Balanced類型直方圖。
2. Height Balanced直方圖
在12c之前,如果目標列distinct值超過了254,oracle會轉爲對目標列收集Height Balanced類型直方圖。
對於這類直方圖,oracle首先根據目標列對目標表的所有記錄從小到大排序,然後用總記錄數除以需要使用的Bucket數,來決定每個Bucket中需要描述的已排好序的記錄數。
此時endpoint number記錄的是Bucket號,Bucket號從0開始一直到N。0號Bucket對應的endpoint value記錄目標列最小值,其他Bucket對應的endpoint value則記錄到此Bucket爲止目標列的最大值。
爲節省空間,對於相鄰的僅endpoint number不同而endpoint value相同的記錄,oracle會在數據字典中合併存儲。假如endpoint number=2和3的Bucket對應endpoint value都爲P,數據字典中只會記錄endpoint number=3,endpoint value=P。因此,Height Balanced類型直方圖數據字典中的endpoint number可能是不連續的。
這種合併後記錄在數據字典的endpoint value,oracle稱爲popular value。顯然popular value記錄的endpoint number值與上一條記錄的endpoint number值相差越大,popular value在目標列中所佔比例越大,對應的cardinality也就越大,這應該也是將其稱爲popular value的原因。
三、 直方圖收集方法
1. 語法格式
對於dbms_stats包,是通過指定method_opt參數實現的,該參數可接受值如下:
for all [indexed | hidden] columns [size_clause]
for columns [size_clause] column | attribute [size_clause] [,column | attribute [size_clause]...]
其中size_clause必須符合以下格式
size [整數值 | repeat | auto | skewonly]
- 整數值:直方圖的Bucket數,範圍爲[1,254],爲1表示刪除列上直方圖信息
- repeat:只對已有直方圖的列收集直方圖信息
- auto:由oracle決定是否對列收集直方圖,以及使用哪類直方圖。oracle默認只對用過的列(where條件中出現過的列)自動收集直方圖統計信息,oracle會在sys.col_usage$基表中記錄各列的使用情況。在自動收集直方圖統計信息時先查該表,如果列未被使用過,則不會收集。
- skewonly:只對傾斜列收集直方圖
2. 例子
對錶所有有索引的列以auto方式收集直方圖
exec dbms_stats.gather_table_stats(ownname => 'USER01',tabname => 'TAB01',method_opt=> 'for
all indexed columns size auto');
對錶的empno和deptno列以auto方式收集直方圖
exec dbms_stats.gather_table_stats(ownname => 'USER01',tabname => 'TAB01',method_opt=> 'for
columns size auto empno deptno');
對錶的empno和deptno列收集直方圖,並指定Bucket數爲10
exec dbms_stats.gather_table_stats(ownname => 'USER01',tabname => 'TAB01',method_opt=> 'for
columns size 10 empno deptno');
對錶的empno和deptno列收集直方圖,指定empno列Bucket數爲10,deptno列Bucket數爲5
exec dbms_stats.gather_table_stats(ownname => 'USER01',tabname => 'TAB01',method_opt=> 'for
columns empno size 10 deptno size 5');
刪除empno列的直方圖
exec dbms_stats.gather_table_stats(ownname => 'USER01',tabname => 'TAB01',method_opt=> 'for
columns empno size 1');
刪除表上所有列的直方圖
exec dbms_stats.gather_table_stats(ownname => 'USER01',tabname => 'TAB01',method_opt=> 'for
all columns size 1');
四、 注意事項
- 直方圖是爲解決數據傾斜問題而引入,它對CBO選擇度、基數估計及執行計劃選擇有直接影響。
- 如果列數據分佈均勻(例如有唯一索引的列),完全沒必要對該列收集直方圖。
- 對於從沒在where條件中出現或者出現非常少的列,無須對該列收集直方圖。
- 在配置10g中引進的自動統計信息收集作業時,需要特別注意對直方圖的收集策略。
- 直方圖可能影響shared cursor能否被共享。若cursor_sharing值設爲similar,而查詢列有直方圖,oracle會對列的每一個distinct值產生一個child cursor,這可能導致嚴重的硬解析,設置引發高版本問題。對於cursor_sharing參數,建議值還是使用默認的EXACT。
- 對於字符型的字段,直方圖有重大缺陷 —— oracle只會將字段的頭32Byte取出來(實際上只取頭15Byte),將其轉換爲一個浮點數,並將這個浮點數作爲直方圖信息存在數據字典裏。對於超過32Byte而前32Byte又相同的字符列值,oracle會認爲它們是完全相同的,即使它們並不相同,這很可能導致執行計劃選擇錯誤。案例參考:https://blog.csdn.net/Hehuyi_In/article/details/106162142