SqlServer|sql server的UPDLOCK、HOLDLOCK试验

【SqlServer|sql server的UPDLOCK、HOLDLOCK试验】SqlServer|sql server的UPDLOCK、HOLDLOCK试验
文章图片

-- 2个tran都查询id = 1000001数据,然后尝试更新 -- 结果2边都能在10s内结束 BEGIN TRAN SELECT * FROM content_article where id = '1000001'; WAITFOR DELAY '00:00:10' UPDATE content_article SET weighted = 2 where id = '1000001'; COMMIT TRAN-- 查询时都加上HOLDLOCK, 左边先运行 -- 结果:左边18s结束,右边12s结束 -- 右边报错:事务(进程 ID 58)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。请重新运行该事务。 BEGIN TRAN SELECT * FROM content_article with (HOLDLOCK) where id = '1000001'; WAITFOR DELAY '00:00:10' UPDATE content_article SET weighted = 2 where id = '1000001'; COMMIT TRAN-- 左边不变,右边加上UPDLOCK -- 结果:同上-----------应该说明右边并没有活动UPDLOCK吧 BEGIN TRAN SELECT * FROM content_article with (HOLDLOCK) where id = '1000001'; WAITFOR DELAY '00:00:10' UPDATE content_article SET weighted = 2 where id = '1000001'; SELECT * FROM op_content_article where id = '1000001'; COMMIT TRAN

推测:
1.因为数据库默认 隔离级别 读已提交,故select语句只是S锁,不加HOLDLOCK,二边事务正常运行;
2.加了HOLDLOCK,首次select的语句会获得UPDLOCK, 故发生死锁时,未获得UPDLOCK报错了,即右边后面2次都无法获得UPDLOCK
实际:如官方文档所示,sql server 死锁监视器会定期检查 陷入死锁的任务,检测到了循环依赖关系,选择其中一个任务作为牺牲品,终止并提示错误
SqlServer|sql server的UPDLOCK、HOLDLOCK试验
文章图片

再举一个具体的例子详细说明:
-- 事务1先获得row1共享锁 -- 事务2获得row2共享锁 -- 事务2尝试获得row1排他锁---但事务1的共享锁阻止了 -- 事务1尝试获得row2排他锁---但事务2的共享锁阻止了-- 事务1和事务2互相死锁,被死锁监视器检测到,故10s左右 两边都会执行完毕; BEGIN TRAN SELECT * FROM content_article (HOLDLOCK) where id = '1000001'; WAITFOR DELAY '00:00:10' UPDATE content_article SET weighted = 2 where id = '1000002'; COMMIT TRANROLLBACK TRAN BEGIN TRAN SELECT * FROM content_article with (HOLDLOCK) where id = '1000002'; UPDATE content_article SET weighted = 2 where id = '1000001'; COMMIT TRAN

    推荐阅读