1

在数据库表中,我保留了一个任务列表。为简单起见,假设任务是一个整数,并让表中有 100 个整数按递增顺序排列。(增加顺序意味着如果有人正在处理任务 N,那么所有小于 N 的任务都已经在处理)

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

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

Whenever a task is picked, I add it to another table, 'tasksTable'. 选择新任务时,我在'tasksTable'中找到max_int,然后选择task = max_int+1

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

getlock
read max_int
pick task
update tasksTable
releaselock

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

4

4 回答 4

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 返回给您。

于 2013-09-26T17:30:23.710 回答
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/

于 2013-09-26T17:24:28.920 回答
0

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

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

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

您可以通过更新某行或使用锁定提示(如XLOCK, ROWLOCK, HOLDLOCK.

Rusanu Remus 有一组关于队列表的文章,可以回答所有问题。随时在本文下方的评论中提出后续问题。

于 2013-09-26T19:00:36.800 回答
0

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

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

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

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

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

于 2013-09-26T17:39:14.350 回答