我正在尝试在 SQL Server 表中插入/更新行(取决于它是否存在)。我正在多台机器上从多个线程执行 SQL,我想避免出现重复的键错误。
我在网上找到了很多解决方案,但它们都导致事务死锁。这是我一直在使用的一般模式:
BEGIN TRANSACTION
UPDATE TestTable WITH (UPDLOCK, SERIALIZABLE)
SET Data = @Data
WHERE Key = @Key
IF(@@ROWCOUNT = 0)
BEGIN
INSERT INTO TestTable (Key, Data)
VALUES (@Key, @Data)
END
COMMIT TRANSACTION
我试过了:
WITH XLOCK
代替UPDLOCK
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
一开始是UPDLOCK
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
并且没有表格提示
我还尝试了以下所有组合的模式:
BEGIN TRANSACTION
IF EXISTS (SELECT 1 FROM TestTable WITH (UPDLOCK, SERIALIZABLE) WHERE Key=@Key)
BEGIN
UPDATE TestTable
SET Data = @Data
WHERE Key = @Key
END
ELSE
BEGIN
INSERT INTO TestTable (Key, Data)
VALUES (@Key, @Data)
END
COMMIT TRANSACTION
我可以让它在没有死锁的情况下工作的唯一方法是使用WITH (TABLOCKX)
.
我使用的是 SQL Server 2005,SQL 是在运行时生成的,因此它不在存储过程中,并且一些表使用复合键而不是主键,但我可以在具有整数主键的表上重现它。
服务器日志如下所示:
waiter id=processe35978 mode=RangeS-U requestType=wait
waiter-list
owner id=process2ae346b8 mode=RangeS-U
owner-list
keylock hobtid=72057594039566336 dbid=28 objectname=TestDb.dbo.TestTable indexname=PK_TestTable id=lock4f4fb980 mode=RangeS-U associatedObjectId=72057594039566336
waiter id=process2ae346b8 mode=RangeS-U requestType=wait
waiter-list
owner id=processe35978 mode=RangeS-U
owner-list
keylock hobtid=72057594039566336 dbid=28 objectname=TestDb.dbo.TestTable indexname=PK_TestTable id=lock2e8cbc00 mode=RangeS-U associatedObjectId=72057594039566336
模式明显不同,具体取决于使用的表提示(但进程总是在等待它们已经拥有的模式)。我见过 RangeS-U、RangeX-X 和 U。
我究竟做错了什么?