SQL SERVER 查詢性能優化——分析事務與鎖(一)html
SQL SERVER 查詢性能優化——分析事務與鎖(二)sql
SQL SERVER 查詢性能優化——分析事務與鎖(三)數據庫
上接SQL SERVER 查詢性能優化——分析事務與鎖(四)緩存
(四)未檢測到的分佈式死鎖性能優化
某應用程序持有數據庫資源,開啓事務以後又與用戶交互,而在與用戶的交互過程當中出現了錯誤,致使數據庫資源遲遲不能釋放。SQL SERVER 2005/2008 動態管理視圖sys.dm_exec_requests提供相關信息,該SESSION_ID的status字段值爲「sleeping」,wait_type爲「NULL」值。若是是SQL 2005則能夠經過Microsoft SQL Server Management Studio管理工具中的「活動監視器--》進程信息」視圖,該進程的「開啓事務字段」顯示非「0」值。以下圖。session
在SQL 2005(2008)中執行代碼,即SQL SERVER 查詢性能優化——分析事務與鎖(二)中的「例一」,也就是下面的代碼,獲得以下圖。併發
select spid 進程,STATUS 狀態, 登陸賬號=SUBSTRING(SUSER_SNAME(sid),1,30) ,用戶機器名稱=SUBSTRING(hostname,1,12) ,是否被鎖住=convert(char(3),blocked) ,數據庫名稱=SUBSTRING(db_name(dbid),1,20),cmd 命令,waittype as 等待類型 ,last_batch 最後批處理時間,open_tran 未提交事務的數量 from master.sys.sysprocesses --列出鎖住別人(在別的進程中blocked字段中出現的值)但本身未被鎖住(blocked=0) Where spid in (select blocked from master.sys.sysprocesses) and blocked=0
因爲應用程序持有事務,並且應用程序出錯以後,沒對事務的相應處理,也沒有須要等待的資源,但持有事務,與前一種(三)狀況相似,但經過SQL PROFILER工具進行跟蹤,卻沒法發現任何錯誤事件。分佈式
建議解決方式高併發
應用程序所形成的分佈式死鎖,很難加以跟蹤分析,須要程序開發人員自行記錄該應用程序的行爲,比較多用戶狀況下,在進行哪些工做以後,系統就遲滯沒法正常執行下去。這須要程序開發人員保持良好的開發習慣:事務越晚開啓越好,使用資源越少越好,一旦開啓了事務早晚關閉,事務執行過程當中不要與用戶有任何交互,要輸入的參數或內容應該在開啓事務以前就應該輸入完畢,對相關數據的各類校驗也要在開啓事務以前進行校驗,事務應該只是在往數據庫中插入更新數據時開啓,插入更新完畢以後,就當即關閉。工具
(五)鎖定數據粒度過低或過高
用戶設置不當的鎖定粒度時,若是設置事務一概使用Row lock或table lock都可能產生問題,或是當系統資源使用過分,也很容易產生被鎖定的情形。
建議解決方式
能夠經過SQL PROFILER 觀察「TextData」字段所呈現的SQL語句,觀察該應用程序是否設置了鎖定提示,若想要暫時中止鎖定提示形成的影響,能夠經過如下語句
Dbcc traceon(8755)
或以SQL SERVER 激活參數-T 8755 來中止鎖定提示功能,如有改善,能夠從新考慮從應用程序重新移除鎖定提示的可能性。
(六)Compile Blocking
此現象是因爲編譯存儲過程致使被鎖定,在master.sys.sysprocesses視圖中或sp_lock存儲過程當中觀察到的等待資源字段中的內容是「COMPILE」,或者使用SQL PROFILER 錄製過程當中出現大量的「SP:REComplie」事件。因爲從新編譯須要耗費CPU資源,因此,此種鎖定是在一長串的被鎖定鏈接中,單一鎖定者鎖定時間不長,但整個連接各點都有一點耗時,因此在連接尾端的被鎖定者須要等待較長時間。同時會出現CPU的使用率比較高。
當存儲過程當中使用了緩存數據表,而該緩存數據表還須要設置結構,如須要要設置主鍵或者利用緩存數據表開打開遊標,則每次調用該存儲過程進,都會要求從新編譯。或這個存儲過程是當應用程序執行時,經常會被調用的熱門存儲過程,就會出現Compile Blocking的情況出現。
但存儲過程第一次使用時,也會須要編譯,因此不要一看到是在等待編譯,就識覺得是COMPILE Blocking現象。
建議解決方式
使用sp_executesql執行語句,即便用sp_executesql執行SQL語句,SQL語句不會編譯爲存儲過程執行計劃的一部分,所以在執行該類語句時,SQL SERVER 會自由的使用高速緩存中的現有語句計劃,或者在執行階段創建新的執行計劃,無論任何一種狀況,調用存儲過程的計劃都不會受影響,也沒必要進行從新編譯。
EXECUTE語句也有相同的效果,但不建議你使用。由於使用EXECUTE沒有使用SP_EXECUTESQL語句的效率高,由於前者不容許查詢參數化。
3、基本原則:
1. 事務不能夠跨批處理,語句越短越好,事務期間不要與用戶進行交互
2. 當心處理逾時放棄,或者執行錯誤等狀況。
3. 正確創建索引。能夠參考本人前面的相關文章。
(如SQL Server 查詢性能優化——建立索引原則(一)
SQL Server 查詢性能優化——覆蓋索引(一)等系列文章)
4. 數據表最好有彙集索引,並且彙集索引的鍵值不要太大,由於全部的非彙集索引存儲的都是彙集索引的鍵值。不要使用常常須要進行更新的字段作爲彙集索引的鍵值,由於彙集索引一旦進行了變動,則全部的非彙集索引也要跟着進行變動,致使大量的鎖定。索引建少了,影響查詢效率,建多了,浪費維護的資源與下降新增、修改、刪除的效率,因此建好索引以後,要當心觀察SQL SERVER 使用索引的狀況,將多餘的索引刪除,對於數據密度大,或者查詢條件鑑別率過低的字段不要創建索引。
5. 儘可能不要激活Implicit Transaction,以避免它長時間的持有事務。
6. 儘可能下降事務隔離級別
7. 進行壓力測試以瞭解當大用戶量時,交互將形成何種程度的鎖定問題。
4、 防止與處理死鎖
1.儘可能避免或儘快處理鎖定,當鎖定與被鎖定過多時,就可能形成死鎖
2.訪問資源的順序要相同。例如鏈接A先訪問資源1,而後訪問資源2,而鏈接B的訪問順序與之相反,則可能發生死鎖。不要在開啓事務的狀況下,調用外部程序,容易形成分佈式死鎖。
3.讓不一樣的鏈接使用相同的鎖定。或兩條鏈接由於修改相同的資源而互相鎖定,若是你的系統對於更新數據的正確性不作強制性要求,能夠考慮使用sp_getbindtoken和sp_bindsession兩個系統存儲過程,讓鏈接共享鎖定,則兩條鏈接同時更新數據,也就可能形成數據更新遺失。
例:
use Test go create proc sp_upd_OPINION @OPINIONID varchar(20), @bindToken varchar(255) output as exec sp_getbindtoken @bindToken output update WBK_OPINION set OPINION_VALUE='true' where OPINION_ID=@OPINIONID go create proc sp_upd_OPINION2 @OPINIONID varchar(20), @bindSession varchar(255) output as exec sp_bindsession @bindSession update WBK_OPINION set OPINION_VALUE='False' where OPINION_ID=@OPINIONID go ----在第一個鏈接中執行 declare @bindToken varchar(255) begin tran exec sp_upd_opinion 'PreEntryIDUse',@bindToken output select * from WBK_OPINION select @@trancount --事務數量爲 select @bindToken
----在第二個鏈接中執行 ---其中@binToken是由第一個鏈接執行完畢以後,而獲取的 begin tran exec sp_upd_opinion2 'PreEntryIDUse',@bindToken select * from WBK_OPINION select @@trancount --事務數量爲
---在第三個鏈接中執行如下語句,因爲不在同一個事務以內,因此會被鎖定 update WBK_OPINION set OPINION_VALUE='true' where OPINION_ID='PreEntryIDUse' rollback tran ---回滾
1. 你能夠根據以上代碼,自行編碼相應的測試示例,經過Management studio分別使用三條鏈接來執行更新示例代碼,你會發現享有相同TOKEN的兩條鏈接會一同更新,而其獲取的@@TRANCOUNT系統變量也是同樣的。而不在同一事務中的其餘鏈接則會被鎖住。@@TRANCOUNT也與前述的事務無關。
2.提交不一樣的數據訪問路徑。若是兩條不一樣鏈接的SQL語句,由於搶相同索引而致使死鎖,能夠考慮爲不一樣的訪問語句創建不一樣的索引,經過索引提示強制讓兩條鏈接訪問各自的索引。或者是兩條不一樣的鏈接訪問相同的數據表,若是引用不一樣的索引,但各自的訪問順序正彼此交錯,造成死鎖,則可強制兩條鏈接使用相同的索引,以維護訪問前後秩序。
無論如何,採用此類解決方式時,都要考慮額外的性能損耗,由於你經過索引提示強制了索引訪問,讓查詢優化程序不能憑藉數據的特性使用最佳的索引。
5、發生死鎖後的處理
經過設置SET DEADLOCK_PRIORITY LOW,讓不重要的事務自動放棄,並在這些鏈接執行的業務邏輯中,加上針對死鎖的錯誤處理。
事實上,在很是複雜的高併發量的系統中,要徹底預防死鎖,或者要知道什麼樣的用戶在特殊的訪問次序中會發生死鎖,是很是困難的。因此應用程序應該對死鎖錯誤「1205」要有相應的處理,以完成原有的業務邏輯的處理或是善後清除處理。