READ UNCOMMITTED隔离级别下SQL Server死锁原因排查
死锁问题排查:READ UNCOMMITTED隔离级别下仍出现死锁的原因
我收到错误提示:Transaction (Process ID 60) was deadlocked on lock resources with another process ...。我有如下两个简单查询:
Query 1
BEGIN try SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED BEGIN tran update RestaurantAccount set CreatedOn = GETDATE() where Id = 1 WAITFOR DELAY '00:00:6' update RestaurantInvoice set CreatedOn = GETDATE() where Id = 1 commit tran END try BEGIN catch IF(@@TRANCOUNT > 0) rollback tran SELECT cast(1 as bit) as hasError ,ERROR_LINE() AS ErrorLine ,(isnull(ERROR_MESSAGE(),'') + isnull(ERROR_PROCEDURE(),'')) AS ErrorMessage END catch
Query 2
BEGIN try SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED BEGIN tran update RestaurantInvoice set CreatedOn = GETDATE() where Id = 1 WAITFOR DELAY '00:00:6' update RestaurantAccount set CreatedOn = GETDATE() where Id = 1 commit tran END try BEGIN catch IF(@@TRANCOUNT > 0) rollback tran SELECT cast(1 as bit) as hasError ,ERROR_LINE() AS ErrorLine ,(isnull(ERROR_MESSAGE(),'') ) AS ErrorMessage END catch
我为两个查询都设置了READ UNCOMMITTED事务隔离级别,先执行第一个查询后立即执行第二个,却仍出现死锁错误。我原以为READ UNCOMMITTED级别不会产生锁,请问该死锁产生的原因是什么?(我使用的是SQL Server 2014)
死锁产生的核心原因
READ UNCOMMITTED隔离级别仅影响读取操作的锁行为:它允许读取未提交的数据(脏读),读取时不会加共享锁,也不会阻塞排它锁。但修改操作(UPDATE/INSERT/DELETE)完全不受READ UNCOMMITTED的约束,SQL Server执行修改时依然会申请并持有排它锁(X锁),直到整个事务提交或回滚才会释放。
你的两个事务的执行流程完全命中了死锁的经典场景:
- 事务1先执行
UPDATE RestaurantAccount WHERE Id=1,获取了RestaurantAccount表中Id=1行的排它锁 - 事务2几乎同时执行
UPDATE RestaurantInvoice WHERE Id=1,获取了RestaurantInvoice表中Id=1行的排它锁 - 等待6秒后,事务1尝试修改
RestaurantInvoice的Id=1行,但该行已被事务2持有排它锁,事务1进入等待状态 - 同时事务2尝试修改
RestaurantAccount的Id=1行,该行已被事务1持有排它锁,事务2也进入等待状态 - 两个事务形成循环依赖,互相等待对方释放锁,触发SQL Server的死锁检测机制,最终其中一个事务会被强制终止以打破死锁
总结
- READ UNCOMMITTED仅控制读取操作的锁策略,对修改操作的锁行为无任何影响
- 本次死锁的本质是两个事务以相反的顺序获取资源锁,和读取隔离级别没有关系
内容的提问来源于stack exchange,提问作者Mohammad Galouz Lee
相关产品推荐
相关产品推荐

