简单问题 - 我在SQL Server表中有300k任务,我希望多个进程逐个选择它们,处理它们并保存结果。
我不时会在拣货和储蓄方面陷入僵局。
我需要确保两个进程不会选择相同的任务。因此,我使用XLOCK
并在完成任务后,我将状态从1-Created更改为2-Started,之后将其处理为3-Completed。
此外,我的任务(tblTasksSets
)与tblGeneralSets
一对一引用(请不要问;))并且tblGeneralSets
被多对一引用到tblContainers
。
所以我有两个程序来更新并选择已选择的任务:
DECLARE @SetIds AS TABLE(Id INT)
-- Updating status
;WITH innerTable AS
(
SELECT TOP 1 taskSets.*
FROM tblTasksSets taskSets WITH (XLOCK ROWLOCK)
INNER JOIN tblGeneralSets generalSets WITH(XLOCK ROWLOCK) ON generalSets.TaskSetId = taskSets.Id
WHERE generalSets.ContainerId = @ContainerId
AND taskSets.ParameterSetStatusId = 1 --CREATED
)
UPDATE innerTable
SET ParameterSetStatusId = 2 -- STARTED
OUTPUT INSERTED.Id INTO @SetIds
-- Here are some unrelated updates on log history tables
-- And returning result
SELECT
taskSets.*, containers.*
FROM
tblTasksSets taskSets
INNER JOIN
tblGeneralSets generalSets ON generalSets.TaskSetId = taskSets.Id
INNER JOIN
tblContainers containers ON containers.Id = generalSets.ContainerId
WHERE
taskSets.Id = (SELECT TOP 1 Id FROM @SetIds)
和第二个将任务标记为已完成:
UPDATE tblTasksSets
SET
....
WHERE Id = @Id
为什么我会陷入僵局?我只想要一个进程等待另一个进程完成更新。
System.Data.SqlClient.SqlException(0x80131904):事务(进程ID 53)在锁资源上与另一个进程死锁,并被选为死锁牺牲品。重新运行该交易。
在测试时,没有其他查询在此数据库上运行。每隔几秒钟就会有50个进程调用这两个存储过程
答案 0 :(得分:1)
有一种强制方式可以序列化对资源的访问。使用sql server的内置锁定,您可以使用SP_GETAPPLOCK()对您的工作进行排队访问。一个例子是:
CREATE PROC MyCriticalWork(@MyParam INT)
AS
DECLARE @LockRequestResult INT = 0
DECLARE @MyTimeoutMiliseconds INT =5000--Wait only five seconds max then timeout
BEGIN TRAN
EXEC @LockRequestResult=SP_GETAPPLOCK 'MyCriticalWork','Exclusive','Transaction',@MyTimeoutMiliseconds
IF(@LockRequestResult>=0)BEGIN
/*
DO YOUR CRITICAL READS AND WRITES HERE
*/
COMMIT TRAN -- <--Releases the lock!
END ELSE
ROLLBACK TRAN