トランザクションの分離は、データベース処理の基礎の 1 つです。 分離とは、頭字語の ACID における I です。分離レベルは、複数のトランザクションで変更が行われ、クエリーが同時に実行される場合に、パフォーマンスと信頼性、一貫性および結果の再現性のバランスを微調整する設定です。
InnoDB
では、SQL:1992 標準に記載された 4 つのトランザクション分離レベル (READ UNCOMMITTED
、READ COMMITTED
、REPEATABLE READ
、SERIALIZABLE
) がすべて提供されます。 InnoDB
のデフォルトの分離レベルは REPEATABLE READ
です。
ユーザーは SET TRANSACTION
ステートメントを使用して単一のセッションまたは後続のすべての接続の分離レベルを変更できます。 すべての接続に対するサーバーのデフォルトの分離レベルを設定するには、コマンド行上、またはオプションファイル内で --transaction-isolation
オプションを使用します。 分離レベルおよびレベル設定構文についての詳細は、セクション13.3.7「SET TRANSACTION ステートメント」を参照してください。
InnoDB
は、ここで説明されている各トランザクション分離レベルを、異なるロックの方法を使用してサポートしています。 ACID 準拠が重要な要件である重要なデータに対する操作の場合は、デフォルトの REPEATABLE READ
レベルを使用して高度な一貫性を適用できます。 あるいは、正確な一貫性や繰り返し可能な結果がロックのためのオーバーヘッドの量の最少化ほど重要でない一括レポートなどの状況では、READ COMMITTED
や場合によっては READ UNCOMMITTED
を使用して一貫性のルールを緩和できます。 SERIALIZABLE
は REPEATABLE READ
よりさらに厳密なルールを適用し、主に XA トランザクションのほか、並列性やデッドロックに関する問題のトラブルシューティングなどの特殊な状況で使用されます。
次のリストは、MySQL が各種のトランザクションレベルをどのようにサポートするかについて説明しています。 このリストは、もっとも一般的に使用されるレベルから使用頻度の低い順に並べられています。
-
これが
InnoDB
のデフォルトの分離レベルです。 同じトランザクション内の Consistent reads は、最初の読取りによって確立された snapshot を読み取ります。 つまり、同じトランザクション内で複数のプレーン (非ロック)SELECT
ステートメントを発行すると、これらのSELECT
ステートメントも互いに一貫性が保たれます。 セクション15.7.2.3「一貫性非ロック読み取り」を参照してください。locking reads (
FOR UPDATE
またはFOR SHARE
のあるSELECT
)、UPDATE
およびDELETE
ステートメントの場合、ロックはステートメントが一意の検索条件を持つ一意のインデックスを使用するか、範囲タイプの検索条件を使用するかによって異なります。一意の検索条件を使用した一意のインデックスの場合、
InnoDB
は見つかったインデックスレコードのみをロックし、その前にあるギャップはロックしません。その他の検索条件の場合、
InnoDB
は、ギャップロックまたはネクストキーロックを使用して、範囲に含まれるギャップへのほかのセッションによる挿入をブロックすることによって、スキャンされたインデックス範囲をロックします。 ギャップロックおよびネクストキーロックについては、セクション15.7.1「InnoDB ロック」 を参照してください。
-
各読取り一貫性は、同じトランザクション内であっても、独自の新しいスナップショットを設定して読み取ります。 読取り一貫性の詳細は、セクション15.7.2.3「一貫性非ロック読み取り」 を参照してください。
ロック読取り (
FOR UPDATE
またはFOR SHARE
を使用したSELECT
)、UPDATE
ステートメントおよびDELETE
ステートメントの場合、InnoDB
はインデックスレコードのみをロックし、その前のギャップはロックしないため、ロックされたレコードの横に新しいレコードを自由に挿入できます。 ギャップロックは、外部キー制約チェックおよび重複キーチェックにのみ使用されます。ギャップロックが無効になっているため、他のセッションがギャップに新しい行を挿入できるため、ファントムの問題が発生する可能性があります。 ファントムの詳細は、セクション15.7.4「ファントム行」 を参照してください。
READ COMMITTED
分離レベルでは、行ベースのバイナリロギングのみがサポートされます。READ COMMITTED
をbinlog_format=MIXED
とともに使用する場合、サーバーは自動的に行ベースのロギングを使用します。READ COMMITTED
の使用には、追加の効果があります:UPDATE
またはDELETE
ステートメントでは、InnoDB
は更新または削除の対象となる行に対してのみ、ロックを保持します。 一致しなかった行のレコードロックは、MySQL によるWHERE
条件の評価後に解除されます。 これにより、デッドロックの可能性が大幅に低くなりますが、まだ発生する可能性はあります。UPDATE
ステートメントである行がすでにロックされていた場合、InnoDB
は 「半一貫性」 読み取りを実行し、最後にコミットされたバージョンを MySQL に返すため、MySQL はその行がUPDATE
のWHERE
条件に一致するかどうかを判断できます。 その行が一致した場合 (その行を更新する必要がある場合)、MySQL はその行を再度読み取り、InnoDB
は今度はその行をロックするか、その行のロックが解除されるまで待機します。
次のような例について、このテーブルから検討します。
CREATE TABLE t (a INT NOT NULL, b INT) ENGINE = InnoDB; INSERT INTO t VALUES (1,2),(2,3),(3,2),(4,3),(5,2); COMMIT;
この場合、テーブルにはインデックスがないため、検索およびインデックススキャンでは、インデックス付けされたカラムではなく、非表示のクラスタインデックスをレコードロックに使用します (セクション15.6.2.1「クラスタインデックスとセカンダリインデックス」 を参照)。
あるセッションで、次のステートメントを使用して
UPDATE
を実行するとします:# Session A START TRANSACTION; UPDATE t SET b = 5 WHERE b = 3;
また、最初のセッションのステートメントの後に次のステートメントを実行して、別のセッションで
UPDATE
を実行するとします:# Session B UPDATE t SET b = 4 WHERE b = 2;
InnoDB
は各UPDATE
を実行する際に、まず各行の排他ロックを取得し、次にその行を変更するかどうかを判断します。InnoDB
は、行を変更しない場合、ロックを解放します。 それ以外の場合、トランザクションが終了するまでInnoDB
はそのロックを保持します。 これにより、トランザクション処理が次のような影響を受けます。デフォルトの
REPEATABLE READ
分離レベルを使用する場合、最初のUPDATE
は読取り対象の各行に対して x ロックを取得し、それらのいずれも解放しません:x-lock(1,2); retain x-lock x-lock(2,3); update(2,3) to (2,5); retain x-lock x-lock(3,2); retain x-lock x-lock(4,3); update(4,3) to (4,5); retain x-lock x-lock(5,2); retain x-lock
次のように、2 番目の
UPDATE
は (1 番目の更新がすべての行のロックを保持しているため)、ロックを取得しようとしてもすぐにブロックされ、1 番目のUPDATE
がコミットまたはロールバックを実行するまで続行されません。x-lock(1,2); block and wait for first UPDATE to commit or roll back
かわりに
READ COMMITTED
が使用されている場合、最初のUPDATE
は読み取る各行で x ロックを取得し、変更しない行の X ロックを解放します:x-lock(1,2); unlock(1,2) x-lock(2,3); update(2,3) to (2,5); retain x-lock x-lock(3,2); unlock(3,2) x-lock(4,3); update(4,3) to (4,5); retain x-lock x-lock(5,2); unlock(5,2)
2 番目の
UPDATE
では、InnoDB
は 「semi-consistent」 読取りを実行し、MySQL に読み取られた各行の最新のコミット済バージョンを返して、MySQL がその行がUPDATE
のWHERE
条件に一致するかどうかを判断できるようにします:x-lock(1,2); update(1,2) to (1,4); retain x-lock x-lock(2,3); unlock(2,3) x-lock(3,2); update(3,2) to (3,4); retain x-lock x-lock(4,3); unlock(4,3) x-lock(5,2); update(5,2) to (5,4); retain x-lock
ただし、
WHERE
条件にインデックス付けされたカラムが含まれており、InnoDB
でインデックスが使用されている場合は、レコードロックを取得および保持するときにインデックス付けされたカラムのみが考慮されます。 次の例では、最初のUPDATE
は、b = 2 の各行で x ロックを取得して保持します。 2 番目のUPDATE
は、カラム b に定義されたインデックスも使用するため、同じレコードで x ロックを取得しようとするとブロックされます。CREATE TABLE t (a INT NOT NULL, b INT, c INT, INDEX (b)) ENGINE = InnoDB; INSERT INTO t VALUES (1,2,3),(2,2,4); COMMIT; # Session A START TRANSACTION; UPDATE t SET b = 3 WHERE b = 2 AND c = 3; # Session B UPDATE t SET b = 4 WHERE b = 2 AND c = 4;
READ COMMITTED
分離レベルは、起動時に設定することも、実行時に変更することもできます。 実行時に、すべてのセッションに対してグローバルに設定することも、セッションごとに個別に設定することもできます。 -
SELECT
ステートメントは非ロックの方法で実行されますが、以前のバージョンの行が使用される可能性もあります。 そのため、この分離レベルを使用すると、このような読み取りには一貫性がありません。 これは、ダーティー読み取りとも呼ばれます。 そうでなければ、この分離レベルはREAD COMMITTED
のように機能します。 -
このレベルは
REPEATABLE READ
と似ていますが、autocommit
が無効になっている場合、InnoDB
はすべてのプレーンSELECT
ステートメントをSELECT ... FOR SHARE
に暗黙的に変換します。autocommit
が有効な場合、SELECT
は独自のトランザクションです。 したがって、読み取り専用であることがわかっているため、一貫性のある (非ロック) 読み取りとして実行された場合は直列化することができ、ほかのトランザクションのためのブロックは必要ありません。 (選択した行が他のトランザクションによって変更された場合にプレーンSELECT
を強制的にブロックするには、autocommit
を無効にします。)注記MySQL 8.0.22 の時点では、MySQL からデータを読み取る DML 操作では、分離レベルに関係なく、(結合リストまたはサブクエリーを介して) テーブルが付与されますが、変更はされません。 詳細は、テーブル同時実行性の付与を参照してください。