web-dev-qa-db-ja.com

IDENTITY_INSERTは並行性にどのように影響しますか?

投稿に不具合があり、サポートが終了したサードパーティのSAPアドオンを使用してお客様を支援しようとしています。

特定の状況下では、投稿キューテーブルから投稿アーカイブテーブルへの不完全な投稿がアーカイブされます。これらのアーカイブされた結果をキューに戻す必要があります。

キューIDはID列であり、同じにしたいと思います。

問題は、identity_insert on/insert/identity_insert offを実行した場合、キューエントリを作成し、identity列が自動的に生成されることを期待するプロセスとの同時実行性に関して何を期待できますか?

そのような振る舞いを実証するための最良の方法へのポインタも同様に大歓迎です。

11
Metaphor

設定IDENTITY_INSERT ONはそれ自体では並行性を排除しません-これはテーブルに排他ロックをかけず、簡単なスキーマ安定性(Sch-S)ロックのみを置きます。

理論的には、デフォルトの動作では、セッション1でこれを行うことができます。

BEGIN TRANSACTION;

-- 1
SET IDENTITY_INSERT dbo.tablename ON;

-- 2
INSERT dbo.tablename(id, etc) VALUES(100, 'foo'); -- next identity is now 101

-- 3
INSERT dbo.tablename(id, etc) VALUES(101, 'foo'); -- next identity is now 102

-- 4
SET IDENTITY_INSERT dbo.tablename OFF;

COMMIT TRANSACTION;

別のセッションでは、ポイント1、2、3、または4でテーブルに行を挿入できます。これはseemのようなものです。別のセッションによってトリガーされる自動生成値はステートメント2の結果に基づいているため、101が生成され、ステートメント3は主キー違反で失敗します。これは、いくつかのWAITFORsを使用して設定およびテストするのが非常に簡単です。

-- session 1
-- DROP TABLE dbo.what;
CREATE TABLE dbo.what(id INT IDENTITY PRIMARY KEY);
GO
BEGIN TRANSACTION;

SET IDENTITY_INSERT dbo.what ON;

INSERT dbo.what(id) VALUES(32);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(33);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(34);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(35);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(36);

SET IDENTITY_INSERT dbo.what OFF;

COMMIT TRANSACTION;

そのバッチが開始したら、別のウィンドウでこのバッチを開始します。

-- session 2
INSERT dbo.what DEFAULT VALUES;
WAITFOR DELAY '00:00:01';
GO 20

セッション2では、1〜20の値のみを挿入する必要があります。ただし、基になるIDが手動挿入セッション1によって更新されているため、ある時点で、セッション2はセッション1が終了した場所を取得し、32、33、または34などを挿入します。これは可能ですが、次に、セッション1は次の挿入でPK違反で失敗します(どちらが勝つかはタイミングの問題である場合があります)。

これを回避する1つの方法は、最初の挿入時にTABLOCKを呼び出すことです。

INSERT dbo.what WITH (TABLOCK) (id) VALUES(32);

これにより、アーカイブされた行の移動が完了するまで、他のユーザーがこのテーブルを挿入しようとする(実際には何もしない)ことがブロックされます。これは確かに同時実行性を抑制しますが、これがwantブロックの動作方法です。そして、うまくいけば、これはあなたがいつも他の人をブロックしているほど頻繁に起こっていることではありません。

他のいくつかの回避策:

  • 生成されたIDENTITYの値を気にしないでください。誰も気にしない?元の値が非常に重要な場合は、UNIQUEIDENTIFIERIDENTITYをサロゲートとして別のテーブルに生成する場合があります)を使用します。
  • アーカイブプロセスを変更して、「ソフト削除」を使用するようにします。この場合、何かが最初にアーカイブ済みとしてマークされ、アーカイブは後日まで永続化されません。次に、それらを元に戻そうとしているプロセスは、直接更新を実行して、ソフト削除フラグを修正するだけです。
8
Aaron Bertrand