2016-09-03 21 views
0

バッチ処理で大量の電子メールを送信しています。このプロセスはCFThreadの内部で実行されます。各レコードセットはSQL Serverから取得されます。電子メールをループすると、送信されたレコードが更新されます。CFThreadバッチ処理 - 複数回処理されているレコード

毎分プロセスを実行すると、正常に動作します。 30秒ごとに実行すると、レコードの処理が複数回開始されます。

レコードを「引っ張った」とフラグを立てても、まだ問題が残っていました。 第1の要求がまだ処理中であるように見えます。第2の要求が開始されます。第2の要求は、第1の要求がそれらを「引っ張られた」とフラグ付けする前にレコードを検索する。したがって、同じレコードが2回処理されます。

テーブルをロックしたくないのは、私がレコードを引っ張っている間に、同じテーブルに送信されたように電子メールを送信し、他のレコードを更新しているからです。

データベース:(擬似コード

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> 
+1

実際のスクリプトを見ずに言うことはできないと思いますが、可能なら共有してください。 – Susang

+2

複数のスレッドがレコードにアクセスする可能性がある場合は、それらが処理された後に完了*としてフラグを付けるだけでは不十分です。競合状態を防ぐために、ある種のロック機構を実装しなければならない(MUST)。たとえば、新規、処理中、完了済み、失敗した複数のステータスを実装できます。ステータスがnewのレコードをチェックし、検索時に*フラグを付けます。これにより、他のプロセスが同じデータを取得するのを防ぐことができます。処理が完了したら、通常どおり完了/失敗としてマークします。 – Leigh

+0

*引っ張ったときにレコードにフラグを立てても、引き続き問題に直面しますが、正しく実行すれば起こりません。いくつかのコードを投稿する必要があります。具体的には、そのロジックの一部です。 – Leigh

答えて

0

私はhow locking works in SQL Server上に読んで推薦します。大きな話題ですが、一言で言えば、ストアドプロシージャ内のステートメントを単にラップするだけでは、アクセスが何らかの形でシングルスレッドであることや、他のスレッドが同じレコードセットを取得することが魔法のように妨げられることはありません。現在のコードでは、UPDATEが実行される前に同じバッチのレコードが "read"になることはありません。実際にはスレッドが多く、間隔が頻繁になるほど、複数のスレッドが同じ処理を行う可能性が高くなります記録。

の後で、レコードのバッチを「使用中」としてフラグを付けるのでは不十分です。が検索されると、フラグはに設定されていなければなりません。たとえば、レコードのバッチにフラグを立てるためにUPDATEを実行します。 OUTPUT clauseを使用してIDを取得し、何らかの種類の一時テーブルに格納します。その後、追加の操作を実行するために一時テーブルに戻る。それが頻繁にアクセスされるテーブルであることを考えれば、最適/最も効率的なアプローチを決定するためにいくつかのプロファイリングを行うべきです。

-- 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 

また、項目を個別に更新するのではなく、一括ステータス更新を容易にするために、一意のバッチ番号をテーブルに追加することをお勧めします。

+0

ありがとう!あなたがそれは私の解決策の100%ではないが、それは私に良い方向を与えた、あなたが偉大なものを与えた...)... –

+0

好奇心の外に、他に何をする必要がありましたか?更新されたIDを同時にロックして取得することは、バッチ処理に必要なすべてのものでなければなりません。 – Leigh

+0

こんにちは@Leigh、遅れて申し訳ありません。レコードを選択 - >最初に更新する - >更新されたレコードのみを取り出すこれまでのところよく働いています。 –