说我有下表:
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添加了新行,所以我无法锁定表格!
答案 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语句放在事务中,并在事务开始时锁定表,以便外层线程等待。 最好的祝福, 约尔丹