在今天的文章里我想談論下在可重復讀隔離級別(Transaction Isolation Level Repeatable Read)里,當你運行事務時可能引起的2類死鎖。當你使用可重復讀(Repeatable Read)隔離級別設置你的事務,SQL Server對讀取數據把持需要的共享鎖(Shared Locks)直到事務的結束(COMMIT或ROLLBAK)。然后當你嘗試修改讀取的數據(通過UPDATE語句),如果同樣的事務多次并發運行,它會英氣不同類型的死鎖。
循環死鎖(Cycle Deadlock)循環死鎖是其中一個最常見的死鎖——你就以不同順序訪問資源(例如不同表),最后每個查詢等待另一個。使用可重復讀隔離級別,但你用多個事務只讀寫一個表時,也是有可能引起循環死鎖。我們來看第1個事務的T-SQL代碼:
1 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ 2 GO 3 4 BEGIN TRANSACTION 5 6 SELECT * FROM Person.Person 7 WHERE ModifiedDate = '20030208' 8 9 UPDATE Person.Person10 SET FirstName = '...'11 WHERE ModifiedDate = '20030208'12 13 SELECT * FROM Person.Person14 WHERE ModifiedDate = '20030209'15 16 UPDATE Person.Person17 SET FirstName = '...'18 WHERE ModifiedDate = '20030209'19 20 ROLLBACK21 GO
這是第2個事務的T-SQL代碼:
1 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ 2 GO 3 4 BEGIN TRANSACTION 5 6 SELECT * FROM Person.Person 7 WHERE ModifiedDate = '20030209' 8 9 UPDATE Person.Person10 SET FirstName = '...'11 WHERE ModifiedDate = '20030209'12 13 SELECT * FROM Person.Person14 WHERE ModifiedDate = '20030208'15 16 UPDATE Person.Person17 SET FirstName = '...'18 WHERE ModifiedDate = '20030208'19 20 ROLLBACK21 GO
從2個代碼可以看到,2個數據范圍被讀取,最后被更新。如果2個事務在同個時間執行,會發生循環死鎖,因為數據范圍在不同順序里被訪問。
當SQL Server開始執行UPDATE語句,必須的更新鎖(Update Lock(U))不能被獲取,因為更新鎖(Update Lock)與來自不同會話已授予的共享鎖(Shared Lock)不兼容。因為在其它會話共享鎖(Shared Lock)已獲得,最后2個UPDATE語句會等待——在一個表上就有了經典的循環鎖!在這個情況下你必須重寫你的代碼來讓這個特定鎖得到解決——以同個順序訪問你的數據范圍。
讀寫/更新死鎖(Read/Update Deadlock)使用可重復讀隔離級別的第2類死鎖會發生,如果你讀取數據,有意向稍后去更新。我們來看1個簡單事務的T-SQL代碼:
1 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ 2 GO 3 4 BEGIN TRANSACTION 5 6 SELECT * FROM Person.Person 7 WHERE ModifiedDate = '20030208' 8 9 UPDATE Person.Person10 SET FirstName = '...'11 WHERE ModifiedDate = '20030208'12 13 ROLLBACK14 GO
為了引起這類死鎖,你只要通過多個會話運行事務。如你從代碼里所見,你甚至不需要訪問不同數據范圍。我們來解釋下這里反生了什么。當這個事務在多個會話并發執行時,對讀取的數據,所有的會話會獲得共享鎖。
因為你在可重復讀里把持共享受(Shared Lock)直到事務的結束(COMMIT或ROLLBACK),下列UPDATE語句不能獲得需要的更新鎖(Update Locks),因為它們已被不同會話里獲得的共享鎖(Shared Locks)所阻塞。死鎖!
這里死鎖可以通過在SELECT語句里使用提示來提前獲取一個更新鎖(Update Lock)。
1 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ 2 GO 3 4 BEGIN TRANSACTION 5 6 SELECT * FROM Person.Person WITH (UPDLOCK) 7 WHERE ModifiedDate = '20030208' 8 9 UPDATE Person.Person10 SET FirstName = '...'11 WHERE ModifiedDate = '20030208'12 13 ROLLBACK14 GO
因此在剛開始只有一個SELECT語句能獲得必須的更新鎖(Update Locks)(更新鎖(Update Lock)與本身更新鎖(Update Lock)不兼容),繼續使用UPDATE語句,最后會釋放需要的鎖。然后第2個事務會繼續它的SELECT和UPDATE語句。
這里你要使用SQL Server內部在更新執行計劃里使用的同樣的技術:在可重復讀隔離級別里,當你讀取的數據稍后有意向去更新時,在讀取階段你需要獲得一個更新鎖來阻止這類死鎖。
小結從這篇文章里可以看到,如果你使用可重復讀隔離級別是很容易引起各類死鎖。因此當你在這個特定隔離級別里寫事務時,你必須要非常小心。
感謝關注!
新聞熱點
疑難解答