Tsql 如何防止此SQL中发生死锁

Tsql 如何防止此SQL中发生死锁,tsql,database-deadlocks,Tsql,Database Deadlocks,首先,我不需要100%防止死锁,但我能做的任何事情都会很好 我有两个表Source和Dest。Source中有一堆唯一的值,我需要从Source请求一个新值,在这样做时,将它移到Dest中 我有以下sql: begin tran declare @value select top 1 @value = [value] from [source] delete from [Source] where [value]=@value insert into [Dest]

首先,我不需要100%防止死锁,但我能做的任何事情都会很好

我有两个表Source和Dest。Source中有一堆唯一的值,我需要从Source请求一个新值,在这样做时,将它移到Dest中

我有以下sql:

begin tran
    declare @value
    select top 1 @value = [value] from [source]
    delete from [Source] where [value]=@value
    insert into [Dest] ([Value]) values (@value)
    select @value
commit tran

当多个用户获得相同的值行时,这会偶尔引发死锁。如何预防/减少这种情况

我正在使用SQLServer2008

另外,在Source和Dest中还有我正在读/写的其他专栏。为了简洁起见,这是一种简化


谢谢

您可以通过在DELETE命令中使用子句来避免这种竞争条件,因为它将从源中删除值并在单个原子操作中返回。我制作了以下脚本来演示这个概念:

-- dummy data
CREATE TABLE #source (mycolumn INT);
CREATE TABLE #destination (mycolumn INT);

INSERT #source VALUES (1);
INSERT #source VALUES (2);
INSERT #source VALUES (3);
GO

-- stored procedure to demonstrate concept
CREATE PROCEDURE move
AS BEGIN
    BEGIN TRANSACTION;

    DECLARE @tmp TABLE (mycolumn INT);
    DELETE TOP(1) #source OUTPUT DELETED.mycolumn INTO @tmp(mycolumn);

    INSERT #destination (mycolumn) OUTPUT INSERTED.mycolumn
    SELECT mycolumn
    FROM @tmp;

    COMMIT;
END
GO

-- testing
EXEC move;
GO -- remove from 1 from #source, insert 1 into #destination, returns 1

EXEC move;
GO -- remove from 2 from #source, insert 2 into #destination, returns 2

EXEC move;
GO -- remove from 3 from #source, insert 3 into #destination, returns 3

您可以通过在DELETE命令中使用子句来避免这种竞争条件,因为它将从源中删除该值,并在单个原子操作中返回该值。我制作了以下脚本来演示这个概念:

-- dummy data
CREATE TABLE #source (mycolumn INT);
CREATE TABLE #destination (mycolumn INT);

INSERT #source VALUES (1);
INSERT #source VALUES (2);
INSERT #source VALUES (3);
GO

-- stored procedure to demonstrate concept
CREATE PROCEDURE move
AS BEGIN
    BEGIN TRANSACTION;

    DECLARE @tmp TABLE (mycolumn INT);
    DELETE TOP(1) #source OUTPUT DELETED.mycolumn INTO @tmp(mycolumn);

    INSERT #destination (mycolumn) OUTPUT INSERTED.mycolumn
    SELECT mycolumn
    FROM @tmp;

    COMMIT;
END
GO

-- testing
EXEC move;
GO -- remove from 1 from #source, insert 1 into #destination, returns 1

EXEC move;
GO -- remove from 2 from #source, insert 2 into #destination, returns 2

EXEC move;
GO -- remove from 3 from #source, insert 3 into #destination, returns 3
您可以使用SELECT语句获取XLOCK

begin tran
    declare @value
    select top 1 @value = [value] from [source] with (XLOCK)
    delete from [Source] where [value]=@value
    insert into [Dest] ([Value]) values (@value)
    select @value
commit tran
您可以使用SELECT语句获取XLOCK

begin tran
    declare @value
    select top 1 @value = [value] from [source] with (XLOCK)
    delete from [Source] where [value]=@value
    insert into [Dest] ([Value]) values (@value)
    select @value
commit tran

当多个用户获得相同的值行时-您的根本问题不是您要问的问题…您运行的隔离级别是什么?默认值,因此我假设这是已提交的当多个用户获得相同的值行时-您的根本问题不是您要问的问题…您运行的隔离级别是什么?默认值,所以我假设这是我刚刚发现的结果——我正要更新我的问题,询问它的可行性+1因此,假设输出DELETE是原子的/导致一个只会导致等待的行锁?实际上,该子句不允许从源表多次返回相同的值,从而避免了原始争用条件。我不确定SQLServer是如何在内部实现其机制的,但在这种情况下它应该不重要;现在没有时间以后再进行电子测试…顺便说一句,因为删除。。。输出语句避免了RACE条件,如果您可以假设INSERT命令总是完成,也就是说,您可以保证所有插入的值都是有效的,可以考虑删除事务/提交语句,因此减少了这个块中的进程争用。哈,我刚刚发现了输出——我正要更新我的问题,询问它的可行性+1因此,假设输出DELETE是原子的/导致一个只会导致等待的行锁?实际上,该子句不允许从源表多次返回相同的值,从而避免了原始争用条件。我不确定SQLServer是如何在内部实现其机制的,但在这种情况下它应该不重要;现在没有时间以后再进行电子测试…顺便说一句,因为删除。。。输出语句避免了RACE条件,如果您可以假定INSERT命令总是完成,也就是说,您可以保证所有插入的值都是有效的,那么您可以考虑删除事务/COMMIT语句,从而减少该块内的进程争用。