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 を見てきました。
私は何を間違っていますか?