SQL Server 統計信息更新時採樣百分比對數據預估準確性的影響

  

爲何要寫統計信息數據庫

  最近看到園子裏有人寫統計信息,樓主也來湊熱鬧。
  話說常常作數據庫的,尤爲是作開發的或者優化的,統計信息形成的性能問題應該說是司空見慣。
  固然解決辦法也並不是一成不變,「一招鮮吃遍天」的作法已經行不通了(題外話:整個時代不都是這樣子嗎)
  固然,仍是那句話,既然寫了就不能太俗套,寫點不同的,本文經過分析一個相似實際案例來解讀統計信息的更新的相關問題。
  對於實際問題,不但要解決問題,更重要的是要從理論上深刻分析,才能更好地駕馭數據庫。
 服務器

統計信息基礎網絡

首先說一個老掉牙的話題,統計信息的更新閾值:
1,表格從沒有數據變成有大於等於1條數據。
2,對於數據量小於500行的表格,當統計信息的第一個字段數據累計變化量大於500之後。
3,對於數據量大於500行的表格,當統計信息的第一個字段數據累計變化量大於500 + (20%×表格數據總量)之後。性能

作個查詢,觸發統計信息更新,rowmodct歸0(繼續累積直到下一個觸發的閾值,觸發更新以後再次歸0)測試

 

關於統計信息「過時」的問題優化

下面開始正文,網絡上不少關於統計信息的文章,提到統計信息,不少都是統計信息過時的問題,而後跟新以後怎麼怎麼樣
尤爲在觸發統計信息自動更新閾值的第三個區間:也就是說數據累計變化超過20%以後才能自動觸發統計信息的更新
這一點對於大表來講一般影響是比較大的,好比1000W的表,變化超過20%也+500也就是200W+500行以後才觸發統計信息更新,
這個閾值區間的自動觸發閾值,絕大多數狀況是不能接受的,因而對於統計信息的診斷就變成了是否「過時」spa

 

 

判斷統計信息是否過時,而後經過更新統計信息來促使執行計劃更加準確地預估行數,這一點本無可厚非
可是,問題也就出在這裏了:那麼怎麼更新統計信息?一成不變的作法是否可行,這纔是問題的重點。
固然確定有人說,我就是按照默認方式更新的,更新完以後SQL也變得更加優化了什麼的
經過update statistics TableName StatisticName更新某一個索引的統計信息,
或者update statistics TableName更新全表的統計信息
這種狀況下每每是小表上能夠這麼作,固然對於大表或者小表沒有一個標準值,一切要結合事實來講明問題
 code

下面開始本文的主題:blog

抽象並簡化出業務中的一個實際案例,建立這麼一張表,相似於訂單和訂單明細表(主子表),
這裏你能夠想象成是一個訂單表的子表,Id字段是惟一的,有一個ParentID字段,是非惟一的,
ParentID相似於主表的Id,測試數據按照一個主表Id對應50條子代表細的規律插入數據索引

CREATE TABLE [dbo].[TestStaitisticsSample](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [ParentId] [int] NULL,
    [OtherColumn] [varchar](50) NULL
) 


declare @i int=0
while(@i<100000000)
begin

    insert into [TestStaitisticsSample](ParentId,OtherColumn)values(@i,NEWID())
    /*
    中間插入50條,也即一個主表Id對應50條子代表細
    */
    insert into [TestStaitisticsSample](ParentId,OtherColumn)values(@i,NEWID())

    set @i=@i+1
end
go

create nonclustered index [idx_ParentId] ON [dbo].[TestStaitisticsSample]
(
    [ParentId] 
)
go

 

原本打算插入1億條的,中間我讓他執行我睡午覺去了,醒來以後發現SSMS掛掉了,掛掉了算了,數據也接近1億了,能說明問題就夠了
如今數據分佈的很是明確,就是一個ParentId有50條數據,這一點首先要澄清。

 

測試數據寫入,以及所建立完成以後來更新 idx_ParentId 索引上的統計信息,就按照默認的方式來更新,而後來觀察統計信息

 

默認方式更新統計信息(未指定採樣密度)

表裏如今是8000W多一點記錄,默認更新統計信息時取樣行數是462239行,那麼這個統計信息靠譜嗎?

上面說了,造數據的時候,我一個ParentId對應的是50行記錄,這一點很是明確,他這裏統計出來的多少?

1,對於取樣的RANG_HI_Key值,好比51632,預估了862.212行

2,對於AVG_RANG_ROW,好比45189到51632之間的每一個Id的數據對應的數據行,預估是6682.490行

以前造數據的時候每一個Id都是50行,這裏的預估靠譜嗎,這個偏差是沒法接受的,

不少時候,對於大表,採用默認(未指定採樣密度)的狀況下,默認的採樣密度並不足以準確地描述數據分佈狀況

 

指定一個採樣密度的方式更新統計信息(20%採樣)

 

這一次用20%的採樣密度,能夠看到取樣的行數是15898626行

1,對於取樣的RANG_HI_Key值,好比216305,他給我預估了24.9295行

2,對於AVG_RANG_ROW,好比186302到216305之間的每一個Id的行數,預估是197.4439行

觀察好比上面默認的取樣密度,這一次不論是RANG_HI_Key仍是AVG_RANG_ROW得預估,都有不一個很是高的降低,開始趨於接近於真實的數據分佈(每一個Id有50行數據)

總體上看,可是這個偏差仍是比較大的,若是繼續提升採樣密度,看看有什麼變化?

 

指定一個採樣密度的方式更新統計信息(70%採樣) 

 

這一次用70%的採樣密度,能夠看到取樣行數是55962290行

1,對於取樣的RANG_HI_Key值,好比1978668,預估了71.15906行

2,對於AVG_RANG_ROW,好比1124024到1978668之間的每一個Id,預估爲61.89334行

能夠說,對於絕大多數值得預估(AVG_RANG_ROW),都愈發接近於真實值

   

 

指定一個採樣密度的方式更新統計信息(100%採樣)

 

能夠看到,取樣行數等於總行數,也就是所謂的所有(100%)取樣

看一下預估結果:

好比Id=3981622,預估是50行,3981622與4131988之間的Id的行數,預估爲49.99874行,基本上等於真實數據分佈

這個就不作過多解釋了,基本上跟真實值是同樣的,只是AVG_RANG_ROW有一點很是很是小的偏差。

 

 取樣密度高低與統計信息準確性的關係

 

  至於爲何默認取樣密度和較低取樣密度狀況下,偏差很大的狀況我簡單解釋一下,也很是容易理解,
  由於「子表」中存儲主表ID的ParentId值容許重複,在存在重複值的狀況下,若是採樣密度不夠,極有可能形成「以偏概全」的狀況
  好比對10W行數據取樣1W行,本來10W行數劇中有2000個不重複的ParentId值,
  若是是10%的取樣,在1W行取樣數據中,由於密度不夠大,只找到了20個不重複的ParentId值,
  那麼就會認爲每一行ParentId對應500行數據,這根實際的分佈的每一個ParentId有一個很是大的偏差範圍
  若是提升採樣密度,那麼這個偏差就會愈來愈小。
  

更新統計信息的時候,高比例的取樣是否可取(可行) 

  所以在觀察統計信息是否過時,決定更新統計信息的時候,必定要注意取樣的密度,
  就是說表中有多少行數據,統計信息更新的時候取了多少採樣行,密度有多高。
  固然,確定有人質疑,那你說採樣密度越高,也就是取樣行數越高越準確,那麼我就100%取樣。
  這樣行不行?
  還要分狀況看,對於幾百萬或者十幾萬的小表來講,固然沒有問題,這也是爲何數據庫越小,表數據越少越容易掩蓋問題的緣由。
  對於大表,上億的,甚至是十幾億的,你按照100%採樣試一試?
  

  舉個實際例子:

  我這裏對一個稍微大一點的表作個全表統計信息的更新,測試環境,服務器沒負載,存儲是比普通的機械硬盤要強不少的SAN存儲
  採用full scan,也就是100%採樣的更新操做,看一下,僅僅這同樣表的update statistic操做就花費了51分鐘
  試想一下,對一個數百GB甚至數TB的庫來講,你敢這麼搞一下。

  

 

  扯一句,這個中秋節過的,折騰了大半天,話說作測試過程當中電腦有開始有點卡,

  作完測試以後停掉SQLServer服務,瞬間內存釋放了7個G,可見這些個操做仍是比較耗內存的

  

  

 

總結:

  本文經過對於某些場景下,在對較大的表的索引統計信息更新時,採樣密度的分析,闡述了不一樣採樣密度下,對統計信息預估的準確性的影響。

  固然對於小表,一些都好說。

  隨着單表數據量的增長,統計信息的更新策略也要作相應的調整,

  不光要看統計信息是否「過時」,更重要的是注意統計信息更新時究竟取樣了全表的多少行數據作統計。

  對於大表,採用FULL SCAN或者100%採樣每每是不可行的,這時候就須要作出權衡,作到既能準確地預估,又可以以合理的代價執行。

相關文章
相關標籤/搜索