web-dev-qa-db-ja.com

SQL Server-READCOMMITEDとNOLOCKを同時に実現する方法

serテーブルがある場合:

_id | name   | age
1  | Mateus | 27
_

最初のトランザクションは更新を実行し、コミットまたはロールバックせずにトランザクションを開いたままにします。
_update User set name = 'John' where id = 1;_

一方、2番目のトランザクションは選択を実行します。
_select * from User where id = 1;_
次のように2番目のトランザクションがテーブルヒントwith(nolock)を使用しない限り、このコマンドは最初のトランザクションがコミットまたはロールバックによってロックを解放するまで待機します。
select * from User with(nolock) where id = 1;
これにより、トランザクションをロックせずにレコードが返されますが、元のJohnではなく、コミットされていない値Mateusが返されます。

私が知っていることから、現在のトランザクションをロックせずにロックされたレコードを返す方法は2つしかありません。レコードを返すwith(nolock)を使用できますが、コミットされていない値を使用し、with(readpast)それは単にレコードを返しません。

テーブルをロックせずに「古い」値を返すことなく、レコードを返す方法はありますか?

5
Mateus Viccari

探しているのは、スナップショット分離や読み取りコミットスナップショット分離などの楽観的な分離レベルです。

コード例:

USE Crap;

CREATE TABLE dbo.users (id INT, username NVARCHAR(40));

INSERT dbo.users ( id, username )
VALUES ( 1, N'Jimbo' )

/*To turn on Snapshot*/
ALTER DATABASE Crap SET ALLOW_SNAPSHOT_ISOLATION ON;

/*To turn on RCSI*/
ALTER DATABASE Crap SET READ_COMMITTED_SNAPSHOT ON;

UPDATE dbo.users 
SET username = 'Dimbo'
WHERE id = 1;

/*Snapshot needs this, RCSI doesn't*/
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
SELECT *
FROM dbo.users AS u
WHERE u.id = 1;

注意事項:

  • 行のバージョン管理 tempdbでスペースを使用 (SQL Server 2019を除く Accelerated Database Recovery が構成されている場合)-バージョンは、ユーザーデータベースと共に、行内または永続バージョンストアに保存されます)
  • 競合状態 がある場合があります。ここで、キューイングのロックに依存します。

違い

スナップショット分離とRCSIの重要な違いの1つは、トランザクション内です。

  • スナップショット分離では、BEGIN TRANはトランザクション内のすべてのクエリがバージョンストアから読み取るポイントをマークします。

  • RCSIでは、BEGIN TRANの後の各ステートメントは、ステートメントが実行された時点でバージョンストアを読み取ります。

もう1つの違いは、スナップショット分離が変更クエリに適用できることですが、RCSIは適用できません。より正確には、SIは書き込みの競合を検出し、競合するトランザクションの1つを自動的にロールバックします。 RCSIでの更新 更新するデータを見つけるときに行バージョンを使用しませんが、これはターゲットテーブルにのみ適用されます。ターゲットテーブルへの追加の参照を含む、同じ削除または更新ステートメント内の他のテーブルは、引き続き行バージョンを使用します。

14
Erik Darling