最近我不得不处理一个我想象会很常见的问题:给定一个要处理的行数大(百万+)的数据库表,以及各种机器/线程中运行的各种处理器,如何安全地允许每个处理器实例获得一大块工作(比方说100项)而不会相互干扰?
我一次得到一个块的原因是出于性能原因 - 我不想为每个项目访问数据库。
答案 0 :(得分:1)
有几种方法 - 您可以将每个处理器与一个令牌相关联,并且有一个SPROC可以将该令牌设置为下一个[n]个可用项目;也许是这样的:
(注意 - 需要合适的隔离级别;可能是可序列化的:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
)
(编辑以修复TSQL)
UPDATE TOP (1000) WORK
SET [Owner] = @processor, Expiry = @expiry
OUTPUT INSERTED.Id -- etc
WHERE [Owner] IS NULL
您还需要暂停(@expiry
),这样当处理器停机时您不会失去工作。您还需要一项任务来清除所有者超过其Expiry
的内容。
答案 1 :(得分:0)
你可以有一个特殊的表来排队工作,消费者删除(或标记)工作,或者使用中间件排队解决方案,如MSMQ或ActiveMQ。
中间件带有一系列问题所以,如果可能的话,我会坚持使用一个特殊的表(尽可能保持小,希望只有一个id,这样工作人员可以自己获取其余的信息数据库的其余部分并没有将队列表锁定太长时间。)
您可以定期填写此表,让处理器从顶部获取所需内容。
关于SQL表队列的相关问题:
Working out the SQL to query a priority queue table
排队中间件的相关问题:
Building a high performance and automatically backupped queue
答案 2 :(得分:0)
您没有说明您正在使用哪个数据库服务器,但有几个选项。
MySQL包含对SQL99 INSERT
的扩展,以限制更新的行数。您可以为每个工作人员分配一个唯一的令牌,更新多个行,然后进行查询以获取该工作人员的批次。 Marc使用UPDATE TOP
语法,但没有指定数据库服务器。
另一种选择是指定用于锁定的表。不要对数据使用相同的表,因为您不想锁定它以进行读取。您的锁定表可能只需要一行,下一个ID需要工作。工作人员锁定表,获取当前ID,按批量大小递增,更新表,然后释放锁。然后它可以查询数据表并拉出它保留的行。此选项假定数据表具有单调递增的ID,并且如果工作程序死亡或无法完成批处理,则不具有容错能力。
答案 3 :(得分:0)
与此问题非常相似:SQL Server Process Queue Race Condition
您运行查询以将100行分配给给定的processorid。如果你使用这些锁定提示,那么它在并发意义上是“安全的”。它是一个单独的SQL语句,不需要SET语句。
这取自另一个问题:
UPDATE TOP (100)
foo
SET
ProcessorID = @PROCID
FROM
OrderTable foo WITH (ROWLOCK, READPAST, UPDLOCK)
WHERE
ProcessorID = 0 --Or whatever unassigned is