可重复读隔离级别里的可能死锁

简介:

在今天的文章里我想谈论下在可重复读隔离级别(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内部在更新执行计划里使用的同样的技术:在可重复读隔离级别里,当你读取的数据稍后有意向去更新时,在读取阶段你需要获得一个更新锁来阻止这类死锁。

小结

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

感谢关注!


本文转自Woodytu博客园博客,原文链接:http://www.cnblogs.com/woodytu/p/4695633.html,如需转载请自行联系原作者

相关文章
|
数据库
【并发事务会产生哪些问题】
【并发事务会产生哪些问题】
146 0
|
8月前
|
SQL 关系型数据库 MySQL
MySQL事务原理分析(ACID特性、隔离级别、锁、MVCC、并发读异常、并发死锁以及如何避免死锁)
MySQL事务原理分析(ACID特性、隔离级别、锁、MVCC、并发读异常、并发死锁以及如何避免死锁)
195 1
|
8月前
|
调度 数据库 数据库管理
数据库事务中调度串行化、冲突可串行化、前趋图(优先图)
数据库事务中调度串行化、冲突可串行化、前趋图(优先图)
563 0
|
Oracle 关系型数据库 MySQL
mysql数据库事务脏读、不可重复度、幻读详解
mysql数据库事务脏读、不可重复度、幻读详解
144 0
|
数据库
并发事务带来哪些问题?
并发事务带来哪些问题?
155 0
|
SQL Java 数据库
【事务与并发】- 不同事务读取相同数据问题
在加了事务的接口中,不同的业务或者是出现并发的时候,发现了一些SQL读取问题,两个都被事务包裹的方法,各自是隔离的,如果一方的事务延时提交,就会导致另一方读取出来的数据相同,并不是修改后的数据。
145 0
|
关系型数据库 MySQL
事务的隔离级别——解决脏读、不可重复度、幻读
事务的隔离级别——解决脏读、不可重复度、幻读
事务的隔离级别——解决脏读、不可重复度、幻读
|
存储 SQL Oracle
弱隔离级别 & 事务并发问题
本篇文章主要介绍了 1. 各种隔离级别 2. 事务并发执行时,存在的并发问题 3. 如何防止并发问题
283 0
|
安全 Java 编译器
诡异并发三大恶人之原子性
上一节阿粉我和大家一起打到了并发中的恶霸可见性,这一节我们继续讨伐三恶之一的原子性。
诡异并发三大恶人之原子性