Sql-Server

記憶體中的參數嗅探和多個計劃

  • May 15, 2021

我們有一個多租戶數據庫。FirmID 是分區鍵,我們有很多不同的公司。

我遇到了一個參數嗅探問題,我花了很多時間來解決它。

我寧願不使用任何

$$ Options $$關於查詢。 我最近的想法是更改我用於公司的參數的名稱。在下面的片段中,您將看到我沒有使用@FirmID,而是將其稱為@Firm611,其中611 是ID 的實際公司。這將為我提供每家公司的獨特查詢。

select
   c.ID [_cid],
   c.Name [Name]  
from vwClaims c with(nolock)  
where c.FirmID=@Firm611       
and (c.Name is not null and c.Name!='')
   
select
   c.ID [_cid],
   c.Name [Name]
from vwClaims c with(nolock)
where c.FirmID=@Firm625
and (c.Name is not null and c.Name!='')

執行 Brent Ozar 的 sp_BlitzCache 後,我發現它只是編譯為相同的查詢並導致重複的記憶體條目:

與同一查詢雜湊關聯的查詢計劃

我的問題是我讀的結果對嗎?即使我更改了參數名稱,它真的仍然使用相同的計劃和參數嗅探嗎?

這裡有幾個不同的問題。

問:如果兩個相同的查詢使用兩個不同的參數名稱,SQL Server 會做什麼?

SQL Server 將為每個查詢建構一個單獨的執行計劃。SQL Server 將即使是最輕微的文本更改(甚至像空格一樣簡單)都視為不同的查詢。

每個變體都將完全獨立於另一個變體進行編譯和記憶體。

問:這兩個查詢會得到不同的計劃還是相同的計劃?

SQL Server 將檢查參數的內容並基於此建構執行計劃。@Firm611 的計劃將根據@Firm611 的選擇性編制,@Firm625 的計劃將根據@Firm625 的選擇性編制。

如果這兩個參數碰巧具有相同的選擇性,則兩個編譯計劃最終可能具有完全相同的形狀、相同的索引使用、相同的記憶體授予等。但是,它們仍將被獨立編譯並分別記憶體。

問:sp_BlitzCache 中的警告是什麼意思?

警告表明您有兩個“不同”的查詢,它們並沒有真正的不同——它們得到的計劃形狀相同。這意味著您正在浪費時間建構它們的多個版本,因為:

  • 您正在浪費 CPU 資源重複編譯相同的計劃
  • 您正在浪費記憶體記憶體不同的計劃(實際上是相同的計劃)

問:OPTION (RECOMPILE) 會解決這個問題嗎?

不,因為那樣它們每次執行時都會重新編譯,從而導致更高的 CPU 使用率。我只建議在查詢每分鐘執行一次或更少時重新編譯提示 - 如果它執行的頻率高於此,我開始擔心 CPU 成本,因為您使用該技巧的所有查詢的頻繁編譯。

我使用每分鐘一次的基線,因為這只是這個查詢 - 您必須記住伺服器上的總工作量。如果您在每個查詢上添加重新編譯提示,您最終可能會獲得 100% 的 CPU 使用率 - 即使每個單獨的查詢每秒僅執行 1 次,當您考慮每個查詢時,這也是很多編譯。

我發現它只是編譯為相同的查詢並導致重複的記憶體條目

我可以明白為什麼你會以這種方式讀取輸出(DistinctPlanCount),但似乎程式碼有一個錯誤。COUNT聚合不應該引用query_plan_hash(這query_hash是一個常數)。

FROM ( SELECT query_hash,
COUNT(DISTINCT(query_hash)) AS DistinctPlanCount,
COUNT(query_hash) AS PlanCount

FROM sys.dm_exec_query_stats
GROUP BY query_hash
) AS q

應該:

FROM
(
   SELECT
       qs.query_hash,
       COUNT(DISTINCT(qs.query_plan_hash)) AS DistinctPlanCount,
       COUNT(qs.query_plan_hash) AS PlanCount
   FROM sys.dm_exec_query_stats AS qs
   GROUP BY
       qs.query_hash
) AS q

您正在使用該過程的相當舊版本,但問題仍然存在於最新版本中(儘管已應用其他修復程序)。

即使我更改了參數名稱,它真的仍然使用相同的計劃和參數嗅探嗎?

不,您將按預期獲得每個參數名稱的單獨計劃。每個計劃(每個公司)在第一次執行時嗅探自己的參數,並在後續呼叫中重用該計劃,直到由於任何原因發生重新編譯。

換句話說,您的計劃正在按您希望的那樣工作。當第一次執行時的 per-firm 參數值沒有為同一公司的所有後續執行生成合理的計劃時,您仍然會遇到問題。這只是普通的參數敏感計劃問題,具有通常的解決方法

正如大衛布朗所說:

這是將所有租戶放在同一個數據庫中的大問題之一。您應該計劃至少將“大”和“小”租戶分開。此外,在查詢中簡單地對tenantId 進行硬編碼以獲得特定於租戶的計劃也是完全合適的。參數用於您想要共享計劃時。

引用自:https://dba.stackexchange.com/questions/291597