The so-called phantom problem occurs within
a transaction when the same query produces different sets of rows
at different times. For example, if a
SELECT is executed twice, but
returns a row the second time that was not returned the first
time, the row is a “phantom” row.
Suppose that there is an index on the
child table and that you want to read
and lock all rows from the table having an identifier value larger
than 100, with the intention of updating some column in the
selected rows later:
SELECT * FROM child WHERE id > 100 FOR UPDATE;
The query scans the index starting from the first record where
id is bigger than 100. Let the table contain
id values of 90 and 102. If the
locks set on the index records in the scanned range do not lock
out inserts made in the gaps (in this case, the gap between 90 and
102), another session can insert a new row into the table with an
id of 101. If you were to execute the same
SELECT within the same transaction,
you would see a new row with an
id of 101 (a
“phantom”) in the result set returned by the query.
If we regard a set of rows as a data item, the new phantom child
would violate the isolation principle of transactions that a
transaction should be able to run so that the data it has read
does not change during the transaction.
To prevent phantoms,
InnoDB uses an algorithm
called next-key locking that combines
index-row locking with gap locking.
performs row-level locking in such a way that when it searches or
scans a table index, it sets shared or exclusive locks on the
index records it encounters. Thus, the row-level locks are
actually index-record locks. In addition, a next-key lock on an
index record also affects the “gap” before that index
record. That is, a next-key lock is an index-record lock plus a
gap lock on the gap preceding the index record. If one session has
a shared or exclusive lock on record
R in an
index, another session cannot insert a new index record in the gap
R in the index order.
InnoDB scans an index, it can also lock
the gap after the last record in the index. Just that happens in
the preceding example: To prevent any insert into the table where
id would be bigger than 100, the locks set by
InnoDB include a lock on the gap following
id value 102.
You can use next-key locking to implement a uniqueness check in your application: If you read your data in share mode and do not see a duplicate for a row you are going to insert, then you can safely insert your row and know that the next-key lock set on the successor of your row during the read prevents anyone meanwhile inserting a duplicate for your row. Thus, the next-key locking enables you to “lock” the nonexistence of something in your table.
Gap locking can be disabled as discussed in Section 14.2.6, “InnoDB Record, Gap, and Next-Key Locks”. This may cause phantom problems because other sessions can insert new rows into the gaps when gap locking is disabled.