搜尋本站文章

2017-11-15

[Performance Tuning] Use SqlParameter, specified SqlDbType and length - Avoid Plan Cache Pollution


使用 SqlParameter - Add(String, SqlDbType, Int32) ,請記得指定 資料長度,可以提升效能
  1. 以效能角度來分析,明確指定 資料類型、資料長度,.NET 程式無須額外判斷,節省資源。
  2. 讓 SQL Server 能使用 Parameterization(參數化),重複使用 Execution Plan 的作法, 有效使用系統資源。
  3. 避免發生 Plan Cache Pollution。

.NET 程式使用 4 種不同的方式,以 For 迴圈執行 1,001 次的 SQL 陳述式,彙整如下:

Method Reuse Execution Plan Cache Type Plan Cache(MB) Execution Time(sec)
Hard-Coded SQL No Adhoc 7.8203125 2.5005660
AddWithValue() Partial Prepared 0.28125 0.1603226
Add(String, SqlDbType) - without length Partial Prepared 0.28125 0.1419786
Add(String, SqlDbType, Int32) Yes Prepared 0.070312 0.1399672



SqlParameter - Add(String, SqlDbType, Int32) Method








Demo -
Avoid Plan Cache Pollution - Use SqlParameter, given the specified SqlDbType and size.

示範環境

  • SQL Server 2017 Enterprise Edition
  • VS 2015

A. Hard-Coded SQL Statements

01. Hard-Coded SQL Statements(硬式編碼的 SQL 陳述式) 

以下示範 .NET 應用程式,使用 Hard-Coded SQL 陳述式的方式組合傳入參數值。

  • 使用 For 迴圈執行 SQL 陳述式 1,001 次,每次執行並傳入一個 Hard-Coded 的參數值。
  • 使用 Hard-Coded SQL 傳入參數值 的方式,將使用每一句 SQL 陳述式的 hash value 是不同的,導致 SQL Server 無法重複使用 Execution Plan,必須額外建立各自的 Execution Plan。
  • 在 Plan Cache 裡將存放了 1,001 份不同的 Execution Plan,但其內容卻幾乎相同,而且這是應能重複使用,卻因 hash value 不同而無法重複使用的 Execution Plan。

-- 011_Hard-Coded SQL



02. 觀察 Plan Cache: 各項物件的記憶體使用情況、使用次數等

以本範例來看,使用 Hard-Coded SQL 傳入參數值的作法,產生的副作用是
  • 1,001 份判斷為無法重複使用 Execution Plan,其 Cache Object 都是 Compiled Plan,個別都使用了 8 KB。
  • 每一份 Execution Plan 耗用 8 KB,CacheType 是 Adhoc1,001 份約耗費 7.8203125 MB 的 Plan Cache。

-- 012_View_cache_execution_plan_use_count_total_size




B. SqlParameter - AddWithValue() Method

01. 使用 SqlParameter - AddWithValue() 參數化查詢

以下示範 .NET 應用程式,使用 SqlParameter - AddWithValue() 參數化查詢。

  • 使用 For 迴圈執行 SQL 陳述式 1,001 次,每次執行並傳入一段由 AddWithValue() 所組建的 SQL 陳述式。
  • 使用 AddWithValue(),耗用 .NET 資源去判斷 資料類型、長度等。
  • SQL Server 在編譯由 AddWithValue() 所組建的 SQL 陳述式時,並未能完全有效重複使用。

-- 021_AddWithValue



02. 觀察 Plan Cache: 各項物件的記憶體使用情況、使用次數等

以本範例來看,使用 SqlParameter - AddWithValue() 參數化查詢的作法,SQL Server 已能 初步 採用 Parameterization(參數化) 方式來執行。

優點:
  • 增加關聯式引擎將新的 SQL 陳述式 與 先前編譯之現有 Execution Plan 配對的能力。
  • 重複使用先前 SQL 陳述式 所編譯的 Execution Plan。

整理 Parameterization(參數化) 執行的方式

Parameter Value Parameter Data Types Use Counts Obj Type
1~9 @ctn1 nvarchar(1) 9 Prepared
10~99 @ctn1 nvarchar(2) 90 Prepared
100~999 @ctn1 nvarchar(3) 900 Prepared
1000~1001 @ctn1 nvarchar(4) 2 Prepared

缺點:

  • 執行 1,001 次,SQL Server 依據傳入的資料類型、資料長度,分類為 4 種:nvarchar(1), nvarchar(2), nvarchar(3), nvarchar(4),編譯為 4 種不同的 Execution Plan。
  • 這 1,001 份 SQL 陳述式,分類為這 4 種參數,並且 初步 能重複使用這些先前 所編譯的 Execution Plan。
  • 每一份 Execution Plan 耗用 72 KB,CacheType 是 Prepared4 份約耗費 0.28125 MB 的 Plan Cache。Avg Use Count 是 250 次。

-- 022_View_cache_execution_plan_use_count_total_size




C. SqlParameter - Add(String, SqlDbType) - without length

01. 使用 SqlParameter - Add(String, SqlDbType) 參數化查詢,但 不 指定資料長度

以下示範 .NET 應用程式,使用 SqlParameter - Add(String, SqlDbType) 參數化查詢。

  • 使用 For 迴圈執行 SQL 陳述式 1,001 次,每次執行並傳入一段由 Add(String, SqlDbType) 所組建的 SQL 陳述式。
  • 使用 Add(String, SqlDbType),耗用 .NET 資源去判斷 資料長度等。
  • SQL Server 在編譯由 Add(String, SqlDbType) 所組建的 SQL 陳述式時,並未能 完全有效重複使用。

-- 031_Add_SqlDbType


02. 觀察 Plan Cache: 各項物件的記憶體使用情況、使用次數等

以本範例來看,使用 SqlParameter - Add(String, SqlDbType) 參數化查詢的作法,SQL Server 已能 初步 採用 Parameterization(參數化) 方式來執行。

在 Plan Cache 的使用上,與 B. AddWithValue() Method 相同。

-- 032_View_cache_execution_plan_use_count_total_size




D. SqlParameter - Add(String, SqlDbType, Int32)

01. 使用 SqlParameter - Add(String, SqlDbType, Int32) 參數化查詢,明確指定 資料長度

以下示範 .NET 應用程式,使用 SqlParameter - Add(String, SqlDbType, Int32) 參數化查詢。

  • 使用 For 迴圈執行 SQL 陳述式 1,001 次,每次執行並傳入一段由 Add(String, SqlDbType, Int32) 所組建的 SQL 陳述式。
  • 使用 Add(String, SqlDbType, Int32),已事先指定 資料類型、資料長度無須耗用  .NET 資源去判斷。
  • SQL Server 在編譯由 Add(String, SqlDbType, Int32) 所組建的 SQL 陳述式時,能有效使用 Parameterization(參數化) 來重複執行。


-- 041_Add_SqlDbType_Int32


02. 觀察 Plan Cache: 各項物件的記憶體使用情況、使用次數等

以本範例來看,使用 SqlParameter - Add(String, SqlDbType, Int32) 參數化查詢 的作法,SQL Server 能有效採用 Parameterization(參數化) 方式來執行。

優點:
  • 增加關聯式引擎將新的 SQL 陳述式 與 先前編譯之現有 Execution Plan 配對的能力。
  • 重複使用先前 SQL 陳述式 所編譯的 Execution Plan。

整理 Parameterization(參數化) 執行的方式

Parameter ValueParameter Data TypesUse CountsObj Type
1~1001@ctn1 nvarchar(25)1001Prepared

分析
  • 執行 1,001 次,由於 .NET 已事先定義好 資料類型、資料長度是 nvarchar(25),SQL Server 僅需編譯 1 份 Execution Plan。
  • 這 1,001 份 SQL 陳述式,能重複使用先前 所編譯的 Execution Plan。
  • 這 1 份 Execution Plan 耗用 72 KB,CacheType 是 Prepared,這 1 份約耗費 0.070312 MB 的 Plan Cache。Avg Use Count 是 1,001 次。

-- 042_View_cache_execution_plan_use_count_total_size




彙總分析

使用 SqlParameter - Add(String, SqlDbType, Int32) ,請記得指定 資料長度,可以 提升效能
  1. 以效能角度來分析,明確指定 資料類型、資料長度,.NET 無須額外判斷,節省資源。
  2. 讓 SQL Server 能使用 Parameterization(參數化),重複使用 Execution Plan 的作法, 有效使用系統資源。
  3. 避免發生 Plan Cache Pollution。

.NET 程式使用 4 種不同的方式,以 For 迴圈執行 1,001 次的 SQL 陳述式,彙整如下:

Method Reuse Execution Plan Cache Type Plan Cache(MB) Execution Time(sec)
Hard-Coded SQL No Adhoc 7.8203125 2.5005660
AddWithValue() Partial Prepared 0.28125 0.1603226
Add(String, SqlDbType) - without length Partial Prepared 0.28125 0.1419786
Add(String, SqlDbType, Int32) Yes Prepared 0.070312 0.1399672


-- 101_Application_Summary





SSMS 檢視資料表的資料類型、長度

-- 111_SSMS_Table_Data_Type_Size







範例程式碼

20171114_Avoid Plan Cache Pollution

https://drive.google.com/drive/folders/1h_f32oYgR8ui53hMSz9tUUsd1iql-IdD?usp=sharing





參考資料

Plan cache pollution or how important it is to properly define parameters in code
https://blogs.msdn.microsoft.com/ivandonev/plan-cache-pollution-or-how-important-it-is-to-properly-define-parameters-in-code/

SqlParameterCollection.Add Method
https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlparametercollection.add.aspx

SqlParameterCollection.AddWithValue Method (String, Object)
https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlparametercollection.addwithvalue(v=vs.110).aspx

Plan Caching and Recompilation in SQL Server 2012
https://msdn.microsoft.com/en-us/library/dn148262.aspx

Simple Parameterization
https://technet.microsoft.com/en-us/library/ms186219(v=sql.105).aspx

[SQL Server] Plan Cache concepts - 認識 Plan Cache
http://sharedderrick.blogspot.tw/2017/11/sql-server-plan-cache-concepts-plan.html

[SQL Server] Enable optimize for ad hoc workloads , 針對特定工作負載最佳化
http://sharedderrick.blogspot.tw/2017/11/sql-server-enable-optimize-for-ad-hoc.html

[SQL Server] Ad hoc Query and Reuse - 重複使用
http://sharedderrick.blogspot.tw/2017/11/sql-server-ad-hoc-query-and-reuse.html

[SQL Server] Plan Cache Pollution - Hard-Coded SQL
http://sharedderrick.blogspot.tw/2017/11/sql-server-plan-cache-pollution-hard.html

2017-11-12

[SQL Server] Plan Cache Pollution - Hard-Coded SQL


延續前一篇  [SQL Server] Ad hoc Query and Reuse - 重複使用


Plan Cache Pollution

這是指 應用程式 執行 SQL 陳述式的方式,有可能因故讓 SQL Server 無法重複使用 Execution Plan,耗費大量的 Plan Cache 資源,污染了 Plan Cache。

本文討論 應用程式 採用 Hard-Coded SQL 陳述式,導致產生 Plan Cache Pollution。





觀察 Plan Cache 各項物件的記憶體使用情況、使用次數




-- View each plan cache type, Use Counts, Memory used
SELECT objtype AS [CacheType], COUNT_BIG(*) AS [Total Plans], 
 SUM(CAST(size_in_bytes AS DECIMAL(18, 2))) / 1024 / 1024 AS [Total MBs],
 AVG(usecounts) AS [Avg Use Count],
 SUM(CAST((
  CASE WHEN usecounts = 1 THEN size_in_bytes ELSE 0
  END) AS DECIMAL(18, 2))) / 1024 / 1024 AS [Total MBs – USE Count 1],
 SUM(
  CASE WHEN usecounts = 1 THEN 1 ELSE 0
        END) AS [Total Plans – USE Count 1]
FROM sys.dm_exec_cached_plans
GROUP BY objtype
ORDER BY [Total MBs – USE Count 1] DESC



Demo
Plan Cache Pollution - Hard-Coded SQL 

01. Hard-Coded SQL Statements(硬式編碼的 SQL 陳述式) 

以下示範 .NET 應用程式,使用 Hard-Coded SQL 陳述式的方式組合傳入參數值。
  • 使用 For 迴圈執行 SQL 陳述式 20,000 次,每次執行並傳入一個 Hard-Coded 的參數值。
  • 使用 Hard-Coded SQL 傳入參數值 的方式,將使用每一句 SQL 陳述式的 hash value 是不同的,導致 SQL Server 無法重複使用 Execution Plan,必須額外建立各自的 Execution Plan。

以本範例來看,使用 Hard-Coded SQL 傳入參數值的作法,產生的副作用是
  • 在 Plan Cache 裡將存放了 20,000 份不同的 Execution Plan,但其內容卻幾乎相同,而且這是應能重複使用,卻因 hash value 不同而無法重複使用的 Execution Plan。

-- 101_Hard-Coded SQL Statement



02. 觀察 Plan Cache
  • 20,000 份判斷為無法重複使用 Execution Plan,其 Cache Object 都是Compiled Plan,個別都使用了 16 KB

-- 102_View each cached query execution plan



03. 觀察 Plan Cache 各項物件的記憶體使用情況、使用次數等
  • 每一份 Execution Plan 耗用 16 KB,20,000份 約耗費 320 MB 的 Plan Cache。

-- 103_View each plan cache type, Use Counts, total size





01. 啟用「optimize for ad hoc workloads」

  • 無須重新啟動 SQL Server。
  • 將 optimize for ad hoc workloads 設定為 1 只會影響新的計畫。
  • 已經存在計畫快取中的計畫則不會受到影響。



02. 觀察 Plan Cache
  • 20,000 份判斷為無法重複使用 Execution Plan,其 Cache Object 都是 「compiled plan stub(已編譯計畫虛設常式)」,個別僅使用 0.125 KB

-- 104_View each cached query execution plan


03. 觀察 Plan Cache 各項物件的記憶體使用情況、使用次數等

  • 每一份 Execution Plan 耗用 0.125 KB,20,000份 約 僅 使用 2.5 MB 的 Plan Cache。

-- 105_View each plan cache type, Use Counts, total size





彙總分析


觀察 optimize for ad hoc workloads 選項的影響

  • 未啟用,耗用 320 MB
  • 啟用後,僅耗用 2.5 MB

與未啟用「optimize for ad hoc workloads 」比較起來:

  • 320 MB 對比 2.5 MB,兩者差距:128 倍。



解決方案

因為 Plan Cache Pollution 導致浪費 Plan Cache,可用的解決方案有:

  • 啟用 Optimize for Ad hoc Workload
  • FORCED PARAMETERIZATION
  • 使用 Parameters,例如:SQLCommand.Parameters
  • 改為 Stored Procedure
  • DBCC FREESYSTEMCACHE



範例程式碼

20171112_Plan Cache Pollution

https://drive.google.com/drive/folders/12De4e2CDvkijaAb9KDfioBOC0RgF_GxY?usp=sharing





參考資料

Plan Caching and Recompilation in SQL Server 2012
https://msdn.microsoft.com/en-us/library/dn148262.aspx

Plan cache pollution or how important it is to properly define parameters in code
https://blogs.msdn.microsoft.com/ivandonev/plan-cache-pollution-or-how-important-it-is-to-properly-define-parameters-in-code/

How to Create and Execute SqlCommand in ADO.NET
https://code.msdn.microsoft.com/windowsdesktop/How-to-Create-and-Execute-86922261#content

SqlParameter Class
https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlparameter(v=vs.110).aspx

Hard-Coded SQL Statements
https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/hard-coded-sql-statements

[SQL Server] Plan Cache concepts - 認識 Plan Cache
http://sharedderrick.blogspot.tw/2017/11/sql-server-plan-cache-concepts-plan.html

[SQL Server] Enable optimize for ad hoc workloads , 針對特定工作負載最佳化
http://sharedderrick.blogspot.tw/2017/11/sql-server-enable-optimize-for-ad-hoc.html

[SQL Server] Ad hoc Query and Reuse - 重複使用
http://sharedderrick.blogspot.tw/2017/11/sql-server-ad-hoc-query-and-reuse.html