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 8.4,
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.