1

基本的にETLタスクを実行するlinq-to-sqlプログラムを作成しましたが、並列化によってパフォーマンスが向上する場所がたくさんあることに気づきました。ただし、2つのスレッドが次のタスク(疑似コード)を実行するときに、一意性制約違反を防ぐことが心配です。

Record CreateRecord(string recordText)
{
    using (MyDataContext database = GetDatabase())
    {
        Record existingRecord = database.MyTable.FirstOrDefault(record.KeyPredicate());
        if(existingRecord == null)
        {
            existingRecord = CreateRecord(recordText);
            database.MyTable.InsertOnSubmit(existingRecord);
        }

        database.SubmitChanges();
        return existingRecord;
    }
}

一般に、このコードは、SELECTレコードの存在をテストするステートメントを実行し、その後にINSERTレコードが存在しない場合はステートメントを実行します。暗黙のトランザクションによってカプセル化されます。

2つのスレッドがの同じインスタンスに対してこのコードを実行する場合recordText、レコードが存在しないことを同時に判断して、両方が同じレコードを作成しようとするのを防ぎたいと思います。分離レベルと明示的なトランザクションはうまく機能しますが、どの分離レベルを使用Serializableすべきかわからない場合は、機能するはずですが、厳密すぎるようです。より良い選択肢はありますか?

4

1 に答える 1

1

このような状況を回避するために、以下に示すようなSQLを使用します。 UPDLOCKトランザクションが完了するまで更新ロックを取得して保持することを指定します。これは。HOLDLOCKと同等SERIALIZABLEです。 SERIALIZABLEトランザクションが完了したかどうかに関係なく、必要なテーブルまたはデータページが不要になるとすぐに共有ロックを解放するのではなく、トランザクションが完了するまで共有ロックを保持することで、共有ロックをより制限的にします。SERIALIZABLEスキャンは、分離レベル で実行されているトランザクションと同じセマンティクスで実行されます。HOLDLOCK指定されたテーブルまたはビューにのみ適用され、それが使用されているステートメントによって定義されたトランザクションの期間のみ適用され ます。オプションを含むステートメントではHOLDLOCK使用できません。SELECTFOR BROWSE

declare @LocationID          int
declare @LocationName        nvarchar (50)

/* fill in LocationID and LocationName appropriately */

INSERT dbo.Location
(LocationID, LocationName)
SELECT @LocationID, @LocationName
WHERE NOT EXISTS (
   SELECT L.*
   FROM dbo.Location L WITH (UPDLOCK, HOLDLOCK)
   WHERE L.LocationID = @LocationID)

この質問への回答によると、Serializableが進むべき道のようです。

于 2010-05-20T22:05:48.123 に答える