Transaction And Lock--唯一索引下INSERT导致的死锁
背景:
曾经的一位同事问我:"数据库只有并发INSERT 操作,会造成死锁么?",我没有太多思考地回答"不会",但真的不会吗?
测试:
--================================= --创建测试表 CREATE TABLE TB3 ( ID INT PRIMARY KEY ) GO --=================================== --新开回话1 BEGIN TRAN INSERT INTO TB3 SELECT 2 WAITFOR DELAY '0:0:10' INSERT INTO TB3 SELECT 1 --=================================== --新开回话2 BEGIN TRAN INSERT INTO TB3 SELECT 1 WAITFOR DELAY '0:0:10' INSERT INTO TB3 SELECT 2
在上面的两个回话中,由于主键(唯一约束)的限制,相同的key对应相同的lock Resource,导致需要等待对方所获取的lock Resource,从而引发死锁
而如果将主键修改为非唯一索引,则不会引发死锁,相同的key对应不相同的lock Resource,因此不会造成相互等待也不引发死锁。
--====================================================================
--华丽的PS
对于最常见的死锁场景:
事务1 获取表TB1上的资源,请求表TB2上的资源
事务2 获取表TB2上的资源,请求表TB1上的资源
很多开发人员都会有意识保证各个事务访问不同表的顺序,从而避免此类死锁的发生,但很少会考虑对相同表的访问顺序,尤其在输入值被参数化的情况,如在下面的情况下:
BEGIN TRAN UPDATE TB1 SET C2=@P2 WHERE C1=@P1 UPDATE TB1 SET C2=@P3 WHERE C1=@P3
COMMIT
在考虑是否会引发死锁时,我们除了分析当前语句外,还得检查这些语句所涉及到的表相关对象:触发器+外键+索引,还需分析其他业务场景的潜在影响。
PS:很多我们自认为正确的小结论,洗洗(细细)分析下,根本就是错误,而这些西奥结论,可能造成很恶劣影响。
--========================================================
照例妹子镇贴
图片来源于一豆瓣友邻