天天看點

參數嗅探(Parameter Sniffing)(1/2)

這個問題會在參數話的SQL語句(例如存儲過程)與SQL Server裡的計劃緩存機制結合的時候會出現。這個文章分為2個部分,第1部分會介紹下參數嗅探(Parameter Sniffing)的概況,第2部分我們介紹下如何解決這個問題。

什麼是參數嗅探(Parameter Sniffing)

在SQL Server裡當你執行參數話的SQL查詢時,查詢優化器會基于第一個提供的參數值編譯執行計劃。然後生成的執行計劃在計劃緩存裡緩存作為後期的重用。這就是說SQL Server後續會直接重用這個計劃,而不管每次你提供的不同參數值。我們需要識别2類參數值:

  • 參數編譯值(Compile time values)
  • 參數運作值(Runtime values)

參數編譯值是用于查詢優化器生成實體執行計劃的值。參數運作值是提供給執行計劃運作的值。對于第一次執行這些值是一緻的,但接下來的執行,這些值就很可能不同了。這就會帶來嚴重的性能問題,因為執行計劃隻為編譯值而優化的,不是為你接下來提供的不同運作值而優化。

如果你在第一次查詢執行的時候提供了一個特定值,然後查詢優化器選擇了非聚集索引查找和書簽查找運算符從你表裡來擷取所有查詢列。這樣的執行計劃隻對特定值有意義,非特定值的話,你的邏輯讀數就會很高,SQL Server會選擇全表掃描,忽略定義的非聚集索引。SQL Server選擇這2個計劃的決定點就是所謂的臨界點(Tipping Point) 。

如果書簽查找的計劃被緩存,SQL Server就不會理會輸入值,盲目重用緩存的計劃。這個情況下SQL Server的保護機制就失效了,隻從計劃緩存裡執行緩存的計劃。作為副作用,你的IO成本(邏輯都)就會爆表,查詢的性能就會非常糟糕。我們來示範下這個情況,下面的腳本會建立一個簡單的表,在表的第2列有不平均的資料分布(就第1條值是1,剩下的1499條值都是2)。

1 -- Create a test table
 2 CREATE TABLE Table1
 3 (
 4     Column1 INT IDENTITY,
 5     Column2 INT
 6 )
 7 GO
 8 
 9 CREATE NONCLUSTERED INDEX idx_Test ON Table1(Column2)
10 
11 -- Insert 1500 records into Table1
12 INSERT INTO Table1 (Column2) VALUES (1)
13 
14 SELECT TOP 1499 IDENTITY(INT, 1, 1) AS n INTO #Nums
15 FROM
16 master.dbo.syscolumns sc1
17 
18 INSERT INTO Table1 (Column2)
19 SELECT 2 FROM #nums
20 DROP TABLE #nums
21 GO      

基于這個不平均的資料分布和臨界點,對于同個邏輯查詢會有2個不同的執行計劃,點選工具欄的

參數嗅探(Parameter Sniffing)(1/2)

顯示包含實際的執行計劃:

1 SELECT * FROM dbo.Table1 WHERE Column2=1
2 SELECT * FROM dbo.Table1 WHERE Column2=2      
參數嗅探(Parameter Sniffing)(1/2)

現在當你建立一個存儲過程時,查詢優化器會根據第一次提供的參數值生成執行計劃,然後在接下來的執行中就會盲目重用了。

1 -- Create a new stored procedure for data retrieval
2 CREATE PROCEDURE RetrieveData
3 (
4     @Col2Value INT
5 )
6 AS
7     SELECT * FROM Table1
8     WHERE Column2 = @Col2Value
9 GO      
1 SET STATISTICS IO ON 
2 EXEC dbo.RetrieveData @Col2Value = 1 -- int
3 EXEC dbo.RetrieveData @Col2Value = 2 -- int      
參數嗅探(Parameter Sniffing)(1/2)
參數嗅探(Parameter Sniffing)(1/2)

現在當你用1值運作存儲過程時,隻傳回1條記錄,查詢優化器在執行計劃裡選擇書簽查找。查詢隻産生3個邏輯讀。但是當你用2值運作存儲過程時,緩存的計劃被重用,書簽查找反複執行1499次。每條記錄上都執行!查詢現在産生了1505個邏輯讀。這和剛才的執行完全不同。當你看檢視2值裡執行計劃裡,SELECT運算符的屬性時,在參數清單裡你可以看到:

參數嗅探(Parameter Sniffing)(1/2)

如你所見它們是不一樣的,參數編譯值是1,參數運作值是2。這就是說在你面前的執行都是基于參數值1而優化的,但實際上你傳給存儲過程的參數值是2。這就是SQL Server裡的參數嗅探(Parameter Sniffing)問題。

小結

 如你所見,在SQL Server裡很容易碰到這個問題。每次你使用參數話的SQL查詢(像在存儲過程裡),當表資料分布不平均,提供的非聚集索引沒有覆寫到查詢列時,你就會碰到這個問題。這裡我們隻介紹了這個問題,下篇文章我會向你展示如何處理這個問題,即SQL Server向你提供了哪些方案來解決這個問題。

參考文章:

https://www.sqlpassion.at/archive/2014/10/20/parameter-sniffing-part-1/

注:此文章為

WoodyTu

學習MS SQL技術,收集整理相關文檔撰寫,歡迎轉載,請在文章頁面明顯位置給出此文連結!

若您覺得這篇文章還不錯請點選下右下角的推薦,有了您的支援才能激發作者更大的寫作熱情,非常感謝!

繼續閱讀