SqlServer,事务死锁,什么时候表被锁定?

时间:2009-04-21 14:09:39

标签: sql-server transactions

此SQL(从c#调用)偶尔会导致死锁。 服务器负载不是很大,所以使用的方法是尽可能锁定。

   -- Lock to prevent race-conditions when multiple instances of an application calls this SQL:
        BEGIN TRANSACTION 
-- Check that no one has inserted the rows in T1 before me, and that T2 is in a valid state (Test1 != null)
            IF NOT EXISTS (SELECT TOP 1 1 FROM T1 WITH(HOLDLOCK, TABLOCKX) WHERE FKId IN {0}) AND 
            NOT EXISTS(SELECT TOP 1 1 FROM T2 WITH(HOLDLOCK, TABLOCKX) WHERE DbID IN {0} AND Test1 IS NOT NULL) 
            BEGIN
-- Great! Im the first - go insert the row in T1 and update T2 accordingly. Finally write a log to T3 
               INSERT INTO T1(FKId, Status) 
               SELECT DbId, {1} FROM T2 WHERE DbId IN {0}; 

               UPDATE T2 SET LastChangedBy = {2}, LastChangedAt = GETDATE() WHERE DbId IN {0}; 

               INSERT INTO T3 (F1, FKId, F3) 
               SELECT {2}, DbId, GETDATE() FROM T2 WHERE DbId IN {0} ;
            END; 

            -- Select status on the rows so the program can evaluate what just happened
            SELECT FKId, Status FROM T1 WHERE FkId IN {0}; 

        COMMIT TRANSACTION

我认为问题是需要锁定多个表。

我有点不确定这些表是否实际上是xlocked - 第一次使用表时 - 或者所有表都是在BEGIN TRANS上一次锁定?

3 个答案:

答案 0 :(得分:3)

使用表锁可以增加死锁的可能性...并非所有死锁都是由于无序操作引起的......有些可能是由于其他仅尝试锁定单个活动而导致(如您所见)在完全锁定的同一个表中记录,因此锁定整个表会增加发生冲突的可能性。使用可序列化隔离级别时,范围锁定放在索引行上,这可以防止其他sql操作的插入/删除,其方式可能导致来自同一过程的两个并发操作导致死锁,即使它们被编码为执行其操作顺序相同......

无论如何,要找出导致死锁的确切原因,请设置SQL Server跟踪标志1204和1222.这些将导致有关每个死锁的详细信息写入SQL Server日志,包括涉及的语句。

Here是关于如何执行此操作的好文章。

(当你完成时别忘了关闭这些标志......)

答案 1 :(得分:1)

锁定在您调用lock或select with lock并在提交或回滚时释放时完成。

如果另一个程序先在T3中锁定,之后在T1或T2中锁定,则可能会死锁。然后两个事务正在等待彼此获取资源,同时锁定对方需要的内容。

您还可以避免表锁并使用可序列化的隔离级别。

答案 2 :(得分:0)

锁定的问题在于你真的需要同时查看你锁定的所有地方,没有办法将问题分离并拆分成许多较小的问题并单独查看。

例如,如果某些其他代码锁定相同的表,但没有明显的,并且顺序错误怎么办?那会导致僵局。

您需要在发现死锁时分析服务器状态,以尝试确定此刻正在运行的其他内容。只有这样才能尝试解决它。​​