--====================sql
-- Oracle histogram 數據庫
--====================oracle
直方圖意義:在oracle數據庫中,CBO會默認認爲目標列的數據量在其最小值和最大值之間是均勻分佈的(最小值最大值不許確會致使謂詞越界),而且會按照這個均勻分佈原則來計算對目標列事假的where查詢條件後的可選這率及結果集的cardinality,進而據此來計算成本值並選擇執行計劃。可是,目標列的數據是均勻分佈的按照這個原則選擇執行計劃是正確的;若是目標數據列分佈不均勻,甚至是嚴重傾斜,分佈極度不均勻,那麼這個按照這個原則選擇執行計劃就不合適,甚至是錯誤的,爲此咱們須要對那些數據分佈不均勻的列進行直方圖收集。ide
直方圖是一種列的特殊的統計信息,主要用來描述列上的數據分佈狀況。當數據分佈嚴重傾斜時,直方圖能夠用小的提高cardinality評估的準確度。構造直方圖最主要的緣由就是幫助優化器在表中數據驗證傾斜是作出最好的選擇。例如,表中的某個列上數據佔據了整個表的80%(數據分佈傾斜),相關的索引就可能沒法幫助減小知足查詢所需的I/O數量。建立直方圖可讓基於成本的優化器知道什麼時候使用索引才最合適。oop
直方圖實際存儲在數據字典sys.histgrm$中,能夠經過數據字典dba_tab_historgrams,dba_part_histograms和dba_subpart_histograms來分別查看錶,分區表的分區和分區表的子分區的直方圖信息。優化
直方圖使用場景:code
(1) 當where字句引用了列值分佈存在傾斜時。當這種誤差至關明顯時,以致於where字句中的值將會使優化器選擇不一樣的執行計劃。這時應該使用直方圖來幫助優化器來修正執行路徑。(如查詢不使用該列,則在該列上建立直方圖也沒有意義)orm
(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
注意:
使用綁定變量會致使直方圖統計信息被忽略。