原文:
阻塞與死鎖(二)——各種操作對鎖的申請
在著手分析、處理阻塞、死鎖之前,首先要進行「監控」和「資訊收集」
可以使用sp_lock來檢視所有連線持有的鎖的內容。
在2005以後引入的dmv,還能用過sys.dm_tran_locks來檢視:
select request_session_id,
resource_type ,
resource_associated_entity_id ,
request_status ,
request_mode ,
resource_description
from sys.dm_tran_locks
也可以組合其他dmv檢視更詳細的資訊:
select request_session_id,
resource_type ,
resource_associated_entity_id ,
request_status ,
request_mode ,
resource_description ,
p.object_id ,
object_name(p.object_id) as object_name ,
p.*from sys.dm_tran_locks a
left join sys.partitions p on a.resource_associated_entity_id = p.hobt_id
where resource_database_id= db_id('資料庫名')
order by request_session_id,resource_type ,
resource_associated_entity_id
由於有很多鎖是在語句執行過程中申請和釋放的。執行之後鎖會消失,所以使用上面方式很難查詢。此時使用sql server profiler 來跟蹤是比較好的方式:
開啟sql server profiler→定義乙個跟蹤(trace),選取以下的events(事件):lock:accquired,lock:released
由於實際過程中申請鎖的情況可能會非常複雜,所以建議只在測試環境針對特定語句進行。
一般來說,使用dmv和sp_lock基本上能解決大部分問題。
乙個常見的select動作要申請的鎖:
(1) 在連線a中,將事務隔離級別設成【可重複讀】(repeatable read)
(2) 在執行查詢前開啟乙個事務
(3) 執行查詢語句,但不提交
(4) 在第二個連線裡,查詢sys.dm_tran_locks中分析查詢結束以後連線a還持有的鎖。
乙個常見的update動作要申請的鎖:
對於乙個update操作,可以理解為先查詢再修改。查詢的過程先要新增s鎖,找到資料後再新增u鎖。最後才把u鎖公升級到x鎖。
如果update操作借助了哪個索引,就會在這個索引的鍵值上有u鎖。沒有用到的索引不加鎖。真正修改的地方會有x鎖。對於查詢涉及的頁面。sql server加了iu鎖。修改發生的頁面,加了ix鎖。
總結:(1) 對於每個使用到的索引,sql server會對上面的鍵值加u鎖。
(2) sql server只對要修改的記錄或鍵加x鎖。
(3) 使用到要修改的列的索引越多,鎖的數目也會越多。
(4) 掃瞄的頁面越多,意向鎖就越多。掃瞄過程中,所有掃瞄到的記錄也會加鎖。哪怕沒有修改。
對此,要在update過程中降低阻塞的機率,可以做以下方面:
(1) 盡量修改少的記錄集。
(2) 減少無謂的索引。
(3) 嚴格避免表掃瞄的發生。如果只需要修改表的一小部分,要盡量使用index seek,避免全表掃瞄這種執行計畫。
乙個常見的delete動作要申請的鎖:
delete的時候也和update一樣,需要先找出要更改的資料,然後再進行操作。
(1) delete 的過程先找到符合條件的記錄,然後做刪除。可以理解為先是乙個select ,然後乙個delete。所以,如果有合適的索引,第一步申請的鎖會比較少。
(2) delete 不但把資料行本身刪除,還要刪除所有相關的索引鍵,所以一張表上的索引數目越多,鎖的數目就會越多,越容易發生阻塞。
為了防止阻塞,我們既不能絕對地不建索引,也不能隨隨便便建很多索引。對於沒有用的索引,去掉比較好。
乙個常見的insert動作要申請的鎖:
sql server會對新插入的資料本身申請乙個x鎖。在發生變化的頁面申請乙個ix鎖。由於是新插入的資料,被引用的概率相對小一些,所以發生阻塞的機率也很小。
小結:資料庫開發者和dba想要影響sql server鎖的申請和釋放行為,以緩解阻塞或死鎖的問題,需要考慮以下因素:
1、 事務隔離級別的選定:級別越高,隔離度越高,併發也越低。越高的級別sql server會不可避免地申請更多的鎖。設計應用時,要和使用者談好,盡量選擇預設的隔離級別(read committed)。
2、 事務的長短和複雜度:決定了事務在sql server內部會持續多長時間,也決定了同時在多少張表和索引上申請和持有鎖。避免在乙個事務裡面做很多事情。
3、 從應用整體併發度考慮,但是事務一次處理的資料量不能過多:如果乙個應用的併發要求比較高,就一定要嚴格控制單個事務處理的資料量。如果有什麼事務操作需要訪問或修改表內大量資料,最好調整到併發使用者比較少的時候執行。
4、 針對語句在**上設計合適的索引:如果沒有合適的索引,在做select /update/delete 的時候,申請多得多的鎖。對這種情況,可以通過加索引提高併發性。但是,索引越多,申請的鎖數目也會越多。對於設計人員,要確保有足夠的索引,防止語句做全表掃瞄。但也要去掉對語句執行貢獻不大的索引,不能隨便往**上加索引。
在預設設定下,乙個讀操作會和乙個寫操作相互阻塞。在未提交讀,雖然不會,但是讀操作可能讀到髒資料。大部分使用者是不能接受的。
從2005以後,引入了行版本控制機制。好處是程式併發性比較高但是使用者讀取資料時雖然不是髒資料但是可能是乙個正在被修改馬上就要過期的資料值,容易產生邏輯錯誤。
sqlserver 有兩種行版本控制:
使用行版本的已提交隔離(read_committed_snapshot)和直接使用snapshot事務隔離級別。
l read_committed_snapshot資料庫選項為on時,read_committed事務通過使用行版本控制提供語句級讀取一致性。
l allow_snapshot_isolation資料庫選項為on時,snapshot事務通過使用行版本控制提供事務級讀取一致性。
可以使用alter database *** set read_committed_snapshot/ on;來開啟。
注意:行版本控制並不是消除阻塞和死鎖的萬靈藥。必須考慮兩個問題:
1、 終端使用者是否接受行版本控制下的執行結果?
2、 sql server 是否能支援行版本控制帶來的額外負荷?因為行版本放在tempdb,對sql server會造成額外的負載。
阻塞與死鎖(二) 各種操作對鎖的申請
在著手分析 處理阻塞 死鎖之前,首先要進行 監控 和 資訊收集 可以使用sp lock來檢視所有連線持有的鎖的內容。在2005以後引入的dmv,還能用過sys.dm tran locks來檢視 select request session id,resource type resource asso...
阻塞與死鎖(二) 各種操作對鎖的申請
原文 阻塞與死鎖 二 各種操作對鎖的申請 在著手分析 處理阻塞 死鎖之前,首先要進行 監控 和 資訊收集 可以使用sp lock來檢視所有連線持有的鎖的內容。在2005以後引入的dmv,還能用過sys.dm tran locks來檢視 select request session id,resour...
阻塞與死鎖(三) 死鎖的定位及解決方法
在sql server的兩個或多個任務中,如果某個任務鎖定了其他任務試圖鎖定的資源。會造成這些任務的永久阻塞,從而出現死鎖。下圖為例 l 事務t1獲得了行r1的共享鎖。l 事務t2獲得了行r2的共享鎖。l 然後事務t1請求行r2的排它鎖,但是t2完成並釋放其對r2的共享鎖之前被阻塞。l t2請求行r...