小心SQL SERVER 2014新特性——基數評估引起一些性能問題
在前陣子寫的一篇博文“ SQL SERVER 2014 下IF EXITS 居然引起執行計劃變更的案例分享 ”里介紹了數據庫從SQL SERVER 2005升級到 SQL SERVER 2014后,發現一個SQL出現性能問題,當時分析后發現執行計劃變了,導致SQL出現了性能問題。但是沒有徹底搞清楚為什么出現這種情況。當時看到Actual Number of Rows 與Estimated Number of Rows之間的偏差較大( 統計信息是最新的 ),以為是優化器的Bug造成的。其實罪魁禍首是SQL SERVER 2014新特性——基數評估(Cardinality Estimator)所引起的。IF EXISTS完全成了我這個標題黨的替罪羊(罪過罪過)。下面我再就這個問題展開做一次分析。
查看該SQL語句的實際執行計劃,在屬性里面我們可以看到CardinalityEstimationModelVersion的值為120,120表示這是新的基數評估,70就是老的基數評估
其實當數據庫的兼容級別為120的時候,默認使用新的基數評估。也就是說啟用了新的基數評估,那么我們現在使用查詢跟蹤標記9481來關閉新的基數評估,使用老的基數評估。
DBCC TRACEON(9481, 1);
GO
啟用跟蹤標記9481后,這個SQL語句的執行計劃變了(可以對比圖4),可以看到CardinalityEstimationModelVersion的值也變為了70。SQL語句一秒就執行完了。這個是因為基數評估出現了偏差導致了不合適的JOIN算法。
我們對比下面”圖四:舊執行計劃“,發現其實還是使用Nested Loops,只是外部循環表與內部循環表變了。
圖四:舊執行計劃
那么關于新的基數評估(Cardinality Estimator)特性,你想多了解一些這方面的知識,可以參考官方文檔 Optimizing Your Query Plans with the SQL Server 2014 Cardinality Estimator 。 中文翻譯版本可以參考 SQL Server 2014新特性——基數評估(白皮書閱讀筆記) 。 下面是官方文檔關于基數評估出現偏差可能會造成的一些后果:
對于基數評估,每個執行計劃中的運算符都有評估值輸入,這個值決定了優化器使用什么算法的操作符,同時也決定了最終的執行計劃。所以如果評估出現偏差,會導致執行計劃選擇出現偏差,導致無法選出一個高效的執行計劃。
評估出現偏差會出現以下結果:
如果評估過小:
1.原本可以使用并行計劃更加有效的,現在使用串行計劃
2.不合適的join算法
3.不合適的索引選擇,和索引訪問方法
如果評估過大:
1.原本使用串行計劃更加有效,現在使用并行計劃
2.不合適的join算法
3.不合適的索引選擇,和索引訪問方法
4.過多的內存分配
5.內存浪費和沒必要的并發
上面這段對應的英文資料如下所示(英語原文作參考,這才是原汁原味的信息):
The individual operator cost models receive the estimates as input. The estimates are a major factor in deciding which physical operator algorithms and plan shapes (such as join orders) are chosen. They also determine the final query plan that executes. Given these critical plan choices, when the cardinality estimation process contains a significantly skewed assumption, this can lead to an inefficient plan choice. This can, in turn, result in degraded performance .
Under estimating rows can lead to memory spills to disk, for example, where not enough memory was requested for sort or hash operations. Under estimating rows can also result in:
- The selection of serial plan when parallelism would have been more optimal.
- Inappropriate join strategies.
- Inefficient index selection and navigation strategies. </ol>
- Selection of a parallel plan when a serial plan might be more optimal.
- Inappropriate join strategy selection.
- Inefficient index navigation strategies (scan versus seek).
- Inflated memory grants.
- Wasted memory and unnecessarily throttled concurrency. </ol>
Inversely, over estimating rows can lead to:
Improving the accuracy of row estimates can improve the quality of the query execution plan and, as a result, improve the performance of the query.
其實關于SQL SERVER 2014這個新的基數評估(Cardinality Estimator)特性,確實造成了不少SQL出現性能問題。我們數據庫升級到SQL SERVER 2014后,被這個新特性坑慘了,由于沒有選擇最優的執行計劃,導致一些SQL出現嚴重的性能問題,也間接導致了SQL之間的阻塞(block)急劇上升。開發人員和我都在救火隊員的角色中疲于奔命。最后我不得不采取將數據庫的兼容基本從120降為110。從而里面解決了這個問題。另外從我搜索的一些資料看,SQL SERVER 2014這個新的基數評估(Cardinality Estimator)這個新特性確實還有很多不完善的地方。因為也有不少人都發現升級到SQL Server 2014后出現了性能問題。例如:
Query is slow in SQL Server 2014, fast in SQL Server 2012
參考資料:
</div> </span></span></span></span></span></span></span></span>