關於參數化暫時有發現有兩個作用:
- 防止SQL注入.
- 執行計划的重用
這里我主要講的是執行計划的重用這塊.
我們項目中寫的SQL在SQL SERVER中都會先生成一個唯一的hash值,然后根據這個hash值去緩存里面匹配對應的執行計划.
如果執行計划.不存在,則開始走生成執行計划的模塊(注:這個模塊是很耗效能的,以至於連微軟都要把這個執行計划緩存)
所以我們在寫SQL的過程中,如果有一個字符發生變動(即使是一個空格),都會導致hash的變動,進而會重新編譯該SQL對應的執行計划.
例如:
上面這個圖兩者的sql作用是一樣的,但是因為第1個SQL多了個空格,導致SQL Server重新編譯了該sql語句
而我們日常開發中,有關客戶端傳遞過來的值;,我們大部分都會用參數化,而項目里面直接寫的,可能就會直接聲明了, 這樣就會導致下面這種情況→_→
直接聲明:
在換個其他值測試:
再次執行,且觀察執行計划可得知
可以得知我們手寫的SQL是執行的SQL自動生成的執行計划,而非我們自己手寫的執行計划。而使用次數作用到了手寫的SQL上面。
另一種方式,我們改用參數化:
換其他值測試:
上述的測試中:一個簡單的非參數化的SQL就已經占用了16KB的內存,如果是多表聯合查詢或者復雜性的查詢,會造成多少內存的浪費,因為原本用一個執行計划就搞定的,現在卻多出了N個。
如果我們項目中大量的SQL都將固定的值寫在sql里面,我們認為會只用一個SQL執行計划,實際上是SQL Server將我們的每個sql都編譯了一個執行計划,在一定程度上增加了對性能和內存的消耗。
所以我們項目中如果是固定的值,也要提取到參數化里面,這樣可以共享一個執行計划,節省性能以及內存的消耗。
-------------華麗的分界線------------------
擴展: 上述的測試中,用參數化執行SQL的方式,我遇到過這種情況, 即如下:
使用了參數化卻還是生成了兩個執行計划,通過分析可發現是因為SQL Parameter沒有傳入長度所致, 調整輸入長度后 測試如下:
進一步可得知,如果參數化的時候不指定字段長度的話(我這里指定30是因為Address表中的City字段類型是:nvarchar(30)),也會在一定程度上造成多余執行計划的增長,進而造成內存的浪費。
結語:
當完全參數化的可能還會出現參數嗅探的問題,所以在使用的時候,還得具體情況具體分析。
目前有個問題想請教下,就是當非參數化的時候,SQL SERVER自動生成的執行計划是做什么用的?
>>這個部分感謝答復我問題的@MSSQL123大神,他解答了我對這個部分的疑惑,非常感謝:)
=================結束的分割線=====================
上面主要用的SQL如下:
清空執行計划:
DBCC FreeProcCache
查詢上面結果的SQL:
SELECT
cp.usecounts as '使用次數' ,
cp.cacheobjtype as '緩存類型',
cp.objtype as '對象類型',
st.text as 'TSQL',
qp.query_plan as '執行計划',
cp.size_in_bytes as '執行計划占用空間(byte)'
FROM
sys.dm_exec_cached_plans cp
cross apply sys.dm_exec_sql_text(plan_handle) st
cross apply sys.dm_exec_query_plan(plan_handle) qp