2

SQL Server テーブルに行を挿入/更新しようとしています (存在するかどうかによって異なります)。複数のマシンで複数のスレッドから SQL を実行していますが、重複キー エラーが発生するのを避けたいと考えています。

オンラインで多くの解決策を見つけましたが、それらはすべてトランザクションのデッドロックを引き起こしています。これは私が使ってきた一般的なパターンです:

BEGIN TRANSACTION

UPDATE TestTable WITH (UPDLOCK, SERIALIZABLE)
SET Data = @Data 
WHERE Key = @Key

IF(@@ROWCOUNT = 0)
BEGIN
     INSERT INTO TestTable (Key, Data)
     VALUES (@Key, @Data)
END

COMMIT TRANSACTION

私が試してみました:

  • WITH XLOCKそれ以外のUPDLOCK
  • SET TRANSACTION ISOLATION LEVEL SERIALIZABLE最初にUPDLOCK
  • SET TRANSACTION ISOLATION LEVEL SERIALIZABLEテーブルヒントなし

上記のすべての組み合わせで次のパターンも試しました。

BEGIN TRANSACTION

IF EXISTS (SELECT 1 FROM TestTable WITH (UPDLOCK, SERIALIZABLE) WHERE Key=@Key) 
BEGIN
    UPDATE TestTable
    SET Data = @Data 
    WHERE Key = @Key
END
ELSE
BEGIN
    INSERT INTO TestTable (Key, Data)
    VALUES (@Key, @Data)
END

COMMIT TRANSACTION

デッドロックなしで動作させる唯一の方法は、を使用することWITH (TABLOCKX)です。

SQL Server 2005 を使用しています。SQL は実行時に生成されるため、ストアド プロシージャには含まれず、一部のテーブルでは主キーではなく複合キーが使用されますが、整数の主キーを持つテーブルで再現できます。

サーバーログは次のようになります。

waiter id=processe35978 mode=RangeS-U requestType=wait
waiter-list
owner id=process2ae346b8 mode=RangeS-U
owner-list
keylock hobtid=72057594039566336 dbid=28 objectname=TestDb.dbo.TestTable indexname=PK_TestTable id=lock4f4fb980 mode=RangeS-U associatedObjectId=72057594039566336
waiter id=process2ae346b8 mode=RangeS-U requestType=wait
waiter-list
owner id=processe35978 mode=RangeS-U
owner-list
keylock hobtid=72057594039566336 dbid=28 objectname=TestDb.dbo.TestTable indexname=PK_TestTable id=lock2e8cbc00 mode=RangeS-U associatedObjectId=72057594039566336

モードは、使用するテーブル ヒントによって明らかに異なります (ただし、プロセスは、既に所有しているモードを常に待機しています)。RangeS-U、RangeX-X、および U を見てきました。

私は何を間違っていますか?

4

3 に答える 3

2

テーブルの存在を確認するために、最初にテーブルの結合を使用して挿入を行うのはどうですか:

BEGIN TRANSACTION

WITH ToInsert AS(
     SELECT @Key AS Key, @Data AS Data
)
INSERT INTO TestTable (Key, Data)
SELECT ti.Key, ti.Data
FROM ToInsert ti
LEFT OUTER JOIN TestTable t
     ON t.Key = ti.Key
WHERE t.Key IS NULL

IF(@@ROWCOUNT = 0)
BEGIN
     UPDATE TestTable WITH (UPDLOCK, SERIALIZABLE)
     SET Data = @Data 
     WHERE Key = @Key
END

COMMIT TRANSACTION

そうすれば、UPDATE ステートメントには常にレコードが存在し、INSERT と INSERT-check は 2 つの別個のステートメントではなく、同じアトミック ステートメント内にあることが保証されます。

于 2012-09-26T09:34:00.180 に答える
0

デッドロックはインデックス リソースにあります。

実行計画では、ブックマーク/キー検索を探し、それらのフィールドをカバーする非クラスター化インデックスを作成します。これにより、UPDATE のデータの「読み取り」が INSERT の「書き込み」と競合しなくなります。

于 2012-05-24T13:43:34.040 に答える