SQL Server死鎖
- 多個事務之間互相等待對方的資源,導致這些事務永久等待
- 注意是永久等待,而非長事務
死鎖的4個條件
- 互斥條件(Mutual exclusion):資源不能被共享,只能由一個進程使用。
- 請求與保持條件(Hold and wait):已經得到資源的進程可以再次申請新的資源。
- 非剝奪條件(No pre-emption):已經分配的資源不能從相應的進程中被強制地剝奪。
- 循環等待條件(Circular wait):系統中若干進程組成環路,該環路中每個進程都在等待相鄰進程正占用的資源。
事務隔離級別
- READ UNCOMMITTED(SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
- READ COMMITTED(SET TRANSACTION ISOLATION LEVEL READ COMMITTED)
- REPEATABLE READ(SET TRANSACTION ISOLATION LEVEL REPEATABLE READ)
- SERIALIZABLE(SET TRANSACTION ISOLATION LEVEL SERIALIZABLE)
- SNAPSHOT(SET TRANSACTION ISOLATION LEVEL SNAPSHOT)
驗證
- 初始數據
- 默認隔離等級下,更新事務對事務外SELECT語句的影響
- 在sql server ssms中分別打開兩個連接,分別模擬事務以及普通SELECT
- 下面這個事務只有begin,沒有COMMIT或者ROLLBACK,目的是模擬並發時多個session同時運行效果
- 執行上面這個事務
- 接着在另外一個session中分別查詢下面這些select
-
/*example 1*/ SELECT * FROM [dbo].[customer] SELECT * FROM [dbo].[customer] WHERE id=1 SELECT * FROM [dbo].[customer] WHERE id=2 SELECT * FROM [dbo].[customer](NOLOCK) WHERE id=2
-
分別執行后的結果(結果里空白的為阻塞狀態)
- 然后在事務session中執行rollback命令
- 在普通SELECT session中運行下面語句
-
SELECT * FROM [dbo].[customer]
- 可以看到,這里又變成了aaa(在NOLOCK查詢時這個值為Changed)
- 結論
- 在默認隔離級別下(READ COMMITTED),對於UPDATE過的記錄,在事務范圍外默認會被阻塞住
- 同樣適用於REPEATABLE READ以及SERIALIZABLE,但不適用於READ UNCOMMITTED
- 同樣適用於DELETE/INSERT記錄
- 如果在事務外部進行查詢的查詢條件不牽涉到事務內的更新記錄,則外部查詢不會被阻塞
- 比如WHERE id=2會被阻塞
- 比如WHERE id=3不會被阻塞
- 比如WHERE id between 1 AND 3會被阻塞
- 其他線下證明
- 例子中是用了pkid來做where條件,但是用其他條件也同樣適用
- 例子中的pkid有index,但是沒有index的字段同樣也適用
- 在默認隔離級別下(READ COMMITTED),對於UPDATE過的記錄,在事務范圍外默認會被阻塞住
- SERIALIZABLE隔離等級下,對事務外的SELECT/UPDATE/DELETE影響
- 初始數據為
- 執行上面這個更改為serializable的事務
- 然后在這個事務外部執行下面測試
- 結論
- 如果某事務是用了serializable隔離級別,則外部就無法insert任何記錄到相應的表中
- 無論insert的字段值是否落在where條件內或者外
- 外部的UPDATE行為:同普通等級
- 實際上DELETE行為也是
- 如果某事務是用了serializable隔離級別,則外部就無法insert任何記錄到相應的表中
- 默認隔離級別中插入的記錄,對於外部查詢的影響
- 執行下面這些測試
- 結論
- 如果某事務是用了read committed隔離級別,則外部默認無法看到這些新增的記錄
- 同樣適用於repeatable read以及serilizable隔離級別
- 使用了NOLOCK關鍵字的查詢語句能看到臟數據
- 如果某事務是用了read committed隔離級別,則外部默認無法看到這些新增的記錄
- 默認隔離級別下事務死鎖例子
- 初始數據
- 我們在2個session中同時執行2個事務,如下
- 結論
- 互相等待資源的釋放、同時又持有現有資源,就會造成死鎖
- 上面例子中被kill掉的事務,選擇犧牲事務的算法,ms稱:正常情況下,SQL Server會把它認為取消或回滾代價最小的連接作為默認的死鎖犧牲品
- 算了,不深入了
- 知道還有個非正常情況 - 死鎖優先級,后續
- 事務優先級 - SET DEADLOCK_PRIORITY(http://msdn.microsoft.com/en-us/library/ms186736.aspx)
- 也可以如下
- 結論
- SET DEADLOCK_PRIORITY可以放在SESSION的任何地方,無論是事務外還是內、前還是后
- sqlserver探測到死鎖后,會根據session的deadlock優先級來kill
- LOW/NORMAL/HIGH
- 分別代表
- -5/0/5
- 分別代表
- 也可以使用數字
- -10直到10
- SET DEADLOCK_PRIORITY可以放在SESSION的任何地方,無論是事務外還是內、前還是后
避免死鎖的建議
- 把SELECT放在事務范圍外
- 將多個事務合並為一個事務
- SELECT加With(NOLOCK)提示
- 降低事務隔離級別, 比如READ UNCOMMITTED
- 使用基於行版本控制的隔離級別