いわゆる「ファントム」の問題がトランザクション内で発生するのは、同じクエリーから生成される一連の行が、実行のたびに異なるような場合です。たとえば、SELECT
        が 2 回実行されたが、1
        回目には返されなかった行が 2
        回目には返された場合、その行が
        「ファントム」 行です。
      
        child テーブルの
        id
        カラム上にインデックスが設定された状態で、次のように識別子の値が
        100
        より大きいすべての行をテーブルから読み取り、その選択された行の特定のカラムをあとで更新できるようにそれらの行をロックするものとします。
      
SELECT * FROM child WHERE id > 100 FOR UPDATE;
        クエリーは、id が 100
        より大きい最初のレコードからインデックスを走査します。このテーブルには
        id の値が 90 と 102
        の行が格納されているものとします。その走査範囲内のインデックスレコード上に設定されたロックがギャップ
        (この場合のギャップは 90 から 102 まで)
        への挿入を禁止しなければ、id
        が 101
        の新しい行を、別のセッションからそのテーブルに挿入することができます。同じトランザクション内で同じ
        SELECT
        を実行すると、クエリーから返された結果セット内に、id
        が 101 の新しい行 (「ファントム」)
        が含まれています。一連の行をデータ項目とみなせば、この新しいファントムの子は、「トランザクションの実行は、読み取るデータがトランザクション中は変化しないような方法で行えるべきである」というトランザクションの遮断原則に違反していることになります。
      
        ファントムの発生を回避できるように、InnoDB
        では通常、インデックス行ロックとギャップロックを組み合わせた「ネクストキーロック」と呼ばれるアルゴリズムが使用されます。InnoDB
        は、それがテーブルインデックスを検索や走査するときに、遭遇したインデックスレコード上で共有または排他ロックを設定する、という方法で行レベルロックを実行します。従って、行レベルロックは実際はインデックスレコードロックであるということになります。さらに、あるインデックスレコードに対するネクストキーロックは、そのインデックスレコードの前の
        「ギャップ」
        にも影響を与えます。つまり、ネクストキーロックは、インデックスレコードロックと、そのインデックスレコードの前のギャップに対するギャップロックとを組み合わせたものです。あるセッションがインデックス内のレコード
        R
        上に共有または排他ロックを持っている場合、別のセッションがインデックスの順番で
        R
        の直前にあたるギャップに新しいインデックスレコードを挿入することはできません。
      
        When InnoDB
        がインデックスを走査するとき、インデックス内の最後のレコードの後のギャップをロックすることもできます。上の例ではまさにそれが行われています。id
        が 100
        より大きい範囲でテーブルへの一切が挿入できないように、InnoDB
        によって設定されるロックには、id
        値 102
        のあとのギャップに対するロックも含まれています。
      
アプリケーション内に一意性確認を実装するためにネクストキーロックを利用することができます:共有モードでデータを読み取り、挿入しようとする行に重複が見られなければ、行を確実に挿入できます。また、読み取り中は対象となる行の後続の行にネクストキーロックが設定されて、第三者による重複行の挿入を防ぎます。このように、ネクストキーロックによって、テーブル内に存在しないものを 「ロック」 することができます。
        ギャップロックは、項9.8.4. 「InnoDB
        レコード、ギャップ、およびネクストキーロック」で説明した方法で無効にすることができます。そうした場合、ファントムの問題が発生する可能性があります。なぜなら、ギャップロックが無効だとほかのセッションが新しい行をギャップに挿入できるからです。
      

