从数据库中同时挑选任务

时间:2013-09-26 17:06:50

标签: c# sql-server database concurrency

在数据库表中,我保留了一个任务列表。为简单起见,假设task是一个整数,并且在表中按顺序递增100个整数。 (增加订单意味着如果某人正在处理任务N,则所有任务< N已经在处理)

我还有5个客户端连接并从数据库中选择任务,并在任务完成时更新数据库。

我不希望任何两个客户选择相同的任务。

每当挑选一个任务时,我都会将它添加到另一个表'tasksTable'中。选择新任务时,我在'tasksTable'中找到max_int,然后选择task = max_int + 1

为避免工作重复,我将序列化挑选任务的过程,即

getlock
read max_int
pick task
update tasksTable
releaselock

由于我只有大约10名工人,所以序列化不是问题。如果我有1000个客户怎么办?如何并行化任务选择?

4 个答案:

答案 0 :(得分:1)

假设有两个表,一个用于未完成的任务(WaitingTasks),一个用于挑选"正在进行中"任务(WorkingTasks):

CREATE TABLE WaitingTasks (WaitingTaskID INT IDENTITY(1,1), ColA, ColB, ...)

CREATE TABLE WorkingTasks (WorkingTaskID INT IDENTITY(1,1), WaitingTaskID INT, ColA, ...)

可以像这样完成并发任务选择:

INSERT INTO WorkingTasks (WaitingTaskID)
SELECT TOP 1 WaitingTaskID
FROM WaitingTasks
WHERE WaitingTaskID NOT IN (SELECT WaitingTaskID FROM WorkingTasks);

SELECT SCOPE_IDENTITY();

这将创建一个新的"工作任务"凭借自己的唯一ID,SCOPE_IDENTITY()将为您返回该唯一ID。

答案 1 :(得分:0)

您可以尝试使用锁定,但您可以使用更新/选择解决此问题:

BEGIN TRAN

UPDATE Task
SET clientId = MyClientId
WHERE [PrimaryKey] = (
   SELECT TOP 1 [PrimaryKey]
   FROM Tasks
   WHERE clientId IS NULL
   ORDER BY CreationDateTime)

SELECT * FROM Tasks
WHERE [PrimaryKey] = MyClientId

COMMIT TRAN

这样的东西?


或者你可以使用OUPUT条款:

DECLARE @TaskId AS INT

UPDATE TOP(1) Task
SET clientId = MyClientId
OUTPUT Task.[PrimaryKey] INTO @TaskId
WHERE clientId IS NULL
ORDER BY CreationDateTime

SELECT @TaskId

(未测试)

来源:http://blog.sqlauthority.com/2007/10/01/sql-server-2005-output-clause-example-and-explanation-with-insert-update-delete/

答案 2 :(得分:0)

不完全遵循你在这里做的事情。您是否在任务完成之前锁定数据库表中的记录?如果是这样,我认为这不是一个好主意。相反,如何在任务数据库表中添加一个提供任务状态的列?如果已签出,请将其设置为true,否则将其设置为false。或者,您可以添加一个列,指示其签出对象(他的personID)。如果它为空,则不会向任何人签出。请注意,在此解决方案中,您只有一个任务数据库表,而不是两个(完成任务的单独表)。我认为这是一个更好的解决方案(更好的规范化)。

仅向用户显示尚未分配的任务。如果他在有机会点击更新按钮之前选择一个并且有人殴打他注册该任务,您可以在更新时告知他为时已晚。示例:此SQL中的'where'子句可以解决这个问题:'update tasks set personID = 233421 where personID == null'。如果更新返回时更新了零记录,则您知道发生了什么。

我认为您的数据库/程序在支持数千名用户时不会有任何问题,因为只有当多个用户在相同的几毫秒内更新任务表时才会有一点延迟,直到第一次更新完成为止。即便如此,对于1000个用户,我怀疑超过10个左右会同时点击更新(几毫秒乘以10仍然不是很多时间)。

此外,我认为您应该让数据库生成一个新的任务编号,而不是使用task = max_int + 1以编程方式执行它。这样您就不必锁定表以确保在更新之前它不会更改。另请注意:如果每次更新需要更新多个表,请设置事务。您不需要为一次表更新执行此操作。

最后注意:处理更新的一个通用解决方案是在表中添加“版本”列(整数或长整数),并在每次有人更新记录时将其增加。然后,对于每次更新,查看版本号是否与上次读取时的版本号相同(SQL:更新任务设置personID = 233421,其中version = 4)。如果不相同,请通知用户。

答案 3 :(得分:0)

我认为你的问题主要是关于增加并发性。你已经有了一个安全的算法。现在你想要缩放。

一些明显的解决方案存在通过锁定表的相同部分来序列化对表的访问的问题。在这里要小心并测试它。

任务排队通常使用READPAST实现。这允许查询读取已经锁定的行,允许在先前任务当前被标记为正在处理时使下一个任务出列。

您可以通过更新某个行或使用XLOCK, ROWLOCK, HOLDLOCK等锁定提示来实现对某一行的独占访问。

There is a set of articles by Rusanu Remus on queue tables that answers about everything.欢迎在本文后面的评论中提出跟进问题。