1. 程式人生 > >T-SQL執行內幕(4)——優化

T-SQL執行內幕(4)——優化

本文屬於SQL Server T-SQL執行內幕系列


    接上文,當解析和編譯完成後,請求的生命週期就進入下一步——優化(Optimisation)。在SQL語言中,優化的本質就是找最好的路線。意思是在多種可能的候選資料訪問方式中選擇最佳一個。比如兩表關聯的簡單查詢語句,每個表有1個索引,那麼就有4種可能的資料訪問方式(AB兩表的索引掃描、AB兩表的索引查詢,A掃描B查詢、A查詢B掃描)。隨著表和索引的數量增長可以預估得到可能的方式呈指數級增長。如果再考慮JOIN演算法如(巢狀迴圈nested loop、雜湊聯接hash、合併聯接merge)等,那複雜度將可能超乎你想象。

    SQL Server及主流關係型資料庫管理系統都使用基於開銷/成本的優化(cost based optimizer,CBO),意味著優化器會考慮儘可能多的候選方案,然後計算出它們的預估開銷,最後選擇最小開銷的那個執行計劃作為實際執行之用。根據網上資料顯示,N個表關聯,理論上最少要分析N!次,最多可達(2N-2)!/(N-1)!次,也就是6表關聯可能需要720次分析,10表關聯就可以達到3628萬次。所以本人認為的優化的核心結論就是——少。其中一個就是關聯數量儘可能少。但是如何才算少,沒有標準,It depends!

    這個開銷成了“效能”的根本,SQL Server並沒有公佈官方計算公式,但是從使用和多年來專家們的測試,開銷的計算主要來自於:每個表的大小、列值的分佈(這兩個方面可以通過統計資訊獲得)、當前CPU的消耗、執行計劃中操作符的所需記憶體等,這些計算結果最終會算入執行計劃每個操作符的具體開銷,然後逐級彙總到整個執行計劃的總體開銷。優化器選擇的依據就是這個總體開銷。關於統計資訊,可以看一下我的另外幾篇文章:

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

2. SQL Server新基數估量器

    說明:執行計劃由一系列的操作符(如果使用圖形化執行計劃就是那些圖示)組成,每個操作符都完成一定的操作(包含資料訪問、資料關聯、資料處理等)。

    但是候選執行計劃的開銷的預估非常複雜而且因為候選數量的巨大導致優化器不可能總是預估所有的可能方案,所以很多時候優化器在一個負載很重的伺服器中,並不能選擇真正的最佳執行計劃。另外為了避免編譯優化這類高CPU開銷操作,SQL Server會盡可能把選擇的執行計劃儲存在記憶體的一個專門儲存中(Plan cache),下次執行時會先檢查是否存在對應的計劃快取,沒有再進行優化。

    關於計劃快取可以查閱官方文件:https://msdn.microsoft.com/zh-cn/library/ms181055.aspx

    效能優化的本質簡單來說,就是通過在合理的資源使用前提下,儘可能縮短響應時間。注意兩個關鍵詞:合理的資源使用及響應時間。很多人以響應時間為唯一的效能指標,但是試想一下(實際也出現過),當某個語句寫法A執行時間5秒且執行頻率低,但是幾乎佔據了伺服器所有資源導致其他操作無法進行。寫法B執行時間10秒,但是佔用資源很少,其他操作沒有明顯受影響。這種情況下,如果要權衡得失,我會選擇後者,雖然單個操作慢一點,但是如果能在可接受的範圍且並非核心操作,那麼要以大局為重。

    本人曾經優化過這麼一套系統,由於最大並行度沒有控制,有些高開銷語句一執行就把CPU佔滿,導致其他操作全部等待。並且非常頻繁。整個系統的 響應時間很久。通過簡單地配置最大並行度及優化long running queries。系統馬上順暢,當然後續還有很多的優化工作比如索引、寫法、設計、伺服器配置等。

    更多細節在本人的《SQL Server效能優化與管理的藝術》一書第八章。

總結

    從本質上來說,解析、編譯和優化過程,是把邏輯操作樹的操作對映到物理操作中,然後由儲存引擎來執行。優 化的產物是一個執行計劃,包含了 一系列物理操作符的樹結構。從理論上來說,為了找到最佳執行方案,優化器需要產生所有可能的執行計劃並正確地評估每個執行計劃的開銷。但是哪怕簡單的語句都可能產生數千甚至百萬級的執行計劃,為了權衡得失,優化器會作出取捨,比如如果優化器使用1秒來找到一個執行計劃只需要1分鐘完成執行,那麼就不會再去檢查是否還有更好的執行計劃。

    在優化器根據基數預估找到認為最佳的執行計劃之後,就會把執行計劃進行快取並傳入執行引擎按照這個“路徑”去執行並返回結果。同時生成的執行計劃會放到記憶體的快取中(計劃快取,plan cache),下一次執行時,如果該快取的執行計劃可用,則跳過優化部分直接使用,減少開銷,否則就重新按照基數預估進行新的執行計劃生成。