Spec-Zone.ru › MySQL 5.7

14.7.4 Призрачные строки

Так называемая проблема призрачных строк возникает в рамках транзакции, когда один и тот же запрос возвращает разные наборы строк в разное время. Например, если оператор SELECT выполняется дважды, но во второй раз возвращает строку, которая не была возвращена в первый раз, эта строка является “призрачной” строкой.

Предположим, что есть индекс по столбцу id таблицы child, и вы хотите прочитать и заблокировать все строки из таблицы, у которых значение идентификатора больше 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 в порядке индекса.

Когда InnoDB сканирует индекс, она также может заблокировать промежуток после последней записи в индексе. Именно это происходит в приведённом выше примере: чтобы предотвратить любую вставку в таблицу, где id было бы больше 100, блокировки, установленные InnoDB, включают блокировку промежутка, следующего за значением id 102.

Вы можете использовать блокировку следующего ключа для реализации проверки уникальности в вашем приложении: Если вы читаете данные в режиме совместного доступа и не видите дубликата для строки, которую вы собираетесь вставить, то вы можете безопасно вставить свою строку и знать, что блокировка следующего ключа, установленная на преемнике вашей строки во время чтения, предотвращает кого-либо вставить дубликат для вашей строки одновременно. Таким образом, блокировка следующего ключа позволяет вам “заблокировать” несуществование чего-либо в вашей таблице.

Блокировка промежутка может быть отключена, как обсуждается в Разделе 14.7.1, «Блокировка InnoDB». Это может привести к проблеме призрачных строк, потому что другие сессии могут вставить новые строки в промежутки, когда блокировка промежутка отключена.

© 2025 Oracle
Licensed under the GPLv2 License.
https://docs.oracle.com/cd/E17952_01/mysql-5.7-en/innodb-next-key-locking.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API