SELECT和UPDATE表,因此Threads没有重叠

时间:2010-01-21 12:35:28

标签: c# sql sql-server-2005 tsql concurrency

说我有下表:

ID|Read
-------
 1|true
 2|false
 3|false
 4|false

...我需要读取最小的ID,其中[Read] == false;另外,更新我现在已经阅读过了。

因此,如果我执行我的存储过程dbo.getMinID,它将返回ID:2,并更新[Read] - >真。

CREATE PROCEDURE [dbo].[getMinID]
(
  @QueryID INT OUTPUT 
)
BEGIN
  SELECT TOP 1 @QueryID = [ID] from Table
  UPDATE Table SET [Read] = 1 WHERE [ID] = @QueryID 
END

问题是我有十(10)个异步线程同时执行dbo.getMinID,我不能让它们在任何情况下都选择SAME [ID]。我担心我的SELECT和UPDATE语句之间执行第二个线程,因此在两个场景中返回[ID]:2。

无论有多少线程对存储过程执行操作,我如何确保不会选择/更新同一记录两次?另外,请记住表CONSTANTLY添加了新行,所以我无法锁定表格!

4 个答案:

答案 0 :(得分:3)

如果你的意思是并发安全队列类型锁定,那么使用ROWLOCK,UPDLOCK,READPAST提示?

SQL Server Process Queue Race Condition

BEGIN TRAN

SELECT TOP 1 @QueryID = [ID] from Table WITH (ROWLOCK, UPDLOCK, READPAST)
UPDATE Table SET [Read] = 1 WHERE [ID] = @QueryID 

COMMIT TRAN -- TRAM

然而,在一个声明中。

之类的东西
WITH T AS
(
    --ORDER BY with TOP , or perhaps MIN is better?
    SELECT TOP 1 [Read], [ID] from Table
    WITH (ROWLOCK, UPDLOCK, READPAST) ORDER BY [Read]
)
UPDATE
    T
SET
    [Read] = 1;

答案 1 :(得分:1)

如果你想要它是原子的,你必须锁定一些东西,但这并不意味着你必须长时间锁定它。我会首先尝试一些严格范围的交易,但我也有兴趣尝试同时执行SELECT的更新变体:

UPDATE TOP (1) [foo]
SET [read] = 1
OUTPUT INSERTED.id
WHERE [read] = 0

如果有任何并发​​问题,你可以 - 诚实,我不知道没有检查!您可能需要添加WITH (ROWLOCK)之类的内容。但就个人而言,我希望保持简单并尝试可序列化的交易。

另请注意,这并不能保证您将获得哪个记录(首先?最后?)

答案 2 :(得分:1)

使您的事务隔离级别SERIALIZABLE并使用SELECT命令置独占锁:

SELECT TOP 1 @QueryID = [ID] from Table WITH (XLOCK) ORDER BY id DESC
UPDATE Table SET [Read] = 1 WHERE [ID] = @QueryID 

这会在最关键的范围内放置XLOCK,并会阻止并发查询读取最高记录。

这样,任何交易都不会获得相同的记录。

答案 3 :(得分:0)

将select和update以及select语句放在事务中,并在事务开始时锁定表,以便外层线程等待。 最好的祝福, 约尔丹