Sql 如何保证只有一个进程拾取一个处理任务
我有多台计算机,它们的任务是发送在公共SQL Server上的表中找到的电子邮件。每台计算机通过查看设置为0的状态标志,轮询电子邮件表以查找它可以发送的邮件。如果计算机执行Sql 如何保证只有一个进程拾取一个处理任务,sql,sql-server,Sql,Sql Server,我有多台计算机,它们的任务是发送在公共SQL Server上的表中找到的电子邮件。每台计算机通过查看设置为0的状态标志,轮询电子邮件表以查找它可以发送的邮件。如果计算机执行 SELECT * FROM tblEmailQueue where StatusFlag=0 如果它返回一条记录,它会立即将StatusFlag设置为1,这将导致轮询同一表的另一台计算机找不到该记录。我担心的是,如果两台计算机在更新状态标志之前同时找到记录,电子邮件将发送两次。有没有人对如何确保只有一台计算机获得记录有想法
SELECT * FROM tblEmailQueue where StatusFlag=0
如果它返回一条记录,它会立即将
StatusFlag
设置为1
,这将导致轮询同一表的另一台计算机找不到该记录。我担心的是,如果两台计算机在更新状态标志之前同时找到记录,电子邮件将发送两次。有没有人对如何确保只有一台计算机获得记录有想法?我知道我可能可以做一个表锁,但我宁愿现在必须这样做 在印第安纳波利斯,我们很熟悉比赛情况;-)
让我们假设您实际拥有和ID字段和StatusFlag字段,并创建一个存储过程,其中包括
declare @id int
select top 1 @id = id from tblEmailQuaue where StatusFlag=0
if @@rowcount = 1
begin
update tblEmailQuaue set StatusFlag=1 where ID = @id AND StatusFlag=0
if @@rowcount = 1
begin
-- I won the race, continue processing
...
end
end
增加
如果您想要的只是选择的结果,那么像这样的显式处理不如Joachim的方法。但是这种方法这种方法也适用于旧版本的SQL server以及其他数据库。在印第安纳波利斯,我们熟悉比赛条件;-)
让我们假设您实际拥有和ID字段和StatusFlag字段,并创建一个存储过程,其中包括
declare @id int
select top 1 @id = id from tblEmailQuaue where StatusFlag=0
if @@rowcount = 1
begin
update tblEmailQuaue set StatusFlag=1 where ID = @id AND StatusFlag=0
if @@rowcount = 1
begin
-- I won the race, continue processing
...
end
end
增加
如果您想要的只是选择的结果,那么像这样的显式处理不如Joachim的方法。但是此方法此方法也适用于旧版本的SQL server以及其他数据库。您可以使用更新值并立即输出更新的行,而不是使用两个可能导致竞争条件的查询
这将更新statusflag=0的行,并输出所有更新的行
UPDATE tblEmailQueue
SET statusflag=1
OUTPUT DELETED.*
WHERE statusflag=0;
编辑:如果您选择一行,您可能需要一些订单。由于更新本身无法排序,因此可以使用公共表表达式进行更新
WITH cte AS (
SELECT TOP 1 id, statusflag FROM tblEmailQueue
WHERE statusflag = 0 ORDER BY id
)
UPDATE cte SET statusflag=1 OUTPUT DELETED.*;
这将更新statusflag=0的行,并输出所有更新的行
UPDATE tblEmailQueue
SET statusflag=1
OUTPUT DELETED.*
WHERE statusflag=0;
编辑:如果您选择一行,您可能需要一些订单。由于更新本身无法排序,因此可以使用公共表表达式进行更新
WITH cte AS (
SELECT TOP 1 id, statusflag FROM tblEmailQueue
WHERE statusflag = 0 ORDER BY id
)
UPDATE cte SET statusflag=1 OUTPUT DELETED.*;
.将另一列添加到表tblEmailQueue(比如UserID),然后尝试提取一封电子邮件,如
--Let flag an email and assign it to the application who made the request
--@CurrentUserID is an id unique to each application or user and supplied by the application
--or user who made the request, this will also ensures that the record is going to
--the right application and perhaps you can use it for other purpose such as monitoring.
UPDATE tblEmailQueue set UserID = @CurrentUserID, StatusFlag=1 where ID = isnull(
select top 1 ID from tblEmailQueue where StatusFlag=0 order by ID
), 0)
--Lets get an email that had a flag for the current user id
SELECT * FROM tblEmailQueue where StatusFlag=1 and UserID = @CurrentUserID
向表tblEmailQueue中添加另一列(比如UserID),然后尝试提取一封电子邮件,如
--Let flag an email and assign it to the application who made the request
--@CurrentUserID is an id unique to each application or user and supplied by the application
--or user who made the request, this will also ensures that the record is going to
--the right application and perhaps you can use it for other purpose such as monitoring.
UPDATE tblEmailQueue set UserID = @CurrentUserID, StatusFlag=1 where ID = isnull(
select top 1 ID from tblEmailQueue where StatusFlag=0 order by ID
), 0)
--Lets get an email that had a flag for the current user id
SELECT * FROM tblEmailQueue where StatusFlag=1 and UserID = @CurrentUserID
您可以在同一事务中执行选择和发送电子邮件。您还可以使用ROWLOCK
提示,在发送电子邮件或为StatusFlag
设置新值之前,不要提交事务。这意味着,只要您提交事务,就没有人(使用提示NOLOCK
或readuncommitted
隔离级别执行事务)可以读取此行
SELECT * FROM tblEmailQueue WITH(ROWLOCK) where StatusFlag=0
此外,您应该检查隔离级别。对于您的案例,隔离级别应为readcommitted
或REPEATABLE READ
。
请参阅有关隔离级别的信息您可以在同一事务中执行选择和发送电子邮件。您还可以使用ROWLOCK
提示,在发送电子邮件或为StatusFlag
设置新值之前,不要提交事务。这意味着,只要您提交事务,就没有人(使用提示NOLOCK
或readuncommitted
隔离级别执行事务)可以读取此行
SELECT * FROM tblEmailQueue WITH(ROWLOCK) where StatusFlag=0
此外,您应该检查隔离级别。对于您的案例,隔离级别应为readcommitted
或REPEATABLE READ
。
请参阅有关隔离级别的信息最佳解决方案。有趣的是,所有其他人都给出了代码,却从未学习过完整的语法,所以他们不知道output关键字;)哇,我不能说以前见过这种构造。让我想知道我还缺少什么:0)谢谢!此语句正在更新使statusflag=1的所有记录。有没有一种方法可以只做一条记录?@Chris Yep,行得通,如果你想对处理过程进行排序,可以在答案中添加另一个选项。@Joachim,你一定读过我的心思,因为我需要添加优先级!我用了你的例子,效果很好!非常感谢。最佳解决方案。有趣的是,所有其他人都给出了代码,却从未学习过完整的语法,所以他们不知道output关键字;)哇,我不能说以前见过这种构造。让我想知道我还缺少什么:0)谢谢!此语句正在更新使statusflag=1的所有记录。有没有一种方法可以只做一条记录?@Chris Yep,行得通,如果你想对处理过程进行排序,可以在答案中添加另一个选项。@Joachim,你一定读过我的心思,因为我需要添加优先级!我用了你的例子,效果很好!非常感谢。