Oracle統計信息(二)—— 直方圖 Histogram

一、 簡介

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