【ACID 特性】Isolation (分離性) をわかりやすく解説

Isolation (分離性)Isolation とは、他のトランザクション処理の影響を受けない特性です。
ACID 特性の記事一覧
データベースの基礎知識
スポンサーリンク

リード現象 (Read Phenomena)

リード現象リード現象とは、読み取り時に、他のトランザクションの影響によって発生する現象です。

一般的なリード現象は次の 3 つです。

ダーティーリード

ダーティーリードとは、COMMIT していない別のトランザクションの結果が見える現象です

ノンリピータブルリード

ノンリピータブルリードとは、別のトランザクション更新により、同じデータを読み取っても、異なる値を取得する現象です。

トランザクションが分離されているなら、右側は COMMIT するまで同じ値を返すべきである。
(右側は COMMIT するまで、左側のトランザクションの影響を受けないことが期待される)

ファントムリード

ファントムリードとは、別のトランザクション追加・削除により、同じ検索条件でも、取得する行数が異なる現象です。
スポンサーリンク

リード現象の対策

リード現象を防ぐために、RDBMS には「MVCC」と「ロック」の2つの機能があります。

MVCC (MultiVersion Concurrency Control) とは

MVCC とは、変更前の行 (スナップショット) を Undo ログに残す仕組みです。

MVCC は、行に以下の3つのフィールドを追加することで実現します。

フィールド説明
DB_TRX_ID行を挿入/更新(削除)したトランザクションの識別子
DB_ROLL_PTRUndo レコードへのポインタ
DB_ROW_ID行の識別子

MVCC では、過去のスナップショット (変更前の行) を読み取ることで、リード現象を防ぎます。

自分の DB_TRX_ID より新しい行は見えない (DB_ROLLPTR で更新 Undo ログを遡る)

なお、Undo ログロールバックでも利用します。

ロックとは

ロックとは、競合/不整合を防ぐために、読み書きを制限することです。

先にロックが取得されている場合は、後続はロックの解除を待つ必要があります。

ロックの範囲

ロックの範囲 (LOCK_TYPE) には、行ロックと、テーブルロックが存在します。

ロックの種類

ロックの種類 (LOCK_MODE) には、次の2種類が存在します。

ロックの種類該当する SQL説明
共有ロック (S)SELECT ... FOR SHARE読み取りのため
排他ロック (X)SELECT ... FOR UPDATE/INSERT/UPDATE/DELETE読み書きのため

ロックの取得条件

すでに誰かがロックを取得している場合、後から取得するロックには、次の制限があります。

先/後共有ロック (S)排他ロック (X)
共有ロック (S)×
排他ロック (X)××

つまり、誰かが読み取り中のレコードは書き込めませんし、誰かが書き込み中のレコードは読み書きできません。(ロックが解放されるまで待つ必要があります)

これにより、リード現象を防ぎます。

ロック読み取りと一貫性読み取り

ロックの解除待ちが発生するため、ロックはなるべくしたくないです。

そのため、ロックを取得しない一貫性読み取り (SELECT 文) が存在します。

読み取りの種類SQLMVCC 読み取り先ロック
一貫性読み取り
(Consistent Read)
SELECTスナップショットロックしない
ロック読み取り
(Locking Read)
SELECT ... FOR SHARE現在のテーブル共有ロック
SELECT ... FOR UPDATE現在のテーブル排他ロック

一貫性読み取りでは、ロックの代わりに MVCC のスナップショットでリード現象を防ぎます。

スポンサーリンク

分離レベル

分離レベル分離レベルとは、リード現象をどの程度許容するか定義したものです。

トランザクションを並行で実行すると、他のトランザクションでリード現象が発生します。

一方で、パフォーマンスの観点から、トランザクションは可能な限り並行で実行したいです。

そのため、分離レベルを用いて、パフォーマンスと Isolation のバランスを決定します。

分離レベルダーティーリードノンリピータブルリードファントムリード
READ UNCOMMITTED発生する発生する発生する
READ COMMITTED発生しない発生する発生する
REPEATABLE READ発生しない発生しない△※1
SERIALIZABLE発生しない発生しない発生しない
※1 ANSI では「発生する」。
MySQL (InnoDB) の場合は、一貫性読み取りとロック読み取りの1種類だけだと「発生しない」。組み合わせた場合は「発生する」
一貫性読み取りは MVCC, ロック読み取りはギャップロック/ネクストキーロックが防ぐ

なお、分離レベルは以下のグローバル変数・セッション変数で確認可能です。

select @@global.tx_isolation, @@session.tx_isolation;
+-----------------------+------------------------+
| @@global.tx_isolation | @@session.tx_isolation |
+-----------------------+------------------------+
| REPEATABLE-READ       | REPEATABLE-READ        |
+-----------------------+------------------------+

SERIALIZABLE

SERIALIZABLE とは、直列に実行 (各トランザクションを順番に順番) した場合と、同じ結果を保証する分離レベルです。

SERIALIZABLE は、全てのリード現象を防ぎます。

MySQL (InnoDB) の SERIALIZABLE では、すべての SELECT 文で共有ロックを取得 (SELECT ... FOR SHARE に変換) することで SERIALIZABLE を保証します。

SERIALIZABLE
このレベルは REPEATABLE READ と似ていますが、autocommit が無効になっている場合、InnoDB はすべてのプレーン SELECT ステートメントを SELECT ... FOR SHARE に暗黙的に変換します。 autocommit が有効な場合、SELECT は独自のトランザクションです。

https://dev.mysql.com/doc/refman/8.0/ja/innodb-transaction-isolation-levels.html

REPEATABLE READ

REPEATABLE READ とは、同じトランザクション内で SELECT を実行すると、常に同じ結果となることが保証されている分離レベルです。

REPEATABLE READ は、ANSI の定義上ファントムリードが発生します。

しかし、MySQL (InnoDB) の場合、「一貫性読み取り」と「ロック読み取り」が混在しない場合は、ファントムリードが発生しません。(混在する場合は発生します)

読み取りの種類SQLファントムリードを防ぐ方法
一貫性読み取り
(Consistent Read)
SELECT最初に SELECT を実行した時の
MVCC のスナップショット
ロック読み取り
(Locking Read)
SELECT ... FOR SHARE共有ロック
(ギャップロック/ネクストキーロック)
SELECT ... FOR UPDATE排他ロック
(ギャップロック/ネクストキーロック)
一意検索 (WHERE id = 1) ではレコードロック、範囲検索 (WHERE id > 5) ではギャップロック/ネクストキーロックを使う

READ COMMITTED

READ UNCOMMITTED とは、他のトランザクションCOMMIT した結果を読み取る分離レベルです。

READ COMMITTED では、ファントムリードとノンリピータブルリードが発生します。

※MySQL の READ COMMITTED では、以下の理由でファントムリードが発生
一貫性読み取りでは、常に最新のスナップショットを取得するため
・ロック読み取りでは、ギャップロックを行わないため

ファントムリードの動作

CREATE TABLE isolation(id INT);
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
INSERT INTO isolation VALUES(1);
SELECT * FROM isolation;
+------+
| id   |
+------+
|    1 |
+------+
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
SELECT * FROM isolation;
Empty set (0.00 sec)
SELECT * FROM isolation;
+------+
| id   |
+------+
|    1 |
+------+

ノンリピータブルリードの動作

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
UPDATE isolation SET id=2 WHERE id=1;
SELECT * FROM isolation;
+------+
| id   |
+------+
|    2 |
+------+
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
SELECT * FROM isolation;
+------+
| id   |
+------+
|    1 |
+------+
SELECT * FROM isolation;
+------+
| id   |
+------+
|    2 |
+------+

READ UNCOMMITTED

READ UNCOMMITTED とは、トランザクションが全く分離していない分離レベルです。

READ UNCOMMITTED では、ファントムリードとノンリピータブルリードに加えて、ダーティリードが発生します。

ダーティリードの動作

CREATE TABLE dirty(id INT);
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN;
INSERT INTO dirty VALUES(1);
SELECT * FROM dirty;
+------+
| id   |
+------+
|    1 |
+------+
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN;
SELECT * FROM dirty;
+------+
| id   |
+------+
|    1 |
+------+

最後に

関連記事

ACID 特性の記事一覧
データベースの基礎知識

参考記事