MySQL 8.0 リファレンスマニュアル


15.7.2.1 トランザクション分離レベル

トランザクションの分離は、データベース処理の基礎の 1 つです。 分離とは、頭字語の ACID における I です。分離レベルは、複数のトランザクションで変更が行われ、クエリーが同時に実行される場合に、パフォーマンスと信頼性、一貫性および結果の再現性のバランスを微調整する設定です。

InnoDB では、SQL:1992 標準に記載された 4 つのトランザクション分離レベル (READ UNCOMMITTEDREAD COMMITTEDREPEATABLE READSERIALIZABLE) がすべて提供されます。 InnoDB のデフォルトの分離レベルは REPEATABLE READ です。

ユーザーは SET TRANSACTION ステートメントを使用して単一のセッションまたは後続のすべての接続の分離レベルを変更できます。 すべての接続に対するサーバーのデフォルトの分離レベルを設定するには、コマンド行上、またはオプションファイル内で --transaction-isolation オプションを使用します。 分離レベルおよびレベル設定構文についての詳細は、セクション13.3.7「SET TRANSACTION ステートメント」を参照してください。

InnoDB は、ここで説明されている各トランザクション分離レベルを、異なるロックの方法を使用してサポートしています。 ACID 準拠が重要な要件である重要なデータに対する操作の場合は、デフォルトの REPEATABLE READ レベルを使用して高度な一貫性を適用できます。 あるいは、正確な一貫性や繰り返し可能な結果がロックのためのオーバーヘッドの量の最少化ほど重要でない一括レポートなどの状況では、READ COMMITTED や場合によっては READ UNCOMMITTED を使用して一貫性のルールを緩和できます。 SERIALIZABLEREPEATABLE READ よりさらに厳密なルールを適用し、主に XA トランザクションのほか、並列性やデッドロックに関する問題のトラブルシューティングなどの特殊な状況で使用されます。

次のリストは、MySQL が各種のトランザクションレベルをどのようにサポートするかについて説明しています。 このリストは、もっとも一般的に使用されるレベルから使用頻度の低い順に並べられています。

  • REPEATABLE READ

    これが 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 ロック」 を参照してください。

  • READ COMMITTED

    各読取り一貫性は、同じトランザクション内であっても、独自の新しいスナップショットを設定して読み取ります。 読取り一貫性の詳細は、セクション15.7.2.3「一貫性非ロック読み取り」 を参照してください。

    ロック読取り (FOR UPDATE または FOR SHARE を使用した SELECT)、UPDATE ステートメントおよび DELETE ステートメントの場合、InnoDB はインデックスレコードのみをロックし、その前のギャップはロックしないため、ロックされたレコードの横に新しいレコードを自由に挿入できます。 ギャップロックは、外部キー制約チェックおよび重複キーチェックにのみ使用されます。

    ギャップロックが無効になっているため、他のセッションがギャップに新しい行を挿入できるため、ファントムの問題が発生する可能性があります。 ファントムの詳細は、セクション15.7.4「ファントム行」 を参照してください。

    READ COMMITTED 分離レベルでは、行ベースのバイナリロギングのみがサポートされます。 READ COMMITTEDbinlog_format=MIXED とともに使用する場合、サーバーは自動的に行ベースのロギングを使用します。

    READ COMMITTED の使用には、追加の効果があります:

    • UPDATE または DELETE ステートメントでは、InnoDB は更新または削除の対象となる行に対してのみ、ロックを保持します。 一致しなかった行のレコードロックは、MySQL による WHERE 条件の評価後に解除されます。 これにより、デッドロックの可能性が大幅に低くなりますが、まだ発生する可能性はあります。

    • UPDATE ステートメントである行がすでにロックされていた場合、InnoDB半一貫性 読み取りを実行し、最後にコミットされたバージョンを MySQL に返すため、MySQL はその行が UPDATEWHERE 条件に一致するかどうかを判断できます。 その行が一致した場合 (その行を更新する必要がある場合)、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 では、InnoDBsemi-consistent 読取りを実行し、MySQL に読み取られた各行の最新のコミット済バージョンを返して、MySQL がその行が UPDATEWHERE 条件に一致するかどうかを判断できるようにします:

    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 分離レベルは、起動時に設定することも、実行時に変更することもできます。 実行時に、すべてのセッションに対してグローバルに設定することも、セッションごとに個別に設定することもできます。

  • READ UNCOMMITTED

    SELECT ステートメントは非ロックの方法で実行されますが、以前のバージョンの行が使用される可能性もあります。 そのため、この分離レベルを使用すると、このような読み取りには一貫性がありません。 これは、ダーティー読み取りとも呼ばれます。 そうでなければ、この分離レベルは READ COMMITTED のように機能します。

  • SERIALIZABLE

    このレベルは REPEATABLE READ と似ていますが、autocommit が無効になっている場合、InnoDB はすべてのプレーン SELECT ステートメントを SELECT ... FOR SHARE に暗黙的に変換します。 autocommit が有効な場合、SELECT は独自のトランザクションです。 したがって、読み取り専用であることがわかっているため、一貫性のある (非ロック) 読み取りとして実行された場合は直列化することができ、ほかのトランザクションのためのブロックは必要ありません。 (選択した行が他のトランザクションによって変更された場合にプレーン SELECT を強制的にブロックするには、autocommit を無効にします。)

    注記

    MySQL 8.0.22 の時点では、MySQL からデータを読み取る DML 操作では、分離レベルに関係なく、(結合リストまたはサブクエリーを介して) テーブルが付与されますが、変更はされません。 詳細は、テーブル同時実行性の付与を参照してください。


関連キーワード:  InnoDB, ロック, テーブル, lock, インデックス, UPDATE, ステートメント, トランザクション, 構成, READ