我有一个包含数千个条目的数据库表。我有多个工作线程,一次拾取一行,做一些工作(每个大约需要一秒)。在拾取行时,每个线程都会更新数据库行上的标志(如时间戳),以便其他线程不会拾取它。但问题是我最终遇到了多个线程正在拾取同一行的情况。
我的一般问题是,我应该遵循什么样的通用设计方法,以确保每个线程获取唯一的行并独立完成其任务。
注意:多个线程并行运行以加速数据库行的处理。所以我希望有一个尽可能小的关键段或独占锁。
只是给出一些上下文,下面是存储过程,它在更新了行上的标志后从表中获取行。请注意,存储过程不可编译,因为我已从中删除了不必要的部分。但一般来说就是它的结构。
当多个线程并行执行存储过程时会发生此问题。除非事务已提交,否则更新语句(请注意,更新是在占用锁之后完成)在一个线程中所做的更改对另一个线程是不可见的。由于UPDATE和TRANSACTION COMMIT之间存在SELECT语句(大约需要50ms),因此在20%的情况下,线程中的UPDATE语句会获取已经处理过的行。
我希望我在这里足够清楚。
USE ['mydatabase']
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[GetRequest]
AS
BEGIN
-- some variable declaration here
BEGIN TRANSACTION
-- check if there are blocking rows in the request table
-- FM: Remove records that don't qualify for operation.
-- delete operation on the table to remove rows we don't want to process
delete FROM request where somecondition = 1
-- Identify the requests to process
DECLARE @TmpTableVar table(TmpRequestId int NULL);
UPDATE TOP(1) request
WITH (ROWLOCK)
SET Lock = DateAdd(mi, 5, GETDATE())
OUTPUT INSERTED.ID INTO @TmpTableVar
FROM request tur
WHERE (Lock IS NULL OR GETDATE() > Lock) -- not locked or lock expired
AND GETDATE() > NextRetry -- next in the queue
IF(@@RowCount = 0)
BEGIN
ROLLBACK TRANSACTION
RETURN
END
select @RequestID = TmpRequestId from @TmpTableVar
-- Get details about the request that has been just updated
SELECT somerows
FROM request
WHERE somecondition = 1
COMMIT TRANSACTION
END
答案 0 :(得分:0)
SQL Server中关键部分的模拟是sp_getapplock,它易于使用。或者,您可以使用(UPDLOCK,READPAST,ROWLOCK)表提示选择要更新的行。这两个都需要多语句事务来控制独占锁定的持续时间。
答案 1 :(得分:0)
您需要在sql上启动transaction isolation level
以隔离您的行,但这会影响您的效果。
查看示例:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
GO
BEGIN TRANSACTION
GO
SELECT ID, NAME, FLAG FROM SAMPLE_TABLE WHERE FLAG=0
GO
UPDATE SAMPLE_TABLE SET FLAG=1 WHERE ID=1
GO
COMMIT TRANSACTION
整理,不存在使用隔离级别的更好方法。您需要分析每个级别隔离的正负点并测试您的系统性能。
更多信息:
https://docs.microsoft.com/en-us/sql/t-sql/statements/set-transaction-isolation-level-transact-sql
http://www.besttechtools.com/articles/article/sql-server-isolation-levels-by-example