SQL Server里強制參數化的痛苦


幾天前,我寫了篇SQL Server里簡單參數化的痛苦。今天我想繼續這個話題,談下SQL Server里強制參數化(Forced Parameterization)

強制參數化(Forced Parameterization

在SQL Server里簡單參數化有很多限制,如果你的SQL語句包含下列任意它不會發生: 

  • JOIN
  • IN
  • BULK INSERT
  • UNION
  • INTO
  • DISTINCT
  • TOP
  • GROUP BY
  • HAVING
  • COMPUTE
  • Sub Queries

如果你還想讓SQL Server進行自動參數化,你可以啟用在數據庫層啟用強制參數化:

1 -- Let's now activate Forced Parameterization on the AdventureWorks2012 database
2 ALTER DATABASE AdventureWorks2012 SET PARAMETERIZATION FORCED
3 GO

在這個情況下,SQL Server總會自動參數化你的SQL語句除掉以下情況:

  • INSERT … EXECUTE
  • Prepared SQL Statements
  • RECOMPILE
  • COMPUTE 

為什么強制參數化總不是個好選擇

現在讓我們看下SQL Server里的強制參數化。對於AdventureWorks2012數據庫,最后的代碼已經將它啟用強制參數化了。下一步我創建Sales.SalesOrderHeader表的副本,並將數據修正,這樣的話在CustomerID列我們會有非線性的數據分布。另外我在那一列也創建了非聚集索引。 

 1 -- Create a copy from the Sales.SalesOrderHeader table
 2 SELECT * INTO Sales.SalesOrderHeader2 FROM Sales.SalesOrderHeader
 3 GO
 4  
 5 -- Create a Non-Clustered Index on the CustomerID column
 6 CREATE NONCLUSTERED INDEX idx_CustomerID ON Sales.SalesOrderHeader2(CustomerID)
 7 GO
 8 
 9 -- "Patch" the data in some way, so that the content of the column "CustomerID" is not evenly distributed across the whole table
10 UPDATE Sales.SalesOrderHeader2
11 SET CustomerID = 29675
12 WHERE SalesOrderID < 60000
13 GO

從下圖你可以看到:ID為29675的客戶有大量的訂單,其它客戶只有一些訂單:

1 SELECT CustomerID,COUNT(SalesOrderID) SaleCount FROM Sales.SalesOrderHeader2
2 GROUP BY CustomerID ORDER BY 2 DESC

 在下一步里我執行一個返回CustomerID為22943的所有記錄——只有3條記錄。因為查詢在臨界點前,SQL Server選擇了有書簽查找的執行計划。查詢合計生成了3個邏輯讀。因為我們對AdventureWorks2012數據庫啟用了強制參數化,對這個語句SQL Server也會自動參數化,因此執行計划會被后續的查詢重用。 

1 -- 3 Logical Reads
2 SELECT * FROM Sales.SalesOrderHeader2
3 WHERE CustomerID = 22943 -- Index Seek, returns 1 record
4 GO

我們再來運行另一個查詢,返回所有CustomerID為29675的記錄。在這個情況下查詢返回16343條記錄。當你再次看執行計划時,你會看到查詢重用了剛才查詢的執行計划。

1 SELECT * FROM Sales.SalesOrderHeader2
2 WHERE CustomerID = 29675 
3 GO

這是對的,以為查詢自動參數化,SQL Server在計划緩存里找已經緩存的計划。但是重用執行計划並不安全,因為現在我們進行了書簽查找16343次——對每一行——反復執行。查詢合計生成了16415個邏輯讀。使用表掃描的話只要780個邏輯讀。

這是強制參數化的副作用。SQL Server不管你執行計划的穩定性。SQL Server值自動參數化你的SQL語句,並反復重用緩存的執行計划。不管這個執行計划有糟糕。因為這是你強制SQL Server只要做的!沒有啟用強制參數化,SQL Server從不為你自動參數化SQL語句,因為那不安全。

性能問題的根源肯定是強制參數化。這里的根源是你的執行計划包含書簽查找。因為書簽查找你就沒有計划穩定性。計划沒有穩定性是說基於你輸入參數值你會有不同的執行計划。在這個例子里有時你得到書簽查找(臨界點前),有時是表掃描(臨界點后)。

在這個情況下,如果你修改下你的索引設計,為這個查詢定義一個覆蓋非聚集索引,性能問題也會消失。這樣的話也不需要啟用強制參數化,因為使用計划穩定性SQL Server會自動參數化你的SQL語句! 

小結

在數據庫級別啟用強制參數化是個非常危險的事。不管你是否有計划穩定性,SQL Server總會自動參數化你的SQL語句,並反復重用你的執行計划。因此你要知道你的執行計划的詳細情況,看看它們是否會引起性能相關的問題。

感謝關注!

參考文章:

https://www.sqlpassion.at/archive/2015/07/27/the-pain-of-forced-parameterization-in-sql-server/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM