Tsql 如何防止此SQL中发生死锁
首先,我不需要100%防止死锁,但我能做的任何事情都会很好 我有两个表Source和Dest。Source中有一堆唯一的值,我需要从Source请求一个新值,在这样做时,将它移到Dest中 我有以下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]
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语句,从而减少该块内的进程争用。