本文屬於《理解效能的奧祕——應用程式中慢,SSMS中快》系列
接上文:理解效能的奧祕——應用程式中慢,SSMS中快(2)——SQL Server如何編譯儲存過程
在我們開始深入研究如何處理引數嗅探相關的效能問題之前,由於這個課題過於廣泛,所以首先先介紹一些跟引數嗅探沒有直接關係的內容,但是又會導致語句在SSMS和應用程式中存在效能差異的情況。
替換變數和引數:
前面已經接觸過,但是在這裡對其進行擴充套件。有時會看到論壇上有人說,某個儲存過程很慢,但是把相同的語句提取出來單獨執行就很快。真相就是:語句涉及了變數,可能是本地變數或者引數。為了單獨檢查語句問題,他們把變數替換成常量。但是前面提到過,單獨的語句和在儲存過程中很不一樣,當用常量替換變數時,SQL Server可以更加準確地預估影響行數,從而生成更好的查詢計劃。並且SQL Server不用考慮下一次執行是,常量會被改變。
另外一個類似的錯誤就是把引數放入變數中,如:
CREATE PROCEDURE some_sp @par1 int
AS ... -- 某些使用到變數 @par1的語句
DECLARE @par1 int SELECT @par1 = 4711 -- 接下來的語句
EXEC sp_executesql N'-- 牽涉到引數的語句 @par1', N'@par1 int', 4711
還有一種方式就是建立一個帶有問題語句的虛擬儲存過程,為了避免資料庫中存在垃圾物件,你可以使用臨時儲存過程:
CREATE PROCEDURE #test @par1 int AS -- 問題語句.
阻塞:
索引檢視和索引化的計算列:
在SQL 2000時代,這個問題比後續版本嚴重很多,從SQL Server 2005開始,所以檢視和索引化後的計算列(包括SQL 2008加入的過濾索引filter index)在語句編譯期間,下面的設定必須為ON:QUOTED_IDENTIFIER, ANSI_NULLS, ANSI_WARNINGS, ANSI_PADDING, CONCAT_NULL_YIELDS_NUL,而NUMERIC_ROUNDABORT必須為OFF。
而在SQL 2000中,應用程式使用預設SET選項對索引化的計算列和索引檢視是沒有什麼好處的。但是即使存在引數嗅探,當你在SSMS或查詢分析器中執行是,效能也往往可能好很多,因為ARITHABORT預設為ON。
但是由於SQL 2000時代過去太久了,估計已經很少人還在用,所以如果對這個內容有興趣,讀者可以去看原文,因為這部分主要是對SQL 2000描述的。點選開啟連結
連結伺服器的問題:
SELECT C.* FROM SOME_SERVER.Northwind.dbo.Orders O JOIN Customers C ON O.CustomerID = C.CustomerID WHERE O.OrderID > 20000

以普通使用者執行是,得到的執行計劃如下:



當查詢直接訪問本地例項的表時,優化器可以得到語句相關的所有表的統計資訊,不存在額外的許可權檢查。 當SQL Server訪問一個連結伺服器時,伺服器之間的通訊並沒有使用專用的的協議,而是使用標準的OLE DB介面訪問連結伺服器,其他諸如sql server例項、oracle、文字檔案或任何自定義資料來源也是如此。具體如何獲取統計資訊還要取決於資料來源和請求的OLE DB Provider。 在這種情況下,SQL Server Native Client會通過兩步獲得統計資訊(可以在遠端伺服器上使用Profiler檢查):
- SQL Server Native Client驅動會先執行sp_table_statistics2_rowset返回統計資訊包含的列的資訊,也包括了基數資訊和密度資訊。
- 驅動執行DBCC SHOW_STATISTICS,返回完整的分佈統計資訊。
- 可以把使用者加到遠端資料庫的db_ddladmin角色中,但是這個意味著使用者可以增加和刪除表,一般不建議。
- 預設情況下,一個使用者連線遠端伺服器的時候,都是使用遠端伺服器中相同的賬號,但是可以使用sp_addlinkedsrvlogin對映 一個登入號,以便使用者有許可權執行屬於db_ddladminde 角色的事情。但是這個賬號必須是SQL 登入賬號,所以要確保遠端伺服器是否啟用了混合身份驗證,但是這種方案從安全形度來說還是有問題。
- 某些情況下可以改寫成OPENQUERY強制在遠端伺服器中進行預估。如果查詢包含了多個遠端表的情況下,這種方式特別有效。(但是也有風險,因為優化器可能只能從遠端伺服器中獲取更少的統計資訊。)
- 也可以使用完整的提示和計劃嚮導來實現你期望的查詢計劃。
- 最後,要考慮一下是否真的有必要使用連結伺服器而不能放在同一個 伺服器上?能否修改?或者有其他解決方案?
小結:
- 收集解決引數嗅探問題的資訊