zoukankan      html  css  js  c++  java
  • 可重复读隔离级别里的可能死锁

    在今天的文章里我想谈论下在可重复读隔离级别(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.Person
    10 SET FirstName = '...'
    11 WHERE ModifiedDate = '20030208'
    12 
    13 SELECT * FROM Person.Person
    14 WHERE ModifiedDate = '20030209'
    15 
    16 UPDATE Person.Person
    17 SET FirstName = '...'
    18 WHERE ModifiedDate = '20030209'
    19 
    20 ROLLBACK
    21 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.Person
    10 SET FirstName = '...'
    11 WHERE ModifiedDate = '20030209'
    12 
    13 SELECT * FROM Person.Person
    14 WHERE ModifiedDate = '20030208'
    15 
    16 UPDATE Person.Person
    17 SET FirstName = '...'
    18 WHERE ModifiedDate = '20030208'
    19 
    20 ROLLBACK
    21 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.Person
    10 SET FirstName = '...'
    11 WHERE ModifiedDate = '20030208'
    12 
    13 ROLLBACK
    14 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.Person
    10 SET FirstName = '...'
    11 WHERE ModifiedDate = '20030208'
    12 
    13 ROLLBACK
    14 GO

    因此在刚开始只有一个SELECT语句能获得必须的更新锁(Update Locks)(更新锁(Update Lock)与本身更新锁(Update Lock)不兼容),继续使用UPDATE语句,最后会释放需要的锁。然后第2个事务会继续它的SELECT和UPDATE语句。

    这里你要使用SQL Server内部在更新执行计划里使用的同样的技术:在可重复读隔离级别里,当你读取的数据稍后有意向去更新时,在读取阶段你需要获得一个更新锁来阻止这类死锁。

    小结

    从这篇文章里可以看到,如果你使用可重复读隔离级别是很容易引起各类死锁。因此当你在这个特定隔离级别里写事务时,你必须要非常小心。

    感谢关注!

    参考文章:

    https://www.sqlpassion.at/archive/2014/12/15/possible-deadlocks-in-the-isolation-level-repeatable-read/

  • 相关阅读:
    (Good Bye 2019) Codeforces 1270B Interesting Subarray
    (Good Bye 2019) Codeforces 1270A Card Game
    Codeforces 1283D Christmas Trees(BFS)
    Codeforces 1283C Friends and Gifts
    Codeforces 1283B Candies Division
    1095 Cars on Campus (30)
    1080 Graduate Admission (30)
    1099 Build A Binary Search Tree (30)
    1018 Public Bike Management (30)
    1087 All Roads Lead to Rome (30)
  • 原文地址:https://www.cnblogs.com/woodytu/p/4695633.html
Copyright © 2011-2022 走看看