Oracle數據庫裏的統計信息是這樣的一組數據:它存儲在數據字典裏,且從多個維度描述了Oracle數據庫裏對象的詳細信息。CBO會利用這些統計信息來計算目標SQL各類可能的、不一樣的執行路徑的成本,並從中選擇一條成本值最小的執行路徑來做爲目標SQL的執行計劃。sql
Oracle數據庫裏的統計信息能夠分爲以下6種類型:數據庫
表的統計信息服務器
索引的統計信息ide
列的統計信息性能
系通通計信息測試
數據字典統計信息優化
內部對象統計信息spa
表的統計信息用於描述Oracle數據庫裏表的詳細信息,它包含了一些典型的維度,如記錄數、表塊(表裏的數據塊)數量、平均行長度等。3d
索引的統計信息於描述Oracle數據庫裏索引的詳細信息,它包含了一些典型的維度,如索引的層級、葉子塊的數量、聚簇因子等。orm
列的統計信息於描述Oracle數據庫裏列的詳細信息,它包含了一些典型的維度,如列的distinct值的數量、列的NULL值的數量、列的最小值、列的最大值以及直方圖等。
系通通計信息於描述Oracle數據庫所在的數據庫服務器的系統處理能力,它包含了CPU和I/O這兩個維度,藉助於系通通計信息,Oracle能夠更清楚地知道目標數據庫服務器的實際處理能力。
數據字典統計信息用於熱核Oracle數據庫裏數據字典基表(如TAB$、IND$等)、數據字典基表上的索引,以及這些數據字典的列的詳細信息,描述上述數據字典基表的統計信息與描述普通表、索引、列的統計信息沒有本質區別。
內部對象統計信息用於描述Oracle數據庫裏的一些內部表(如X$系列表)的詳細信息,它的維度和普通表的統計信息的維度相似,只不過其表塊的數量爲0,由於X$系統表實際上只是Oracle自定義的內存結構,並不佔用實際的物理存儲空間。
1、收集統計信息
在Oracle數據庫裏,一般有兩種方法能夠用來收集統計信息:一種是使用ANALYZE命令;另外一種是使用DBMS_STATS包。表、索引、列的統計信息和數據字典統計信息用ANALYZE命令或者DBMS_STATS包收集都可,但系通通計信息和系統內部對象統計信息只能使用DBMS_STATS包來收集。
對系統內部表若使用ANALYZE命令來收集統計信息,會報錯ORA-02030
1.1 用ANALYZE命令收集統計信息
從Oracle7開始,ANALYZE命令就能夠用來收集表、索引、列的統計信息,以及系通通計信息。
典型用法以下:
zx@ORCL>create table t2 as select * from dba_objects; Table created. zx@ORCL>create index idx_t2 on t2(object_id); Index created. zx@ORCL>analyze index idx_t2 delete statistics; Index analyzed.
從Oracle 10g開始,建立索引後Oracle會怎麼收集目標索引的統計信息,出現演示的目的,這裏刪除索引IDX_T2的統計信息:
執行sosi腳本,從輸出內容能夠看到表T2、表T2的列和索引IDX_T2均沒有相關的統計信息
zx@ORCL>select count(*) from t2; COUNT(*) ---------- 86852
只對表T2收集統計信息,而且以估算模式,採樣的比例爲15%:
zx@ORCL>analyze table t2 estimate statistics sample 15 percent for table; Table analyzed.
再次執行sosi腳本,能夠看出如今只用表T2有統計信息,表T2的列和索引IDX_T2均沒有相關的統計信息。並且由於採用的是估算模式因此估算結果和實際結果並不必定會徹底匹配,好比表T2的實際數量與估算出的數量不一致。
只對表T2收集統計信息,而且以計算模式:
zx@ORCL>analyze table t2 compute statistics for table; Table analyzed.
再次執行sosi腳本,能夠看出如今只用表T2有統計信息,表T2的列和索引IDX_T2均沒有相關的統計信息。並且由於採用的是計算模式,計算模式會掃描目標對象的全部數據,因此統計結果和實際結果是匹配的。
對錶T2收集完統計信息後,如今對錶T2的列OBJECT_NAME和OBJECT_ID以計算模式收集統計信息:
zx@ORCL>analyze table t2 compute statistics for columns object_name,object_id; Table analyzed.
再次執行sosi腳本,能夠看出,如今列OBJECT_NAME和OBJECT_ID確實已經有統計信息了
注:在崔華老師的《基於Oracle的SQL優化》一書中提到T2原有的統計信息已經被抹掉了,也就是說對同一個對象而言,新執行的ANALYZE命令會抹掉以前ANALYZE的結果。可是在我實際的執行結果是表T2原有的統計信息沒有被抹掉。我用到的環境是10.2.0.4和11.2.0.4,暫時沒有11.2.0.1的環境。
可使用以下的命令同時以計算模式對錶T2和列OBJECT_NAME、OBJECT_ID收集統計信息:
zx@ORCL>analyze table t2 compute statistics for table for columns object_name,object_id; Table analyzed.
再次執行sosi腳本,能夠看到表T2和列OBJECT_NAME、OBJECT_ID上都有統計信息了。
使用以下命令能夠以計算模式收集索引IDX_T2的統計信息
zx@ORCL>analyze index idx_t2 compute statistics; Index analyzed.
再次執行sosi腳本,從輸出能夠看到,如今索引IDX_T2已經有了統計信息,而且以前收集的表T2和列OBJECT_NAME、OBJECT_ID上的統計信息並無被抹掉,這是由於咱們剛纔執行的ANALYZE命令和以前執行的ANALYZE命令針對的不是同一個對象。
使用以下命令能夠刪除表T2、表T2的全部列及表T2的全部索引的統計信息:
zx@ORCL>analyze table t2 delete statistics; Table analyzed.
再次執行sosi腳本,從輸出能夠看到,剛纔收集的表T2、表T2的列OBJECT_NAME、OBJECT_ID以及索引IDX_T2的統計信息已經所有被刪除了。
若是想一次性以計算模式收集表T2、表T2的全部列和表T2上的全部索引的統計信息,執行以下的語句就能夠了:
zx@ORCL>analyze table t2 compute statistics; Table analyzed.
再次執行sosi腳本,從輸出能夠看到,如今表T2、表T2的全部列和索引IDX_T2的統計信息都有了。
1.2 用DBMS_STATS包收集統計信息
從Oracle 8.1.5開始,DBMS_STATS包被普遍用於統計信息的收集,用DMBS_STATS包收集統計信息也是Oracle官方推薦的方式。在收集CBO所須要的統計信息方面,能夠簡單的將DBMS_STATS包理解成是ANALYZE命令的增長版。
DBMS_STATS包裏最經常使用的就是以下4個存儲過程:
GATHER_TABLE_STATS:用於收集目標表、目標表的列和目標表上的索引的統計信息。
GATHER_INDEX_STATS:用於收集指定索引的統計信息。
GATHER_SCHEMA_STATS:用於收集指定schema下全部對象的統計信息。
GATHER_DATABASE_STATS:用於收集全庫全部對象的統計信息。
如今介紹DBMS_STATS包在收集統計信息時的常見用法,仍是針對上面的測試表T2,這裏使用DBMS_STATS包實現了和ANALYZE命令如出一轍的效果。
先刪除表T2上的全部統計信息
analyze table t2 delete statistics;
只對表T2收集統計信息,而且以估算模式,採用的比例一樣爲15%:
zx@ORCL>exec dbms_stats.gather_table_stats(ownname=>'ZX',tabname=>'T2',estimate_percent=>15,method_opt=>'FOR TABLE',cascade=>false); PL/SQL procedure successfully completed.
執行sosi腳本,從輸出內容能夠看出,如今只有表T2有統計信息,表T2的列和索引IDX_T2均沒有相關的統計信息。並且由於採用的估算模式,因此估算結果和實際結果並不必定會徹底匹配。
須要注意的是,這裏Oracle數據庫的版本是11.2.0.4,咱們在調用DMBS_STATS.GATHER_TABLE_STATS時指定參數METHOD_OPT的值爲'FOR TABLE',這表示只收集表T2的統計信息。這種收集表統計信息的方法並不適用於Oracle數據庫全部的版本。例如這種方法就不適用於Oracle10.2.0.4和Oracle10.2.0.5,在這兩個版本里,即便指定了'FOR TABLE',Oracle除了收集表統計信息以外還會對全部的列收集統計信息。
若是公對錶T2收集統計信息,而且是以計算模式收集,用DBMS_STATS包實現的方法就是將估算模式的採樣比例(即參數ESTIMATE_PERCENT)設置爲100%或NULL;
exec dbms_stats.gather_table_stats(ownname=>'ZX',tabname=>'T2',estimate_percent=>100,method_opt=>'FOR TABLE',cascade=>false);
exec dbms_stats.gather_table_stats(ownname=>'ZX',tabname=>'T2',estimate_percent=>NULL,method_opt=>'FOR TABLE',cascade=>false);
zx@ORCL>exec dbms_stats.gather_table_stats(ownname=>'ZX',tabname=>'T2',estimate_percent=>100,method_opt=>'FOR TABLE',cascade=>false); PL/SQL procedure successfully completed.
執行sosi腳本,從輸出內容能夠看出,如今只有表T2的統計信息,表T2的列和索引IDX_T2均沒有相關的統計信息。並且由於採用的是計算模式,計算模式會掃描目標對象的全部數據,因此統計結果和實際結果是匹配的。
對錶T2收集完統計信息後,如今咱們來對錶T2的列OBJECT_NAME、OBJECT_ID以計算模式收集統計信息(不收集直方圖):
zx@ORCL>exec dbms_stats.gather_table_stats(ownname=>'ZX',tabname=>'T2',estimate_percent=>100,method_opt=>'for columns size 1 object_name,object_id',cascade=>false); PL/SQL procedure successfully completed.
執行sosi腳本,從輸出內容能夠看出,如今表T2的列OBJECT_NAME、OBJECT_ID上都有統計信息了,而且Oracle還會同時收集表T2上的統計信息(注意,這和ANALYZE命令有所區別)。
使用以下命令能夠以計算模式收集索引IDX_T2的統計信息
zx@ORCL>exec dbms_stats.gather_index_stats(ownname=>'ZX',indname=>'IDX_T2',estimate_percent=>100); PL/SQL procedure successfully completed.
執行sosi腳本,從輸出內容能夠看出,如今索引IDX_T2已經有了統計信息。
使用以下命令能夠刪除表T2、表T2的全部列及表T2的全部索引的統計信息:
zx@ORCL>exec dbms_stats.delete_table_stats(ownname=>'ZX',tabname=>'T2'); PL/SQL procedure successfully completed.
執行sosi腳本,從輸出內容能夠看出,表T2、表T2的全部列及表T2的全部索引的統計信息已經所有被刪除了。
若是想一次性以計算模式收集表T2、表T2的全部列及表T2的全部索引的統計信息,執行以下語句就能夠了
zx@ORCL>exec dbms_stats.gather_table_stats(ownname=>'ZX',tabname=>'T2',estimate_percent=>100,cascade=>true); PL/SQL procedure successfully completed.
1.3 ANALYZE和DBMS_STATS的區別
從上面的演示中能夠看出ANALYZE命令和DBMS_STATS包均可以用來收集表、索引和列的統計信息,看起來它們在收集統計信息方面的效果是如出一轍的,爲何Oracle會推薦使用DBMS_STATS包來收集統計信息呢?
由於ANALYZE命令和DMBS_STATS包相比,存在以下缺陷:
ANALYZE命令不能正確地收集分區表的統計信息,而DBMS_STATS包卻能夠。ANALYZE命令只會收集最低層次對象的統計信息,而後推導和彙總出高一級的統計信息,好比對於有子分區的分區表而言,它只會先收集子分區統計信息,而後再彙總,推導出分區或表級的統計信息。有的統計信息是能夠從當前對象的下一級對象進行彙總後獲得的,好比表的總行數,能夠由各分區的行數相加獲得。但有的統計信息則不能從下一級對象獲得,好比列上的distinct值數量NUM_DISTINCT以及DESNSITY等。
ANALYZE命令不能並行收集統計信息,而DBMS_STATS包卻能夠。並行收集統計信息對數據量很大的表表而言,是很是有用的特性。對於數據量很大的表,若是不能並行收集統計信息,則意味着若是想精確地收集目標對象的統計信息,那麼耗費的時間可能會很是長,這有多是不能接受的。在Oracle數據庫裏,DBMS_STATS包收集統計信息能夠並行執行,這在必定程度上緩解了對大表的統計信息收集過長所帶來的一系列問題。
DBMS_STATS包的並行收集是經過手工指定輸入參數DEGREE來實現的,好比對錶T1進行收集統計信息,同時指定並行度爲4:
exec dbms_stats.gahter_table_stats(ownname=>'SCOTT',tabname=>'T1',cascade=>true,estimate_percent=>100,degree=>4);
固然,DBMS_STATS包也不是完美的,它與ANALYZE命令相比,其缺陷在於DBMS_STATS包只能收集與CBO相關的統計信息,而與CBO無關的一些額外信息,好比行遷移/行連接的數量(CHAIN_CNT)、校驗表和索引的結構信息等,DBMS_STATS包就無能爲力了。而ANALYZE命令能夠用來分析和收集上述額外的信息,好比analyze table xxx list chained rows intoyyy 能夠用來分析和收集行遷移/行連接的數量,analyzeindex xxx validate structure能夠用來分析索引的結構。
2、查看統計信息
前面介紹瞭如何收集統計信息,那如何查看這些統計信息呢?Oracle數據庫的統計信息會存儲在數據字典裏,咱們只須要去查詢相關的數據字典就行了。若是有充裕的時間,現寫SQL去查詢數據字典裏的統計信息也沒有什麼,但當咱們真正碰到有性能問題的SQL時,一般會但願能在第一時間就收集到與目標SQL相關的各類統計信息,以便於在第一時間定位問題所在,這時候寫SQL去查詢數據字典就已經來不及了,因此咱們須要事先準備好通用的查詢統計信息的腳本,出問題的時候只須要運行一下腳本,就能在第一時間獲取目標對象的全部統計信息了。
sosi腳本(Show Optimizer Statistics Information)就是這樣一種腳本,國內的Oracle數據庫專家也一直在用這個腳本,它源於MOS上的文章:SCRIPT - Select to show OptimizerStatistics for CBO (文檔 ID 31412.1),用法很簡單,只須要運行一下sosi腳本,並指定要查看統計信息的表名就能夠了。它支持分區表,顯示分爲三部分,分別是表級別的統計信息,分區級別的統計信息和子分區級別的統計信息。前面作實驗用到的也是這個腳本。
附件是sosi腳本能夠下載使用。
參考《基於Oracle的SQL優化》