14.7.3 Блокировки, устанавливаемые различными операторами SQL в InnoDB
Оператор UPDATE, оператор DELETE или оператор SELECT ...
FROM обычно устанавливают блокировки записей на каждом индексном элементе, который сканируется при выполнении оператора SQL. Не имеет значения, существуют ли WHERE условия в операторе, которые исключали бы строку. InnoDB не запоминает точное WHERE условие, а только знает, какие диапазоны индексов были просканированы. Блокировки обычно также блокируют вставки в “щель” непосредственно перед записью. Однако, можно отключить явно, что приводит к тому, что блокировка следующей записи не используется. Дополнительную информацию см. в Разделе 14.7.1, «Блокировки InnoDB». Уровень изоляции транзакции также может повлиять на установленные блокировки; см. Раздел 14.7.2.1, «Уровни изоляции транзакций».
Если в поиске используется вторичный индекс и блокировки индексных записей должны быть эксклюзивными, InnoDB также извлекает соответствующие записи кластеризованного индекса и устанавливает блокировки на них.
Если у вас нет индексов, подходящих для вашего оператора, и MySQL должен просканировать всю таблицу для обработки оператора, каждая строка таблицы будет заблокирована, что, в свою очередь, блокирует все вставки другими пользователями в таблицу. Важно создавать хорошие индексы, чтобы ваши запросы не сканировали больше строк, чем необходимо.
InnoDB устанавливает определенные типы блокировок следующим образом.
SELECT ... FROMявляется последовательным чтением, считывая моментальную фотографию базы данных и не устанавливая никаких блокировок, если уровень изоляции транзакции не установлен вSERIALIZABLE. Для уровняSERIALIZABLEпоиск устанавливает общие блокировки следующих ключей на записях индекса, которые он встречает. Однако для операторов, блокирующих строки с использованием уникального индекса для поиска уникальной строки, требуется только блокировка записи индекса.Для
SELECT ... FOR UPDATEилиSELECT ... LOCK IN SHARE MODEблокировки приобретаются для сканируемых строк и ожидается их освобождение для строк, которые не подходят для включения в результат (например, если они не соответствуют критериям, указанным вWHEREусловии). Однако в некоторых случаях строки могут не быть разблокированы немедленно, потому что связь между строкой результата и ее исходным источником теряется во время выполнения запроса. Например, вUNIONсканированные (и заблокированные) строки из таблицы могут быть вставлены во временную таблицу перед проверкой, подходят ли они для результата. В этом случае связь строк во временной таблице со строками в исходной таблице теряется, и последние строки не разблокируются до завершения выполнения запроса.SELECT ... LOCK IN SHARE MODEустанавливает общие блокировки следующих ключей на всех записях индекса, которые встречает поиск. Однако для операторов, блокирующих строки с использованием уникального индекса для поиска уникальной строки, требуется только блокировка записи индекса.-
SELECT ... FOR UPDATEустанавливает эксклюзивную блокировку следующих ключей на каждой записи, которую встречает поиск. Однако для операторов, блокирующих строки с использованием уникального индекса для поиска уникальной строки, требуется только блокировка записи индекса.Для записей индекса, которые встречает поиск,
SELECT ... FOR UPDATEблокирует другие сеансы от выполненияSELECT ... LOCK IN SHARE MODEили от чтения в определенных уровнях изоляции транзакций. Последовательные чтения игнорируют любые блокировки, установленные на записях, которые существуют в представлении чтения. UPDATE ... WHERE ...устанавливает эксклюзивную блокировку следующих ключей на каждой записи, которую встречает поиск. Однако для операторов, блокирующих строки с использованием уникального индекса для поиска уникальной строки, требуется только блокировка записи индекса.Когда
UPDATEизменяет запись кластеризованного индекса, неявно накладываются блокировки на соответствующие записи вторичных индексов. ОперацияUPDATEтакже накладывает общие блокировки на затронутые записи вторичных индексов при выполнении сканирования проверки дубликатов перед вставкой новых записей вторичных индексов и при вставке новых записей вторичных индексов.DELETE FROM ... WHERE ...устанавливает эксклюзивную блокировку следующих ключей на каждой записи, которую встречает поиск. Однако для операторов, блокирующих строки с использованием уникального индекса для поиска уникальной строки, требуется только блокировка записи индекса.-
INSERTустанавливает эксклюзивную блокировку на вставляемой строке. Эта блокировка является блокировкой записи индекса, а не блокировкой следующих ключей (то есть нет блокировки пробела) и не препятствует другим сеансам вставке в пробел перед вставленной строкой.Перед вставкой строки устанавливается тип блокировки пробела, называемой блокировкой намерения вставки. Эта блокировка сигнализирует о намерении вставить данные таким образом, что несколько транзакций, вставляющих данные в тот же пробел индекса, не должны ждать друг друга, если они не вставляют данные в ту же позицию в пробеле. Предположим, что существуют записи индекса со значениями 4 и 7. Отдельные транзакции, пытающиеся вставить значения 5 и 6, каждая блокируют пробел между 4 и 7 с помощью блокировок намерения вставки перед получением эксклюзивной блокировки на вставляемой строке, но не блокируют друг друга, потому что строки не конфликтуют.
Если возникает ошибка дублирования ключа, устанавливается общая блокировка на записи дублирующего индекса. Это использование общей блокировки может привести к тупику, если несколько сеансов пытаются вставить одну и ту же строку, если другой сеанс уже имеет эксклюзивную блокировку. Это может произойти, если другой сеанс удалит строку. Предположим, что таблица
InnoDBt1имеет следующую структуру:CREATE TABLE t1 (i INT, PRIMARY KEY (i)) ENGINE = InnoDB;
Теперь предположим, что три сеанса выполняют следующие операции в указанном порядке:
Сеанс 1:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Сеанс 2:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Сеанс 3:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Сеанс 1:
ROLLBACK;
Первая операция сеанса 1 приобретает эксклюзивную блокировку для строки. Операции сеансов 2 и 3 приводят к ошибке дублирования ключа, и они оба запрашивают общую блокировку для строки. Когда сеанс 1 откатывается, он освобождает свою эксклюзивную блокировку на строке, и запросы общей блокировки для сеансов 2 и 3 выполняются. В этот момент сеансы 2 и 3 попадают в тупик: ни один из них не может получить эксклюзивную блокировку для строки из-за общей блокировки, удерживаемой другим.
Аналогичная ситуация происходит, если в таблице уже есть строка со значением ключа 1, и три сеанса выполняют следующие операции в указанном порядке:
Сеанс 1:
START TRANSACTION; DELETE FROM t1 WHERE i = 1;
Сеанс 2:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Сеанс 3:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Сеанс 1:
COMMIT;
Первая операция сеанса 1 приобретает эксклюзивную блокировку для строки. Операции сеансов 2 и 3 приводят к ошибке дублирования ключа, и они оба запрашивают общую блокировку для строки. Когда сеанс 1 подтверждает, он освобождает свою эксклюзивную блокировку для строки, и запросы общей блокировки для сеансов 2 и 3 выполняются. В этот момент сеансы 2 и 3 попадают в тупик: ни один из них не может получить эксклюзивную блокировку для строки из-за общей блокировки, удерживаемой другим.
INSERT ... ON DUPLICATE KEY UPDATEотличается от простогоINSERTтем, что при возникновении ошибки дублирования ключа на строке, подлежащей обновлению, устанавливается эксклюзивная, а не общая блокировка. Для дублирующего значения первичного ключа берется эксклюзивная блокировка записи индекса. Для дублирующего значения уникального ключа берется эксклюзивная блокировка следующих ключей.REPLACEвыполняется какINSERT, если нет столкновений по уникальному ключу. В противном случае на строке, подлежащей замене, устанавливается эксклюзивная блокировка следующих ключей.-
INSERT INTO T SELECT ... FROM S WHERE ...устанавливает эксклюзивную блокировку записи индекса (без блокировки пробела) для каждой вставленной строки вT. Если уровень изоляции транзакции равенREAD COMMITTEDилиinnodb_locks_unsafe_for_binlogвключен и уровень изоляции транзакции не равенSERIALIZABLE,InnoDBвыполняет поиск вSкак последовательное чтение (без блокировок). В противном случаеInnoDBустанавливает общие блокировки следующих ключей на строки изS.InnoDBдолжен установить блокировки в последнем случае: при выполнении восстановления прокрутки вперед с использованием двоичного журнала на основе операторов каждый оператор SQL должен выполняться точно так же, как и изначально.CREATE TABLE ... SELECT ...выполняетSELECTс общими блокировками следующих ключей или как последовательное чтение, как дляINSERT ... SELECT.Когда
SELECTиспользуется в конструкцияхREPLACE INTO t SELECT ... FROM s WHERE ...илиUPDATE t ... WHERE col IN (SELECT ... FROM s ...),InnoDBустанавливает общие блокировки следующих ключей на строки из таблицыs. -
InnoDBустанавливает эксклюзивную блокировку конца индекса, связанного со столбцомAUTO_INCREMENT, при инициализации ранее указанного столбцаAUTO_INCREMENTв таблице.При использовании
innodb_autoinc_lock_mode=0,InnoDBиспользует специальный режим блокировки таблицыAUTO-INC, где блокировка приобретается и удерживается до конца текущего оператора SQL (а не до конца всей транзакции) при доступе к счетчику автоинкремента. Другие клиенты не могут вставлять данные в таблицу, пока удерживается блокировка таблицыAUTO-INC. То же поведение имеет место для “массовых вставок” сinnodb_autoinc_lock_mode=1. Блокировки табличного уровняAUTO-INCне используются сinnodb_autoinc_lock_mode=2. Для получения дополнительной информации см. Раздел 14.6.1.6, «Обработка AUTO_INCREMENT в InnoDB».InnoDBизвлекает значение ранее инициализированного столбцаAUTO_INCREMENTбез установки каких-либо блокировок. Если для таблицы определено ограничение
FOREIGN KEY, любая вставка, обновление или удаление, которые требуют проверки условия ограничения, устанавливают общие блокировки записей на уровне записей на записях, которые он просматривает для проверки ограничения.InnoDBтакже устанавливает эти блокировки в случае, если ограничение не выполняется.
-
LOCK TABLESустанавливает блокировки таблиц, но это верхний слой MySQL над слоемInnoDB, который устанавливает эти блокировки.InnoDBзнает о блокировках таблиц, еслиinnodb_table_locks = 1(по умолчанию) иautocommit = 0, а слой MySQL вышеInnoDBзнает о блокировках строк.В противном случае автоматическое обнаружение тупиков
InnoDBне может обнаружить тупики, в которые вовлечены такие блокировки таблиц. Кроме того, поскольку в этом случае верхний слой MySQL не знает о блокировках строк, возможно получить блокировку таблицы на таблице, на которой в настоящее время другой сеанс имеет блокировки строк. Однако это не ставит под угрозу целостность транзакции, как обсуждается в Разделе 14.7.5.2, «Обнаружение тупиков». -
LOCK TABLESприобретает две блокировки на каждой таблице, еслиinnodb_table_locks=1(по умолчанию). В дополнение к блокировке таблицы на уровне MySQL, он также приобретает блокировку таблицыInnoDB. Чтобы избежать приобретения блокировок таблицInnoDB, установитеinnodb_table_locks=0. Если не приобретается блокировка таблицыInnoDB,LOCK TABLESзавершается даже если некоторые записи таблиц блокируются другими транзакциями.В MySQL 5.7,
innodb_table_locks=0не влияет на таблицы, явно заблокированные с помощьюLOCK TABLES ... WRITE. Он оказывает влияние на таблицы, заблокированные для чтения или записи с помощьюLOCK TABLES ... WRITEнеявно (например, через триггеры) илиLOCK TABLES ... READ. Все блокировки
InnoDB, удерживаемые транзакцией, освобождаются при подтверждении или прерывании транзакции. Таким образом, нет смысла вызыватьLOCK TABLESнаInnoDBтаблицах в режимеautocommit=1, так как приобретенные блокировки таблицInnoDBбудут освобождены немедленно.Вы не можете заблокировать дополнительные таблицы в середине транзакции, потому что
LOCK TABLESвыполняет неявныйCOMMITиUNLOCK TABLES.
© 2025 Oracle
Licensed under the GPLv2 License.