CFThread 批处理 - 记录被处理多次
CFThread batch proccess - Records being processed multiple times
我们正在通过批处理发送大量电子邮件。这个进程在 CFThread 中 运行s。每个记录集都是从 SQL 服务器检索的。当我们遍历电子邮件时,我们更新发送的记录。
当我们 运行 每分钟处理一次时,它工作正常。当每 30 秒 运行ning 一次时,它会开始多次处理记录。
尽管我们将记录标记为 "pulled",但我们仍然面临问题。
看起来当第一个请求仍在处理时,第二个请求开始了。第二个请求在第一个请求可以将它们标记为 "pulled" 之前检索记录。所以同样的记录被处理了两次。
我不想锁定 table,因为在提取记录的同时,我也在发送电子邮件并更新在同一 table 中发送的其他记录。
数据库:(伪代码)
BEGIN
DECLARE @MyTempTable table(
ID INT IDENTITY(1,1) NOT NULL,
emailBody varchar(max),
eMailfromdisplay varchar(200),
eMailFromAdr varchar(200),
eMailsubject varchar(200),
emailAdr varchar(200),
FirstName_vch varchar(250),
LastName_vch varchar(250),
Sent INT,
pulled INT,
masterEmailTableID BIGINT
);
insert into @MyTempTable
select Distinct top 1000
emailBody,
eMailfromdisplay,
eMailFromAdr,
eMailsubject,
emailAdr,
FirstName,
LastName,
Sent,
pulled ,
masterEmailTableID
from
emailMasteTable
where
pulled = 0 and
Sent = 0
SELECT TOP 1000 id
,masterEmailTableID
,Email_adr
,firstName
,LastName
,Sent
,pulled
FROM @MyTempTable where sent = 0 and pulled = 0
-- Other tables status updates
update emailMasteTable set pulled = 1 where EmailID IN (Select distinct masterEmailTableID from @MyTempTable);
END
ColdFusion : (伪代码)
<cfquery name="getMessages">
exec spGetEmailMessages
</cfquery>
<cfoutput query="getMessages">
<cfmail>
<!--- Email Stuff --->
<cfquery>
update emailMasteTable set sent = 1 where EmailID = #getMessages.masterEmailTableID#;
</cfquery>
</cfmail>
</cfoutput>
我建议阅读 how locking works in SQL Server。这是一个很大的话题,但简而言之,简单地将语句包装在存储过程中并不意味着访问在某种程度上是单线程的,或者神奇地阻止了其他线程获取同一组记录。当前代码中没有任何内容可以阻止同一批记录在 UPDATE 开始之前多次“read”。事实上,涉及的线程越多,间隔越频繁,它就越有可能是多个线程将处理相同的记录。
在 检索到 "in use" 后将一批记录标记为 是不够的。在 检索它们时,必须对它们进行标记 。例如,运行 UPDATE 标记一批记录。使用 OUTPUT clause 捕获 ID 并将它们存储在某种临时文件中 table。然后 JOIN 回到临时 table 以执行任何其他操作。鉴于它是一个经常访问的 table 你应该做一些分析以确定 optimal/most 有效的方法。
-- Stores next batch of ids
DECLARE @TempTable TABLE ( masterEmailTableID BIGINT );
-- Flag next batch as being "processed"
-- Note: Without an order by clause result order is arbitrary
UPDATE TOP (1000) BigTable
SET Pulled = 1
OUTPUT inserted.masterEmailTableID INTO @TempTable
WHERE Pulled = 0
AND Sent = 0
-- do other queries ....
-- Return details for current batch
SELECT main.masterEmailTableID
, main.masterEmailTableID
, ... other columns
FROM BigTable main INNER JOIN @TempTable tmp ON tmp.masterEmailTableID = main.masterEmailTableID
GO
旁注,您还可以考虑在 table 中添加一个唯一的批号,以促进批量状态更新,而不是单独更新项目。
我们正在通过批处理发送大量电子邮件。这个进程在 CFThread 中 运行s。每个记录集都是从 SQL 服务器检索的。当我们遍历电子邮件时,我们更新发送的记录。
当我们 运行 每分钟处理一次时,它工作正常。当每 30 秒 运行ning 一次时,它会开始多次处理记录。
尽管我们将记录标记为 "pulled",但我们仍然面临问题。 看起来当第一个请求仍在处理时,第二个请求开始了。第二个请求在第一个请求可以将它们标记为 "pulled" 之前检索记录。所以同样的记录被处理了两次。
我不想锁定 table,因为在提取记录的同时,我也在发送电子邮件并更新在同一 table 中发送的其他记录。
数据库:(伪代码)
BEGIN
DECLARE @MyTempTable table(
ID INT IDENTITY(1,1) NOT NULL,
emailBody varchar(max),
eMailfromdisplay varchar(200),
eMailFromAdr varchar(200),
eMailsubject varchar(200),
emailAdr varchar(200),
FirstName_vch varchar(250),
LastName_vch varchar(250),
Sent INT,
pulled INT,
masterEmailTableID BIGINT
);
insert into @MyTempTable
select Distinct top 1000
emailBody,
eMailfromdisplay,
eMailFromAdr,
eMailsubject,
emailAdr,
FirstName,
LastName,
Sent,
pulled ,
masterEmailTableID
from
emailMasteTable
where
pulled = 0 and
Sent = 0
SELECT TOP 1000 id
,masterEmailTableID
,Email_adr
,firstName
,LastName
,Sent
,pulled
FROM @MyTempTable where sent = 0 and pulled = 0
-- Other tables status updates
update emailMasteTable set pulled = 1 where EmailID IN (Select distinct masterEmailTableID from @MyTempTable);
END
ColdFusion : (伪代码)
<cfquery name="getMessages">
exec spGetEmailMessages
</cfquery>
<cfoutput query="getMessages">
<cfmail>
<!--- Email Stuff --->
<cfquery>
update emailMasteTable set sent = 1 where EmailID = #getMessages.masterEmailTableID#;
</cfquery>
</cfmail>
</cfoutput>
我建议阅读 how locking works in SQL Server。这是一个很大的话题,但简而言之,简单地将语句包装在存储过程中并不意味着访问在某种程度上是单线程的,或者神奇地阻止了其他线程获取同一组记录。当前代码中没有任何内容可以阻止同一批记录在 UPDATE 开始之前多次“read”。事实上,涉及的线程越多,间隔越频繁,它就越有可能是多个线程将处理相同的记录。
在 检索到 "in use" 后将一批记录标记为 是不够的。在 检索它们时,必须对它们进行标记 。例如,运行 UPDATE 标记一批记录。使用 OUTPUT clause 捕获 ID 并将它们存储在某种临时文件中 table。然后 JOIN 回到临时 table 以执行任何其他操作。鉴于它是一个经常访问的 table 你应该做一些分析以确定 optimal/most 有效的方法。
-- Stores next batch of ids
DECLARE @TempTable TABLE ( masterEmailTableID BIGINT );
-- Flag next batch as being "processed"
-- Note: Without an order by clause result order is arbitrary
UPDATE TOP (1000) BigTable
SET Pulled = 1
OUTPUT inserted.masterEmailTableID INTO @TempTable
WHERE Pulled = 0
AND Sent = 0
-- do other queries ....
-- Return details for current batch
SELECT main.masterEmailTableID
, main.masterEmailTableID
, ... other columns
FROM BigTable main INNER JOIN @TempTable tmp ON tmp.masterEmailTableID = main.masterEmailTableID
GO
旁注,您还可以考虑在 table 中添加一个唯一的批号,以促进批量状态更新,而不是单独更新项目。