在理解統計信息(3/6):誰創建和管理統計信息?在性能調優中,統計信息的作用里我們討論了統計信息的自動創建和自動更新。我們真的需要人為維護統計信息來保持性能最優?答案是肯定的,這取決與你的工作量。SQL Server只在達到閥限值時進行統計信息的自動更新。當大量的Insert/Update/Delete操作發生時,內建的自動更新統計信息不能持續保證性能的最優。
經過一系列的Insert/Update/Delete后,統計信息可能不會是最新。如果SQL Server查詢優化器在表里需要指定列的統計信息,自上次統計信息創建或更新后經歷了實質的更新活動,SQL Server會通過采樣列值自動更新統計信息(通過自動更新統計信息)。統計信息的自動更新由查詢優化器或編譯好的計劃執行來觸發,它只涉及到查詢里引用到的各個列。如果自動異步更新統計信息是停用的話,統計信息會在查詢編譯前更新,啟用的話是在查詢編譯后更新。當統計信息是異步更新時,受益于觸發更新的查詢使用老的統計信息。對一些工作量來說,這可以提供更可預估的響應時間,尤其是那些大表上的短時間運行的查詢。
當一個查詢首次編譯完成,如果優化器需要指定對象的統計信息,這個統計信息存在的話,若已過期則自動更新統計信息。如果一個查詢被執行且它的計劃在緩存里,計劃依賴的統計信息會被檢查是否過期,如果過期,計劃會在緩沖中移除,在查詢的重編譯時,統計信息會被更新。如果計劃依賴的任何統計信息被更新的話,計劃都會從緩存中移除。
SQL Server 2008基于列修改的計數器(colmodctrs)來決定是否更新統計信息:
在下列情況下,統計信息對象被認為過期:
如果在常規表上定義的統計信息,被認為過期的話,那么:
上述描述來自微軟的MSDN,具體參見Statistics Used by the Query Optimizer in Microsoft SQL Server 2008。
前2個條件還是相當好的,但第3個條件在處理大表時,有些時候閥值會很高,但對統計信息更新還是無效。例如有個表有100000條記錄,只有在200500條件記錄被修改后(update/insert),對于觸發自動更新還是無效的閥值。
我們來看個例子。
1 USE StatisticsDB2 GO3 4 DROP TABLE SalesOrderDetail5 SELECT * INTO SalesOrderDetail FROM AdventureWorks2008r2.sales.SalesOrderDetail6 CREATE INDEX ix_PRoductID ON SalesOrderDetail(ProductID)7 SET STATISTICS IO ON8 SELECT * FROM SalesOrderDetail WHERE ProductID=725
我們創建了SalesOrderDetail表的副本,并在上面創建非聚集索引,我們看下最后SELECT查詢的執行計劃,點擊工具欄的顯示包含實際的執行計劃。
優化器選擇了索引查找和書簽查找操作作為優化的計劃,完成這個操作需要377個邏輯讀。
salesOrderDetail表有121317條記錄,上述第3個條件如果要使統計信息無效的話,121317的20% =24263+500=24763條記錄需要被修改,我們用下列語句只更新5000條記錄,再次看看查詢的執行計劃,點擊工具欄的顯示包含實際的執行計劃。
1 SET ROWCOUNT 50002 UPDATE SalesOrderDetail SET ProductID=725 WHERE ProductID<>7253 SET ROWCOUNT 04 SET STATISTICS IO ON5 SELECT * FROM SalesOrderDetail WHERE ProductID=725
執行計劃里估計行數是374,這是基于上次更新操作收集的統計信息。優化器基于統計信息,選擇了索引查找和書簽查找作為最優計劃。SELECT操作進行5390邏輯讀來完成這個操作。
下一步,我們用producid值為725來更新19762條記錄。實際上我們更新24762條記錄(包含上一步5000條更新的記錄),比使統計信息無效的更新的記錄(24763)少1條。
1 SET ROWCOUNT 197622 UPDATE SalesOrderDetail SET ProductID=725 WHERE ProductID<>7253 SET ROWCOUNT 04 SET STATISTICS IO ON5 SELECT * FROM SalesOrderDetail WHERE ProductID=725
執行計劃里估計行數是374,這是基于上次更新操作收集的統計信息。優化器基于統計信息,選擇了索引查找和書簽查找作為最優計劃。完成這個操作需要25206個邏輯讀。
現在我們更新再多一條記錄使統計信息無效。
1 SET ROWCOUNT 12 UPDATE SalesOrderDetail SET ProductID=725 WHERE ProductID<>7253 SET ROWCOUNT 04 SET STATISTICS IO ON5 SELECT * FROM SalesOrderDetail WHERE ProductID=725
(這里我跌了個跟頭,在SQL SERVER 2008R2里首次執行,始終是下列結果:
回家吃飯還在思考這個問題,一想原因,應該是自動創建統計信息和自動更新統計信息被停用的原因(上篇文章理解統計信息(3/6):誰創建和管理統計信息?在性能調優中,統計信息的作用代碼執行后未還原為默認設置),在數據庫屬性里一看,果然是False狀態,趕緊用下列語句啟用,出現的問題立馬消失!
1 ALTER DATABASE StatisticsDB SET AUTO_CREATE_STATISTICS ON2 ALTER DATABASE StatisticsDB SET AUTO_UPDATE_STATISTICS ON
看來計算機是最誠實可靠的,即使計算機犯了錯,也是因為人犯錯造成的! )
和我們預期的一樣,SELECT語句觸發了自動更新統計信息,計劃中的估計行數和實際行數已經非常接近了。這可以幫助優化器選擇更好的執行計劃。優化器選擇了表掃描而不是索引查找和書簽查找。SELECT操作只進行了1495個邏輯讀來選取25137條記錄,比起25212個邏輯讀才選擇2516條記錄。在第一步,我們只更新了5000條記錄,如果統計信息在那個時候更新的話,優化器可能會選擇表掃描作為最優計劃而不是索引查找和書簽查找。那樣的話就可以只用1495個邏輯讀代替5390個邏輯讀來完成操作,這樣就會好很多。
從這個例子我們可以清楚看到,對于自動更新統計信息的閥值對于獲得最優性能還是不夠好。對于大表來說會更糟。我們就需要人為去更新統計信息用來保證長須的最佳性能,當然更新的頻率要看具體的工作量。
在進行大量DML操作后,統計信息都會過期,在查詢計劃訪問統計信息前,統計信息都不會自動更新。更清楚的說,SQL Server會在下列情況自動更新統計信息:
繼續圍觀理解統計信息(5/6):如何檢測過期的統計信息。
新聞熱點
疑難解答