レベルエンター山本大のブログ

面白いプログラミング教育を若い人たちに

BLOCKVROCKリファレンス目次はこちら

SQLServer2005から新しく追加された「スナップショット分離レベル」の動作

スナップショット分離レベル(Snapshot)は、リードコミッテッド分離レベル(Read Committed)と同じロックの動作をします。
つまり、読み取りの操作時に、データに対して共有ロックをかけません。
スナップショットとリードコミッテッドの違うところは、反復読み取りの整合性です。
スナップショットでは、一度読み取ったデータは当該トランザクション中、ずっと同じ値で読み取ります。

■Read Committed分離レベル

タイミング トランザクションA トランザクションB
1 SELECT →値:1 SELECT →値:1
2 UPDATE →値:2 SELECT →値:1
3 SELECT →値:2 SELECT →値:2←反復読み取り時の不整合

■Snapshot分離レベル

タイミング トランザクションA トランザクションB
1 SELECT →値:1 SELECT →値:1
2 UPDATE →値:2 SELECT →値:1
3 SELECT →値:2 SELECT →値:1←Snapshot分離レベルによるトランザクション単位の一貫性

 
ただし、スナップショット分離レベルはトランザクション期間中ずっとバージョン情報を保持しなくてはならないため
tempdbのリソースをより多く消費します。

また、スナップショット分離レベルでは、更新の競合が発生した場合エラーとなります。
つまり、上記例ではタイミング3の時点で、
トランザクションBにおいて、トランザクションAが更新したデータを更新しようとしたときに
以下のようなエラーが発生することになります。

System.Data.SqlClient.SqlException: 更新の競合により、スナップショット分離トランザクションが中断しました。スナップショット分離を使用してデータベース 'KUAD_ST' のテーブル 'dbo.M194EmitNumber' に直接または間接的にアクセスし、別のトランザクションによって変更または削除された行を更新、削除、または挿入することはできません。トランザクションを再試行するか、更新/削除ステートメントの分離レベルを変更してください。
   場所 System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   場所 System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   場所 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
   場所 System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   場所 System.Data.SqlClient.SqlDataReader.HasMoreRows()
   場所 System.Data.SqlClient.SqlDataReader.ReadInternal(Boolean setTimeout)
   場所 System.Data.SqlClient.SqlDataReader.Read()
   場所 System.Data.SqlClient.SqlCommand.CompleteExecuteScalar(SqlDataReader ds, Boolean returnSqlValue)
   場所 System.Data.SqlClient.SqlCommand.ExecuteScalar()