17.7.3 Блокировки, устанавливаемые различными операторами SQL в InnoDB
Оператор , оператор UPDATE или оператор DELETE обычно устанавливает блокировки записей на каждой записи индекса, которая сканируется при обработке оператора SQL. Не имеет значения, есть ли в операторе WHERE условия, которые исключали бы строку. InnoDB не запоминает точное WHERE условие, а только знает, какие диапазоны индексов были просканированы. Блокировки, как правило, также блокируют вставки в “щель” непосредственно перед записью. Однако, могут быть отключены явно, что приводит к тому, что блокировка смежных ключей не используется. Для получения дополнительной информации см. Раздел 17.7.1, «Блокировки InnoDB». Уровень изоляции транзакции также может повлиять на устанавливаемые блокировки; см. Раздел 17.7.2.1, «Уровни изоляции транзакций».
Если во время поиска используется вторичный индекс, и блокировки записей индекса должны быть эксклюзивными, InnoDB также извлекает соответствующие записи кластеризованного индекса и устанавливает блокировки на них.
Если у вас нет подходящих индексов для вашего оператора и MySQL должен просканировать всю таблицу для обработки оператора, каждая строка таблицы блокируется, что, в свою очередь, блокирует все вставки другими пользователями в таблицу. Важно создавать хорошие индексы, чтобы ваши запросы не сканировали больше строк, чем необходимо.
InnoDB устанавливает определенные типы блокировок следующим образом.
SELECT ... FROMявляется последовательным чтением, считывая моментальный снимок базы данных и не устанавливая блокировок, если уровень изоляции транзакции не установлен вSERIALIZABLE. Для уровня изоляцииSERIALIZABLEпоиск устанавливает общие блокировки по следующему ключу на записях индекса, которые он встречает. Однако, для операторов, которые блокируют строки с помощью уникального индекса для поиска уникальной строки, требуется только блокировка записи индекса.SELECT ... FOR UPDATEиSELECT ... FOR SHAREоператоры, использующие уникальный индекс, приобретают блокировки для просматриваемых строк и освобождают блокировки для строк, которые не подходят для включения в результирующий набор (например, если они не соответствуют критериям, указанным вWHEREклаузе). Однако в некоторых случаях строки могут не быть разблокированы немедленно, потому что связь между строкой результата и ее исходным источником теряется во время выполнения запроса. Например, вUNION, просматриваемые (и заблокированные) строки из таблицы могут быть вставлены во временную таблицу, прежде чем будет оценено, подходят ли они для результирующего набора. В этом случае связь строк во временной таблице со строками в исходной таблице теряется, и последние строки не разблокируются до конца выполнения запроса.-
Для (
SELECTсFOR UPDATEилиFOR SHARE),UPDATEиDELETEоператоров, блокировки, которые принимаются, зависят от того, использует ли оператор уникальный индекс с уникальным условием поиска или условие поиска типа диапазона.Для уникального индекса с уникальным условием поиска,
InnoDBблокирует только найденную запись индекса, а не перед ней.Для других условий поиска и для неуникальных индексов,
InnoDBблокирует диапазон индекса, просматриваемый с использованием или для блокировки вставок другими сессиями в промежутки, покрытые диапазоном. Дополнительная информация о блокировках промежутков и блокировках по следующему ключу находится в Разделе 17.7.1, «Блокировка InnoDB».
Для записей индекса, которые поиск находит,
SELECT ... FOR UPDATEблокирует другие сессии от выполненияSELECT ... FOR SHAREили от чтения при определенных уровнях изоляции транзакций. Последовательные чтения игнорируют любые блокировки, установленные на записях, которые существуют в представлении чтения.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выполняет поиск по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. Для получения дополнительной информации см. Раздел 17.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 не знает о блокировках на уровне строк, возможно получить блокировку таблицы на таблице, на которой в другой сессии в настоящее время установлены блокировки на уровне строк. Однако это не угрожает целостности транзакции, как обсуждается в Разделе 17.7.5.2, «Обнаружение тупиков». -
LOCK TABLESприобретает две блокировки на каждой таблице, еслиinnodb_table_locks=1(по умолчанию). В дополнение к блокировке таблицы на уровне MySQL, он также приобретает блокировку таблицыInnoDB. Чтобы избежать приобретения блокировок таблицыInnoDB, установитеinnodb_table_locks=0. Если блокировка таблицыInnoDBне приобретается,LOCK TABLESзавершается, даже если некоторые записи таблиц блокируются другими транзакциями.В MySQL 9.2,
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.