2010-01-21 79 views
4

说我有如下表:选择并更新表,所以没有线程的重叠

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

...我需要阅读的最小ID,有[阅读] ==虚假;另外,更新我现在已经阅读它。

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

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,在同一时间,我不能有选择它们在任何情况下相同的[ID]。我担心我的SELECT和UPDATE语句之间执行第二个线程,因此在两种情况下返回[ID]:2。

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

回答

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; 
+0

min,不会触发所有行上的锁?或者我只是说废话 – 2010-01-21 13:11:33

+0

共享锁是只读的,并且由于READPAST它将跳过其他锁。没关系,真的... – gbn 2010-01-21 13:52:21

0

将select和update以及select语句放入事务中并在事务启动时锁定表,以使其他线程等待。 最好的问候, 约尔丹

+0

我不想锁定表,因为插入是不断的。再加上明天可能会有1000个线程在同一个表上运行,这意味着我需要一个不太干扰,但明确的方式来做到这一点。 – 2010-01-21 12:45:33

+1

@Theofanis - 没关系*如果你搞乱了数据,你的db访问速度如何?首先以最简单的方式尝试它(通过交易),然后*挑战绩效。 – 2010-01-21 12:50:15

+1

如果是这种情况,并且线程的数量可能会增加到1000,则尝试在表中添加一个新的空列。第一件事情就是将它的id写在新列中,所以没有其他线程可以占用这一行 – IordanTanev 2010-01-21 12:52:30

1

如果你希望它是原子,你必须锁定的东西,但是,这并不意味着你必须将其锁定长期。我会先用一些限定范围的交易尝试,但我也很想尝试更新的变种,在同一时间做了SELECT

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

你可以看到如果有任何并发​​问题 - 诚实地说,我不知道没有检查!您可能需要添加诸如WITH (ROWLOCK)之类的内容。但个人而言,我想保持简单并尝试一个可序列化的事务。

另外请注意,这并不能保证其记录你会得到

+0

我不介意锁定ROW,只要我不锁定表。 – 2010-01-21 12:52:52

+0

你的意思是将事务隔离级别设置为可序列化? – 2010-01-21 12:53:32

+0

如果将事务级别设置为可序列化并使用事务,则应该能够使用*原始*代码。 – 2010-01-21 12:56:11

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,并阻止并发查询读取最高记录。

这样,没有交易会获得相同的记录。