トランザクション隔離性水準 とも言うらしい。「複数のトランザクションが同時に実行されたとき、お互いの変更内容がどこまで見えてしまってよいか(=互いにどれだけ干渉を許すか)の安全基準」のことと考えてよいです。
データベースで複数人が同時にデータを更新・参照すると、タイミングによって以下のような「3つの不都合な現象(データの矛盾)」が発生しますが、ANSI SQL規格ではこれら不都合な現象を対策するに以下分離レベルが定義されています。
3つの不都合な現象
1.ダーティリード(Dirty Read)
- 他人がまだcommitしていない(確定していない)変更途中のデータを読み込んでしまう現象。rollbackされるかもしれないデータに基づいて処理をすすめてしまうかもしれないという危険性がある。(Oracle/PostgreSQL/MySQLではMVCCにより、未コミットの行をselectが参照しないので発生しません)
セッションBのupdateはまだcommitしていない(確定していない)にもかかわらず、セッションAで未コミットである20が見えてしまう。本来であれば、最後に確定している10が見えるべき。
2.不可読読み(Non-repeatable Read)
- 同一トランザクション内で同じデータを2回読み込んだ時、他セッションによるupdateやdeleteが行われることで1回目、2回目でデータ内容が変わってしまう現象。select対象項目がA→Bになってしまったり、A→NULL(deleteによる)に変化する現象の事です。一般的にselect for updateで対策されている事多く見ます。
セッションAで同一トランザクションの最初に読んだ値20が、再度読んだタイミングでは、セッションBによる書き換えで30に見えてしまう。本来同一トランザクション内であれば同一の20が見えるべき。
3.ファントムリード(Phantom Read)
- 同一トランザクション内で同じデータを2回読み込んだ時、1回目、2回目でデータ内容が変わってしまう現象。他のセッションからINSERT/DELETEが行われる事で発生する事象。(こちらは行の増減に起因する事なのでselect for updateでも対策できない)
セッションAで同一トランザクションの最初に読んだ2レコードが、再度読んだタイミングでは、セッションBによるINSERTで3レコードに見えてしまう。本来同一トランザクション内であれば2レコードのままであるべき。
ANSI SQL分離レベル
| # | 分離レベル | 分離レベル解説 | ダーティリード | 不可読読み | ファントムリード |
|---|---|---|---|---|---|
| 1 | Read Uncommitted | 他のトランザクションがまだ確定(コミット)していない変更データも読みこむ。処理速度を最優先にする事で最も制限は緩くなる。 | 発生する | 発生する | 発生する |
| 2 | Read Committed | 確定したデータのみ読み取ります。他のトランザクションが編集中(未確定)のデータは読めません | 防ぐ | 発生する | 発生する |
| 3 | Repeatable Read | 同一トランザクション内であれば何度読んでも同じ値が保証されるがレコードの追加や削除は保証されない | 防ぐ | 防ぐ | 発生する |
| 4 | Serializable | 複数のトランザクションを完全に順番通り(直列化)に処理し、すべての矛盾やデータの読み違えを防ぎます。ただし処理速度は最も低下します | 防ぐ | 防ぐ | 防ぐ |
RDBMS毎のデフォルトの分離レベルとデフォルト時のselectの挙動
| RDBMS | デフォルト設定 | selectの挙動 |
|---|---|---|
| PostgreSQL | READ COMMITTED | トランザクションの有無にかかわらず最新のコミットデータをロックの影響なく読める |
| Oracle | READ COMMITTED | トランザクションの有無にかかわらず最新のコミットデータをロックの影響なく読める |
| DB2(注1) | READ COMMITTED 注2 | トランザクションの有無にかかわらず最新のコミットデータを読むがXロックにあたると待たされる。WITH UR 句を使う事で待ちは回避される事になるが、その場合未コミットの値を参照する可能性がある |
| MySQL(InnoDB) | REPEATABLE READ | トランザクションを使った通常の SELECT であれば、トランザクション開始時の断面を見せる(MVCC)ことで不可読読みもファントムリードも対策できます。 過去の断面ではなく現在の最新データを取得し、それを確定維持したい場合は SELECT … FOR UPDATE を使用します。これにより内部的に「ギャップロック」が働き、 他セッションからの割り込み INSERT を物理的にブロックして最新状態を維持しファントムリードを対策します。 (※ Oracle や PostgreSQL にも SELECT … FOR UPDATE は存在しますが、ギャップロックという仕組みがないため割り込みINSERTを対策できません) |
| SQL Server | READ COMMITTED (shared lock 方式) | トランザクションの有無を問わずSELECTはXロックにあたると待たされる。 |
| ※MVCC方式に切り替える事も可能 | トランザクションの有無にかかわらず最新のコミットデータをロックの影響なく読める |
(注1) DB2 for i(AS 400)/DB2 for LUW とあってデフォルトの分離レベルは同じと考えていいがそれぞれ内部アーキテクチャが異なるので細かい挙動は異なってくる。
(注2)ダーティリードを防ぐという意味ではREAD COMMITTED 相当と言われているが、その挙動はOracleやPostgreSQLとは異なる。DB2のマニュアルではCURSOR STABILITY と表記されている。select時であってもカーソルが乗っている行はロックがかかる。フルスキャンが発生した場合先頭行から順次ロック、アンロックが最終行まで行われる。さらにはカーソルが移動したら本来すぐさまアンロックされるべきであるが、やや遅延しつつアンロックされているらしい。(for iでそれが顕著にでるみたい)そのため、別セッションで更新(Xロック)が入っている場合、selectであっても待たされる事になります。(OracleやPostgreSQLユーザからすると驚かれるとおもいますが、、)これを対策するにWITH UR 句を使うわけですが、この場合未コミットのデータを見る事になります。
(注3) READ_COMMITTED_SNAPSHOT オプションによって 2種類の全く異なる動き方 が存在します。shared lock 方式がデフォルトですが、RCSI有効とすることで、Oracle/PostgreSQL/MySQLのようなMVCC方式に切り替えて利用可能です。この時tempdbにundo領域が確保される事で実現できます。
ロックの基本
Sロック(Shared Lock:共有ロック/参照ロック)
データを読み取っている間、他人に変更させないためのロック。SロックがされているレコードにはXロックはできない(待たされる)。Sロックに対するSロックは待たされることなく可能。
Xロック(Exclusive Lock:排他ロック/専有ロック)
データを変更(UPDATE / DELETE / INSERT)している間、他人に読み書きさせないため」のロックです。(MVCCの仕組みをもつOracleやPostgreSQLでは、Xロックがかかっていてもselectはできます)
Uロック(Update Lock:更新予約ロック)
updateを行う際、where句で指定した対象行に対しいったん全てUロックがかかり、そのあと順次Xロックにエスカレーションしていく。Uロックの間は別セッションから参照できるけれど、Xロックにエスカレーションしたら参照もできなくなる。(DB2等のロックベースの場合)
