SQL Server優化器基於開銷(Cost)評估執行計划,選擇開銷最小的作為“最優化”的執行計划。計算開銷的根據是索引及其統計信息,因此,索引和統計數據是非常重要的。查詢優化器(Query Optimizer)使用統計信息對查詢的開銷進行評估(Estimate),選擇開銷最小的查詢計划,作為最終的、“最優的”的執行計划。SQL Server自動為索引列或查詢的數據列創建統計信息,統計信息包括三部分:頭部(Header),密度向量(Density Vector) 和 分布直方圖(Distribution Histogram)。
統計信息是數據分布的反饋,SQL Server根據數據更新的數量和特定的規則自動更新統計信息,一般情況下,表的數據量越大,SQL Server更新統計信息需要的數據更新量越大,隨着數據的更新,有些表的數據不會及時更新,以至於統計信息過時,不能真實反映數據的分布情況,用戶可以通過命令手動更新統計信息,但是更新統計信息需要掃描數據表,這可能是一個非常耗時的IO密集型操作,用戶需要權衡性能的提升和資源的消耗。
一,查看統計信息
統計信息不是實時更新的,如果統計信息過期,查詢優化器(Query optimizer)可能不能生成高質量的查詢計划,必須有必要的調度程序,自動更新統計數據。數據庫管理員(DBA)可以使用DBCC SHOW_STATISTICS 能夠查看表或索引視圖(Indexed view)的統計信息,以及最后一次更新統計信息的日期,如果統計信息過期,可以使用UPDATE STATISTICS命令手動更新統計信息,以使查詢優化器依據正確的統計信息生成高效的查詢計划。但是,並不是統計信息更新的越頻繁越好,更新統計信息是IO密集型的操作,還會導致現有的查詢計划的重新編譯,建議不要太頻繁地更新統計信息,在改進查詢計划和查詢計划的重新編譯之間權衡開銷,找到一個平衡點。
DBCC SHOW_STATISTICS ( table_or_indexed_view_name , target ) WITH STAT_HEADER | DENSITY_VECTOR | HISTOGRAM | STATS_STREAM
target 參數是:索引的名稱,統計對象的名稱,或者列名。如果target是索引名稱,或統計對象的名稱,那么該命令返回關於target的統計信息。如果target是數據列,那么該命令會自動在該列上創建統計,返回關於該列的統計信息。
1,統計對象
在SSMS中打開Table的屬性,展開“Statistics”,這就是跟該表有關的統計對象:
查看統計對象 [cix_dt_test_idcode]的統計信息:
dbcc show_statistics('dbo.dt_test',[cix_dt_test_idcode])
命令返回的統計信息包含三部分,分別是 頭部信息,密度向量和分布直方圖:
2,頭部數據
第一個表是Header表,Name字段是統計對象的名稱,
頭部數據返回的字段說明:
- Updated字段:是統計信息最后一次更新的時間,通過該字段,可以判斷統計信息是否過期。
- Rows字段:是統計信息更新時,表或索引視圖(Indexed View)中的數據行數量,注意,該字段不會實時反應數據表的總行數。
- Rows Sampled字段:用於計算統計信息時的樣本數據的總行數,如果 Rows Sampled < Rows,顯示的直方圖和密度結果是根據抽樣數據進行估計的。
- Steps字段:是分布直方圖中的梯級數。每個梯級都跨越一個列值范圍,直方圖梯級是根據統計信息中的第一個鍵列定義的,最大梯級數為 200。
3,密度向量
第二個表是密度向量(Density Vector),用於對鍵列(Key Column)執行密度分析,密度的計算公式非常簡單:1和唯一值的比例,即 density= 1/(Distinct Value的個數)
密度向量的總行數跟索引鍵的數量有關,每一行都是索引鍵的前綴組合,而唯一值是前綴組合列的無重復值。例如,如果統計對象包含索引鍵列(A,B,C),密度向量為3行,第一行是(A)的密度,唯一值是列A的無重復值;第二行是(A,B)的密度,唯一值是列A和B的無重復值;第三行是(A,B,C)的密度,唯一值是列A,B和C的無重復值。
示例中索引列是(id,code),索引列的密度是計算(id),(id,code)的密度,密度向量表中,All Density字段是統計對象計算的密度。
第一行的密度是0.001,由於列id的唯一值數量是1000,因此,1/1000=0.001
select count( distinct id) from dbo.dt_test
試想,如果列ID的重復值比較多,(ID,Code)組合的重復值比較少,那么(ID)的All Density的值大於(ID,Code)的密度,通過Density Vector可以看出數據重復率的趨勢。
密度向量始終是從索引列的第一列開始統計,如果篩選子句(where,on)中沒有包含索引的第一列,那么查詢優化器不會使用索引,因此,索引列的順序非常重要。
4,分布直方圖
第三個表是分布直方圖(Distribution Histogram),使用參數target的第一個索引鍵列(key column)來統計數據的分布,統計的數據是第一個索引列中非重復值的出現頻率。如果統計的對象是復合索引,那么只統計索引列第一列的值的分布情況,忽略其他索引列。
本例的索引列是(ID,Code),那么統計的是ID 值的分布直方圖:
分布直方圖返回的數據列說明:
- RANGE_HI_KEY:直方圖梯級的上限列值。列值也稱為鍵值。
- RANGE_ROWS:其列值位於直方圖梯級內(不包括上限)的行的估算數目。
- EQ_ROWS:其列值等於直方圖梯級的上限的行的估算數目。
- DISTINCT_RANGE_ROWS:非重復列值位於直方圖梯級內(不包括上限)的行的估算數目。
- AVG_RANGE_ROWS:重復列值位於直方圖梯級內(不包括上限)的平均行數(如果 DISTINCT_RANGE_ROWS > 0,則為 RANGE_ROWS / DISTINCT_RANGE_ROWS)。
在分布直方圖中,每一行都是一個范圍(Range),
- 字段RANGE_HI_KEY是范圍的最大值,范圍的最小值大於上一條記錄的最大值(RANGE_HI_KEY)。在直方圖中,第一條記錄是數據表的最小值,只有一條記錄。
- 字段Range_Rows表示在當前范圍中,不包括最大值(RANGE_HI_KEY)的總行數。
- EQ_Rows字段是當前范圍中,等於最大值(RANGE_HI_KEY)的總行數。
- DISTINCT_RANGE_ROWS字段表示在當前范圍中,除去RANGE_HI_KEY之外的所有數據行,其唯一值的數量。
- AVG_RANGE_ROWS字段是一個比例,當DISTINCT_RANGE_ROWS=0時,AVG_RANGE_ROWS=1;當DISTINCT_RANGE_ROWS>0時,AVG_RANGE_ROWS=Range_Rows/DISTINCT_RANGE_ROWS。
例如,當前范圍中有(1),(2),(3),(1),(2)五個數據行,最大值是(3),且只有一個,因此,RANGE_HI_KEY=(3),EQ_Rows=1,除去最大值,共有4行數據,唯一值是2個,因此Range_Rows=4,DISTINCT_RANGE_ROWS=2,由於唯一值的數量不是0,因此,AVG_RANGE_ROWS=4/2。
二,驗證分布直方圖數據
下圖是統計對象 cix_dt_test_idcode 的分布直方圖:
第一條記錄是數據表的最小值,也是該范圍的最大值,數據只有一條:
直方圖第一行:RANGE_HI_KEY=0, EQ_Rows=1 ,Range_Rows=0,DISTINCT_RANGE_ROWS=0,AVG_RANGE_ROWS=1
第二條記錄,范圍的最大值是7,范圍的最小值是1,是大於第一條記錄(0)的最小值;從1到7共有7條記錄,除去最大值7之外,共有6行數據,所以,Range_Rows=6;這6行數據都不重復,因此DISTINCT_RANGE_ROWS=6;由於DISTINCT_RANGE_ROWS>0,因此 AVG_RANGE_ROWS=Range_Rows/DISTINCT_RANGE_ROWS=6/6=1。
直方圖第二行:RANGE_HI_KEY=7,EQ_Rows=1,Range_Rows=6,DISTINCT_RANGE_ROWS=6,AVG_RANGE_ROWS=1
三,創建統計信息
對於大多數查詢而言,查詢優化器自動為索引列創建統計信息,對於非索引列,有時查詢優化器為了產生高質量的查詢計划自動為表創建必需的統計信息,系統自動創建的統計信息是單列的,統計對象的命名以_WA開頭。用戶也可以使用CREATE STATISTICS命令創建統計信息,一般是為多列的組合創建統計對象,以提高查詢性能。
1,手動創建統計信息
通過CREATE STATISTICS 命令可以創建多列的統計對象:
CREATE STATISTICS statistics_name ON { table_or_indexed_view_name } ( column [ ,...n ] ) [ WHERE <filter_predicate> ] [ WITH [ FULLSCAN | SAMPLE number { PERCENT | ROWS } ] , [ NORECOMPUTE ] ]
參數注釋:
- FULLSCAN:全表掃描以創建統計信息
- SAMPLE n {PERCENT | ROWS}:取樣掃描,以創建統計信息, 如果設置 n=0,那么SQL Server創建一個空的統計信息,不包含任何統計數據。
- NORECOMPUTE:該選項用於禁用統計信息的自動更新,不建議使用
- WHERE 子句用於過濾表的子集,在子集上創建統計信息(過濾統計信息)
過濾統計信息對於特定的數據子集非常有用。
2,自動創建統計信息
數據庫選項:AUTO_CREATE_STATISTICS 默認為ON,自動創建統計信息,僅應用於自動創建關系表的單列統計信息。
查詢優化器根據查詢謂詞的使用情況,在表格上單獨給某一列創建統計信息(這些單列暫時未創建直方圖),協助查詢計划的基數估計。通過該選項創建的統計信息,名稱以 _WA 開頭,查詢語句如下所示:
SELECT OBJECT_NAME(s.object_id) AS object_name, COL_NAME(sc.object_id, sc.column_id) AS column_name, s.name AS statistics_name FROM sys.stats AS s INNER JOIN sys.stats_columns AS sc ON s.stats_id = sc.stats_id AND s.object_id = sc.object_id WHERE s.name like '_WA%' ORDER BY s.name;
四,更新統計信息
SQL Server 查詢優化器使用這些統計信息來計算開銷,選擇最優的執行計划。查詢優化器選擇索引的一個標准是:索引列的選擇性高,也就是說,該列的重復值少,重復率可以從直方圖的Avg_Range_Rows和密度向量的All Desity字段中獲取。
1,查看統計信息最后一次更新的時間
系統根據特定的規則更新統計信息,但是,隨着數據的少量更新,數據表的統計信息不會實時更新,STATS_DATE 函數用於返回表或索引視圖上統計信息的最后一次更新的日期:
STATS_DATE ( object_id , stats_id )
參數stats_id是統計對象的ID,可以通過sys.stats來查看統計對象及其ID,
2,統計對象和基礎數據列之間的關系
系統視圖:sys.stats_columns顯式統計對象和基礎表(或索引視圖)的數據列之間的關系:
select object_name(s.object_id) object_name, s.name as statistics_name, sc.stats_column_id, col_name(sc.object_id, sc.column_id) as column_name, stats_date(s.object_id,s.stats_id) as stats_last_updated_date from sys.stats as s inner join sys.stats_columns as sc on s.stats_id = sc.stats_id and s.object_id = sc.object_id where s.object_id=object_id('table_name','U') order by s.name;
3,更新統計信息
用戶有時需要手動更新統計信息,這可以通過UPDATE STATISTICS命令來實現:
update statistics dbo.dt_test [cix_dt_test_idcode]
在計算統計信息時,有多種掃描數據表的方式:
- FULLSCAN:掃描所有的數據行,開銷最大,計算的統計信息最精確;
- SAMPLE number { PERCENT | ROWS }:取樣本,只掃描樣本數據;
- RESAMPLE:使用最新的樣本數據計算統計信息,可能會導致全表掃描;
SQL Server查詢優化器根據統計來評估開銷,生成最優的執行計划。 選擇適當的掃面方式,能夠及時更新統計數據,使用最小的工作負載,實現性能的最大提升。
UPDATE STATISTICS schema_name . table_name { statistics_name | index_name } WITH FULLSCAN | SAMPLE number PERCENT| RESAMPLE
4,自動更新統計信息
數據庫選項:AUTO_UPDATE_STATISTICS (自動更新統計信息)選項,默認值是ON,查詢優化器自動確定統計信息何時可能過期,然后在查詢使用這些統計信息時更新它們。通常情況下,從上次自動更新至今,如果期間積累了較大數量的數據變更,包括插入、刪除及修改,或表結構變更等,均會造成統計信息過期。統計信息將在插入、更新、刪除或合並操作更改表或索引視圖中的數據分布后過期。 查詢優化器通過計算自最后統計信息更新后數據修改的次數並且將這一修改次數與某一閾值進行比較,確定統計信息何時自動更新。
查詢優化器在編譯查詢和執行緩存查詢計划前,檢查是否存在過期的統計信息。 在編譯某一查詢前,查詢優化器使用查詢謂詞中的列、表和索引視圖確定哪些統計信息可能過期。 在執行緩存查詢計划前, 數據庫引擎 確認該查詢計划引用最新的統計信息。
數據庫選項:AUTO_UPDATE_STATISTICS_ASYNC(異步自動更新統計信息) ,用於指定查詢優化器是同步更新統計信息還是異步更新統計信息。默認為OFF,也就是說,在默認情況下,使用同步方式自動更新統計信息,查詢計划始終使用最新的統計信息進行編譯和執行,如果遇到統計信息過期,則會在查詢編譯前等待更新的統計信息。如果使用異步方式自動更新統計信息,查詢計划使用現有的統計信息進行編譯和執行,即使遇到統計信息過期,也會直接使用現有統計信息編譯然后執行,即使可能由於統計信息過期造成編譯不佳,執行計划非最優,但仍按照編譯結果運行。