2009-02-22 4 views
5

私は1レコードバックを選択するストアドプロシージャを持っています。ストアドプロシージャは、異なるPC上の複数の異なるアプリケーションから呼び出すことができます。考えられるのは、ストアドプロシージャが処理する必要のある次のレコードを戻し、2つのアプリケーションが同時にストアドプロシージャを呼び出す場合、同じレコードを戻すべきではないということです。私の質問は以下の通りですが、私はできるだけ効率的に質問を書こうとしています(sql 2008)。これよりも効率的に行うことができますか?効率的なトランザクション、レコードロック

CREATE PROCEDURE GetNextUnprocessedRecord 
AS 
BEGIN 
    SET NOCOUNT ON; 

    --ID of record we want to select back 
    DECLARE @iID BIGINT  

    -- Find the next processable record, and mark it as dispatched 
    -- Must be done in a transaction to ensure no other query can get 
    -- this record between the read and update 
    BEGIN TRAN 

     SELECT TOP 1 
      @iID = [ID] 
     FROM 
      --Don't read locked records, only lock the specific record 
      [MyRecords] WITH (READPAST, ROWLOCK) 
     WHERE 
      [Dispatched] is null 
     ORDER BY 
      [Received] 

     --Mark record as picked up for processing 
     UPDATE 
      [MyRecords] 
     SET 
      [Dispatched] = GETDATE() 
     WHERE 
      [ID] = @iID  

    COMMIT TRAN 

    --Select back the specific record 
    SELECT 
     [ID], 
     [Data] 
    FROM  
     [MyRecords] WITH (NOLOCK, READPAST) 
    WHERE 
     [ID] = @iID 

END 
+0

でそれをすべて行い、この構造を使用することができ、トランザクションすぎるインデックスをフィルタリング安全... –

+0

SELECTの後でUPDATEの前にWAITFOR DELAYを0:2:0にして、SPを実行し、別の接続から同じSPを実行してみてください... –

+0

実際には、私は間違っています! HOLDLOCKはMyRecordsテーブルのREPEATABLEREADと同じ効果を持ちます。 –

答えて

3

READPASTのロックヒントが正しいと、SQLが正常に見えます。

私は、これはあなたがIDを得ることを意味し、排他的にあなたが運ぶ間、その行をロックして、それを更新するも

... 
[MyRecords] WITH (READPAST, ROWLOCK, XLOCK) 
... 

SERIALIZABLE HOLDLOCK /であるにもかかわらず使用XLOCKを追加すると思います。

編集:DispatchedカラムとReceivedカラムのインデックスを追加すると、そのインデックスをすばやく作成できます。 [ID](私はそれがPKであると仮定します)がクラスタ化されていない場合、[ID]をインクルードします。それはSQL 2008

だから、あなたも、私はこのTSQLを確信していない1 XLOCKなしで行くかHOLDLOCK

UPDATE 
    MyRecords 
SET 
    --record the row ID 
    @id = [ID], 
    --flag doing stuff 
    [Dispatched] = GETDATE() 
WHERE 
    [ID] = (SELECT TOP 1 [ID] FROM MyRecords WITH (ROWLOCK, READPAST) WHERE Dispatched IS NULL ORDER BY Received) 

UPDATE, assign, set in one

+0

すばらしいヒント! XLOCKのヒントを追加し、ReceivedとDispatchedに非クラスタ化インデックスを作成し、「Dispatched IS NULL」のフィルタを含むIDを追加しました。これはID上のクラスタ化インデックスに追加されています。私はまだ1つのUpdateアサインセットが速いかどうかを判断しようとしています。 – Jeremy

+1

ありがとうございます。私は、完全なパフォーマンスよりもトランザクションの整合性についてもっと心配しています。この1つのステートメントは、XLOCKの必要性を取り除きます。 – gbn

0

各ピッカープロセスに一意のIDを割り当て、列にpickerprocとpickstateを追加することができます。次いで

UPDATEのMyRecords
SETのpickerproc = MYPROC、
pickstate = 'I' - のための「I'n処理
WHERE ID =(SELECT MyRecords FROM MAX(ID)WHERE pickstate = 'A' ) - 'A'vailable

これはあなたにあなたの記録を1アトムステップで取得し、あなたの余暇で残りの処理を行うことができます。次にピックステートを「C'omplete」、「E'rror」などに設定することができます。

私はMitchがメッセージキューテーブルを作成してそこにIDを挿入する別の優れたテクニックを指していると思います。いくつかのSOスレッドがあります。「メッセージキューテーブル」を検索します。

+0

ではなく、トランザクションをSERIALIZABLEにする必要があると思います! –

+0

それはなぜでしょうか?あなたがそれを変更するまで、ピックステートAで同じMAX(Id)を引き渡すつもりはありません。おそらく10^6 +インスタンスに基づく私の経験ではありません。 – dkretz

+0

..そして、どちらも効率的ではありません –

0

MyRecordsを「MEMORY」テーブルに保存すると、処理が高速になります。

+0

テーブルがかなり大きくなる可能性があります。さらに、サーバーがクラッシュするとどうなりますか? – Jeremy