1. 程式人生 > >什麼東西導致了執行計劃的嚴重錯誤——需要更新統計資訊嗎?

什麼東西導致了執行計劃的嚴重錯誤——需要更新統計資訊嗎?

    原文出處:What caused that plan to go horribly wrong – should you update statistics?

    由於本人確實遇到過這類問題,但是基於水平和經歷,不打算重複造輪子,所以把大牛的文章翻譯一下以供大家參考。以下是譯文:

過去幾年裡,我(作者)遇到這類情景:

    有一個儲存過程在大部分時間裡面都執行得很好,但是突然就不行了。其效能出現斷崖式下降。你不知道原因,不過有人說:“一定是統計資訊的問題”。確實,如果你有大量時間,檢查執行計劃的時候,會發現估計行數和實際行數有很大出入。OK,應該就是統計資訊的問題了。

    但是,也許不是。 首先要知道,儲存過程、使用 sp_executesql 執行的引數化語句和客戶端提交的預準備(prepared)語句都重用快取的執行計劃。這些執行計劃使用稱為“引數嗅探(parameter sniffing)”的技術來定義。引數嗅探本身不是什麼問題,但是在後續使用相同語句、儲存過程執行時就可能成為問題。如果某些語句的執行計劃根據引數只返回1行資料,那麼執行計劃可能很簡單——使用一個非聚集索引和書籤查詢來查詢資料。但是相同的語句在再次執行但引數會導致返回數千行資料時,這個執行計劃可能就不夠合理了。

    執行計劃並不儲存在磁碟中,當快取中不存在執行計劃時才會建立。所以可能會有很多原因導致快取中找不到所需的執行計劃。如果剛好出現了一個不典型的引數集作為執行計劃生成的第一組引數(準確來說應該是“已失效”),那麼意味著這個不常用的引數的執行計劃會一直被使用,而且這種執行計劃往往是不夠高效的。同時,此時你研究執行計劃時也會發現估計行數和實際行數的巨大差異。這個並不是統計資訊的問題。

    如果確實是統計資訊的問題,那麼要怎麼做呢?

    通常來說就是UPDATE STATISTICS 表名或UPDATE STATISTICS 表名 索引名(執行計劃用到的索引)。然後再次執行儲存過程。這次準確了。所以你會覺得是統計資訊問題。

    但是你可能只看到了伺服器端的統計資訊已經更新,當你更新統計資訊,SQL Server通常會使執行計劃失效。因此在快取中的執行計劃也會失效。當重新執行的時候,就會建立新的執行計劃,這個執行計劃會使用當前引數來生成。

    那麼對於這類問題要如何操作?

    首先,不要動不動就先更新統計資訊。如果有一個儲存過程出現了這個問題,應該考慮先進行重編譯,看看是否能得到更好的執行計劃。重編譯(如使用sp_recompile)會使得快取中的對應執行計劃失效。這個操作不僅快速簡單,並且還能驗證是否統計資訊出問題。如果此時有效,那麼可能要考慮改進程式碼。關於這部分可以參考一下這篇文章:

Stored procedures, recompilation and .NetRocks。如果無效,那麼才考慮更新統計資訊。不過,首先要做的是確保程式碼的編譯值和執行值相同。可以通過包含實際執行計劃的屬性視窗查詢:


    如果使用正確值還是不行,那可能就是統計資訊問題,但是統計資訊往往被詬病,可是通常不是真正原因,執行計劃才是。

    當更新統計資訊時執行計劃是否總會失效?答案是否定的。關於這個問題,可以參考兩篇文章:http://erinstellato.com/2012/01/statistics-recompilations/ 和 Statistics and Recompilations, Part II。簡單來說,從SQL 2012開始,更新統計資訊並不引起執行計劃失效。


總結

    在遇到相似現象時,應該先檢查是否出現引數嗅探問題,而不是馬上更新統計資訊。另外注意除了儲存過程之外,使用 sp_executesql 執行的引數化語句和客戶端提交的預準備(prepared)語句都可能出現。

    另一篇本人(譯者)的系列文章:理解效能的奧祕——應用程式中慢,SSMS中快(1)——簡介