SQL Server 查詢最佳化(測試02)參數嗅探-執行計畫選擇

來源:互聯網
上載者:User

標籤:

最近常看到"參數嗅探"這個詞,看了幾篇文章,於是就自己摸索做個測試來加深印象!去官網下載了資料庫:AdventureWorks2012直接測試吧!找幾個熟悉的表關聯起來,用ProductID作為條件找到兩個ID返回行數相差較大的值.ProductID=870(4688行)ProductID=897(2行)

【測試一】

--先清空計畫快取DBCC FREEPROCCACHE--執行前先開啟計數器監控查看(分開執行以下查詢)select sdh.SalesOrderID,sdh.SalesOrderNumber,P.ProductID,p.Name,sod.LineTotalfrom [Sales].[SalesOrderHeader] sdhinner join [Sales].[SalesOrderDetail] sod on sdh.SalesOrderID = sod.SalesOrderIDinner join [Production].[Product] p on sod.ProductID = p.ProductIDwhere P.ProductID =870select sdh.SalesOrderID,sdh.SalesOrderNumber,P.ProductID,p.Name,sod.LineTotalfrom [Sales].[SalesOrderHeader] sdhinner join [Sales].[SalesOrderDetail] sod on sdh.SalesOrderID = sod.SalesOrderIDinner join [Production].[Product] p on sod.ProductID = p.ProductIDwhere P.ProductID =897


先看計數器,有兩個綠色的峰值為1.就是上面分別執行時發生的編譯次數.

--查看緩衝對象執行類型:Adhoc(即時查詢)SELECT cacheobjtype,objtype,refcounts,usecounts,[sql]FROM sys.syscacheobjectsWHERE [sql] LIKE '%SalesOrderID%' AND [sql] NOT LIKE '%sys%'--再用視圖查看緩衝查詢計劃和計劃大小SELECT refcounts,usecounts,cacheobjtype,size_in_bytes,[text],query_planFROM sys.dm_exec_cached_plans CROSS APPLY sys.dm_exec_sql_text(plan_handle)CROSS APPLY sys.dm_exec_query_plan(plan_handle)WHERE [text] LIKE '%SalesOrderID%' AND [text] NOT LIKE '%sys%'


可以看到產生了兩個不同的查詢計劃(query_plan),並且佔用了緩衝(size_in_bytes).


以上這種寫法的優缺點是:
缺點: 如果查詢條件值發生變化,每次都會作為新的查詢語句編譯第一次,不僅消耗CPU,而且產生新的查詢計劃也會佔用緩衝.
優點:每次執行計畫都是最優的


【測試二】
現在換成帶參數的形式.

--先清空計畫快取DBCC FREEPROCCACHE--ProductID=870(4688行)ProductID=897(2行)DECLARE @ProductID INTSET @ProductID = 870select sdh.SalesOrderID,sdh.SalesOrderNumber,P.ProductID,p.Name,sod.LineTotalfrom [Sales].[SalesOrderHeader] sdhinner join [Sales].[SalesOrderDetail] sod on sdh.SalesOrderID = sod.SalesOrderIDinner join [Production].[Product] p on sod.ProductID = p.ProductIDwhere P.ProductID [email protected]DECLARE @ProductID INTSET @ProductID = 897select sdh.SalesOrderID,sdh.SalesOrderNumber,P.ProductID,p.Name,sod.LineTotalfrom [Sales].[SalesOrderHeader] sdhinner join [Sales].[SalesOrderDetail] sod on sdh.SalesOrderID = sod.SalesOrderIDinner join [Production].[Product] p on sod.ProductID = p.ProductIDwhere P.ProductID [email protected]

看計數器,同樣有兩個綠色的峰值為1.發生了2次編譯

--再用視圖查看緩衝查詢計劃和計劃大小SELECT refcounts,usecounts,cacheobjtype,size_in_bytes,[text],query_planFROM sys.dm_exec_cached_plans CROSS APPLY sys.dm_exec_sql_text(plan_handle)CROSS APPLY sys.dm_exec_query_plan(plan_handle)WHERE [text] LIKE '%SalesOrderID%' AND [text] NOT LIKE '%sys%'



這會可以看到產生了兩個相同的查詢計劃(query_plan),緩衝大小(size_in_bytes)也就相同了.
還有另一點不同之處就是,執行計畫分兩部分執行,第一部分參數賦值,第二部分查詢語句.
因此第二部分才用了相同的查詢計劃.

以上這種寫法的優缺點是:
缺點: 如果查詢條件值發生變化,每次都會作為新的查詢語句編譯第一次,不僅消耗CPU,而且產生新的查詢計劃也會佔用緩衝.
還有就是,由於查詢計劃相同.當返回行數相差較大.有的查詢效能並不是較好的.
優點: 當返回資料量都差不多的時候是較好的,查詢最佳化工具根據參數估計一個較好的查詢計劃,有利於對查詢計劃進行控制.
(但是比較發現,這種寫法比上一種還差!最後再測試)


【測試三】

--這時把執行語句放到預存程序CREATE PROCEDURE P_Test(@ProductID INT)ASBEGINselect sdh.SalesOrderID,sdh.SalesOrderNumber,P.ProductID,p.Name,sod.LineTotalfrom [Sales].[SalesOrderHeader] sdhinner join [Sales].[SalesOrderDetail] sod on sdh.SalesOrderID = sod.SalesOrderIDinner join [Production].[Product] p on sod.ProductID = p.ProductIDwhere P.ProductID [email protected]END--ProductID=870(4688行)ProductID=897(2行)--執行預存程序DBCC FREEPROCCACHEEXEC P_Test @ProductID = 870EXEC P_Test @ProductID = 897--查看緩衝對象執行類型:Proc(預存程序)SELECT cacheobjtype,objtype,refcounts,usecounts,[sql]FROM sys.syscacheobjectsWHERE [sql] LIKE '%SalesOrderID%' AND [sql] NOT LIKE '%sys%'SELECT refcounts,usecounts,cacheobjtype,size_in_bytes,[text],query_planFROM sys.dm_exec_cached_plans CROSS APPLY sys.dm_exec_sql_text(plan_handle)CROSS APPLY sys.dm_exec_query_plan(plan_handle)WHERE [text] LIKE '%SalesOrderID%' AND [text] NOT LIKE '%sys%'


這時發現,只有1個緩衝計劃!無論參數怎麼改變都是只緩衝一個查詢計劃,這樣就省去了記憶體的佔用.
但是這個方法的優缺點就更明顯了.


這種寫法的優缺點是:
缺點: 如果查詢條件值發生變化,每次都會編譯1次,消耗CPU.
最重要的缺點是,查詢計劃的產生,是以第一次執行預存程序所傳遞的參數值來確定的!
也就是說,在預存程序建立後,傳遞參數首次執行預存程序,該參數返回的行數或多或少都會影響到執行計畫的永久確定.

DBCC FREEPROCCACHE--情況計畫快取
EXEC P_Test @ProductID = 870--現在換870先執行
EXEC P_Test @ProductID = 897--剛才為897首次執行預存程序

執行後再看查詢計劃,又是不一樣了!
所以這點要注意,為什麼同樣的預存程序,表統計資訊沒問題,但是有的查詢快,有的慢.
跟蹤把具體語句查出來運行又正常,就如同上面【測試一】一樣。



優點: 省下了記憶體!(不過記憶體一般用不了多少)



【測試四】

--這時把執行語句放到預存程序CREATE PROCEDURE P_Test2(@ProductID INT)ASBEGINDECLARE @ID INTSET @ID = @ProductID --區別在這裡select sdh.SalesOrderID,sdh.SalesOrderNumber,P.ProductID,p.Name,sod.LineTotalfrom [Sales].[SalesOrderHeader] sdhinner join [Sales].[SalesOrderDetail] sod on sdh.SalesOrderID = sod.SalesOrderIDinner join [Production].[Product] p on sod.ProductID = p.ProductIDwhere P.ProductID [email protected] END--ProductID=870(4688行)ProductID=897(2行)DBCC FREEPROCCACHEEXEC P_Test2 @ProductID = 870EXEC P_Test2 @ProductID = 897SELECT refcounts,usecounts,cacheobjtype,size_in_bytes,[text],query_planFROM sys.dm_exec_cached_plans CROSS APPLY sys.dm_exec_sql_text(plan_handle)CROSS APPLY sys.dm_exec_query_plan(plan_handle)WHERE [text] LIKE '%SalesOrderID%' AND [text] NOT LIKE '%sys%'





這種方法優缺點與【測試三】幾乎一樣,唯一不同的是,首次產生的執行計畫不受參數影響。
如下兩個預存程序,剛建立完預存程序後,不管誰先執行,查詢計劃都是一樣的!
EXEC P_Test2 @ProductID = 870
EXEC P_Test2 @ProductID = 897


這裡就真正用到了所謂的“參數嗅探”!以為最佳化引擎首次確定查詢計劃時,並不知道執行的參數值是什麼。
因此只嗅探到傳遞的參數,系統就是根據參數確定了預存程序的查詢計劃。
這裡也有不好的一點,就是參數返回多少也可能影響到效能。


---------------------------------------------------------------------
---------------------------------------------------------------------

總結: 

以上幾種都有優缺點,最不好的就是【測試二】那種。
還有一個現象,就是上面的所有測試,個人在效能監控器中都沒有發現“重編譯”的情況,每次都只有“編譯”。
雖然編譯包括重編譯,但是重編譯都沒出現過一次。除非顯示讓語句重編譯(如 option(recompile))才出現。

測試一:最佳,每次都會產生新的計畫快取

測試二:不好,同樣緩衝計劃,返回結果集較大時效能不一樣

測試三:省緩衝,隨著資料量增長,預存程序最好重新編譯

測試四:省緩衝,查詢計劃固定,更改不了。


最後總體測試對錶以上這四種情況:
資料較多,不了,總結如下:


ProductID 查詢類型 格式 總邏輯讀 CPU 記憶體 時間 每次編譯 緩衝大小 查詢開銷
870(4688行) 即時查詢 where P.ProductID =870 1305 20 952 20 56 KB 26%
870(4688行) 即時參數查詢 where P.ProductID [email protected] 1305 16 1016 16 56 KB 24%
870(4688行) 預存程序 proc :@ProductID = 870 1305 17 928 17 56 KB 26%
870(4688行) 預存程序內聲明 proc :where P.ProductID [email protected] 1305 18 984 18 56 KB 24%
897(2行) 即時查詢 where P.ProductID =897 20 10 792 10 48 KB 13%
897(2行) 即時參數查詢 where P.ProductID [email protected] 1305 17 1016 17 56 KB 37%
897(2行) 預存程序 proc :P_Test @ProductID = 897 20 8 760 8 56 KB 13%
897(2行) 預存程序內聲明 proc :where P.ProductID [email protected] 1305 18 984 18 56 KB 37%









SQL Server 查詢最佳化(測試02)參數嗅探-執行計畫選擇

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.