Spec-Zone.ru › MySQL 8.4

15.3.6 Установление и снятие блокировок таблиц (LOCK TABLES и UNLOCK TABLES)

LOCK {TABLE | TABLES}
    tbl_name [[AS] alias] lock_type
    [, tbl_name [[AS] alias] lock_type] ...

lock_type: {
    READ [LOCAL]
  | WRITE
}

UNLOCK {TABLE | TABLES}

MySQL позволяет сессиям клиентов явно устанавливать блокировки таблиц для взаимодействия с другими сессиями при доступе к таблицам или для предотвращения изменений в таблицах другими сессиями в периоды, когда сессия требует к ним исключительного доступа. Сессия может устанавливать или снимать блокировки только для себя. Одна сессия не может устанавливать блокировки для другой сессии или снимать блокировки, установленные другой сессией.

Блокировки могут использоваться для эмуляции транзакций или для повышения скорости при обновлении таблиц. Более подробное объяснение содержится в разделе Ограничения и условия блокировки таблиц.

LOCK TABLES явно устанавливает блокировки таблиц для текущей сессии клиента. Блокировки таблиц могут устанавливаться для базовых таблиц или представлений. Вам необходимо иметь LOCK TABLES и SELECT привилегию для каждого объекта, который должен быть заблокирован.

Для блокировки представлений LOCK TABLES добавляет все базовые таблицы, используемые в представлении, в набор таблиц, которые должны быть заблокированы, и блокирует их автоматически. Для таблиц, лежащих в основе любого блокируемого представления, LOCK TABLES проверяет, обладает ли определяющий представление (для SQL SECURITY DEFINER представлений) или вызывающий (для всех представлений) пользователя соответствующими привилегиями на этих таблицах.

Если вы явно блокируете таблицу с помощью LOCK TABLES, все таблицы, используемые в триггерах, также блокируются неявно, как описано в LOCK TABLES и триггеры.

Если вы явно блокируете таблицу с помощью LOCK TABLES, любые таблицы, связанные внешним ключом, открываются и блокируются неявно. Для проверок внешнего ключа устанавливается общая блокировка чтения без записи (LOCK TABLES READ) на связанных таблицах. Для каскадных обновлений устанавливается общая блокировка без записи (LOCK TABLES WRITE) на связанных таблицах, участвующих в операции.

UNLOCK TABLES явно снимает любые блокировки таблиц, установленные текущей сессией. LOCK TABLES неявно снимает любые блокировки таблиц, установленные текущей сессией, прежде чем установить новые.

Еще одно применение UNLOCK TABLES заключается в снятии глобальной блокировки чтения, установленной с помощью оператора FLUSH TABLES WITH READ LOCK, что позволяет заблокировать все таблицы во всех базах данных. См. Раздел 15.7.8.3, “Оператор FLUSH”. (Это очень удобный способ получения резервных копий, если у вас есть файловая система, такая как Veritas, которая может делать моментальные снимки во времени.)

LOCK TABLE — синоним LOCK TABLES; UNLOCK TABLE — синоним UNLOCK TABLES.

Блокировка таблицы защищает только от ненадлежащего чтения или записи другими сессиями. Сессия, удерживающая блокировку WRITE, может выполнять операции на уровне таблиц, такие как DROP TABLE или TRUNCATE TABLE. Для сессий, удерживающих блокировку READ, операции DROP TABLE и TRUNCATE TABLE запрещены.

Следующее обсуждение относится только к таблицам, не являющимся TEMPORARY. LOCK TABLES разрешено (но игнорируется) для таблицы TEMPORARY. К таблице может свободно обращаться сессия, в которой она была создана, независимо от других блокировок. Блокировка не нужна, потому что никакая другая сессия не может увидеть таблицу.

  • Установка блокировки таблицы

  • Снятие блокировки таблицы

  • Взаимодействие блокировки таблиц и транзакций

  • LOCK TABLES и триггеры

  • Ограничения и условия блокировки таблиц

Установка блокировки таблицы

Для установки блокировок таблиц в текущей сессии используйте оператор LOCK TABLES, который устанавливает блокировки метаданных (см. Раздел 10.11.4, “Блокировка метаданных”).

Доступны следующие типы блокировок:

Блокировка READ [LOCAL]:

  • Сессия, удерживающая блокировку, может читать таблицу (но не записывать в нее).

  • Несколько сессий могут одновременно установить блокировку READ на таблицу.

  • Другие сессии могут читать таблицу, не устанавливая явную блокировку READ.

  • Модификатор LOCAL позволяет другим сессиям выполнять неконфликтные операторы INSERT (параллельные вставки), пока блокировка удерживается. (См. Раздел 10.11.3, “Параллельные вставки”). Однако, READ LOCAL нельзя использовать, если вы собираетесь манипулировать базой данных с помощью процессов, внешних по отношению к серверу, в то время как удерживаете блокировку. Для InnoDB таблиц, READ LOCAL то же самое, что и READ.

Блокировка WRITE:

  • Сессия, удерживающая блокировку, может читать и записывать в таблицу.

  • Только сессия, удерживающая блокировку, может получить доступ к таблице. Никакая другая сессия не может получить к ней доступ, пока блокировка не будет снята.

  • Запросы на блокировку таблицы другими сессиями блокируются, пока удерживается блокировка WRITE.

Блокировки WRITE обычно имеют более высокий приоритет, чем блокировки READ, чтобы гарантировать, что обновления обрабатываются как можно быстрее. Это означает, что если одна сессия получает блокировку READ, а затем другая сессия запрашивает блокировку WRITE, последующие запросы на блокировку READ ожидают, пока сессия, запросившая блокировку WRITE, получит блокировку и не снимет её. (Исключение из этого правила может возникнуть для малых значений системной переменной max_write_lock_count; см. Раздел 10.11.4, “Блокировка метаданных”).

Если оператор LOCK TABLES должен ожидать из-за блокировок, установленных другими сессиями на каких-либо таблицах, он блокируется, пока все блокировки не будут получены.

Сессия, которой требуются блокировки, должна получить все необходимые блокировки в одном операторе LOCK TABLES. Пока удерживаются полученные блокировки, сессия может получить доступ только к заблокированным таблицам. Например, в следующей последовательности операторов возникает ошибка при попытке доступа к t2, потому что она не была заблокирована в операторе LOCK TABLES:

mysql> LOCK TABLES t1 READ;
mysql> SELECT COUNT(*) FROM t1;
+----------+
| COUNT(*) |
+----------+
|        3 |
+----------+
mysql> SELECT COUNT(*) FROM t2;
ERROR 1100 (HY000): Table 't2' was not locked with LOCK TABLES

Таблицы базы данных INFORMATION_SCHEMA являются исключением. К ним можно получить доступ без явной блокировки, даже когда сессия удерживает блокировки таблиц, полученные с помощью LOCK TABLES.

Нельзя ссылаться на заблокированную таблицу несколько раз в одном запросе с тем же именем. Используйте псевдонимы, и устанавливайте отдельную блокировку для таблицы и каждого псевдонима:

mysql> LOCK TABLE t WRITE, t AS t1 READ;
mysql> INSERT INTO t SELECT * FROM t;
ERROR 1100: Table 't' was not locked with LOCK TABLES
mysql> INSERT INTO t SELECT * FROM t AS t1;

Ошибка возникает для первого оператора INSERT, потому что есть две ссылки на одно и то же имя для заблокированной таблицы. Второй оператор INSERT выполняется успешно, потому что ссылки на таблицу используют разные имена.

Если ваши операторы ссылаются на таблицу с помощью псевдонима, вы должны заблокировать таблицу, используя этот же псевдоним. Это не сработает, если заблокировать таблицу без указания псевдонима:

mysql> LOCK TABLE t READ;
mysql> SELECT * FROM t AS myalias;
ERROR 1100: Table 'myalias' was not locked with LOCK TABLES

И наоборот, если вы блокируете таблицу с помощью псевдонима, вы должны ссылаться на нее в своих операторах, используя этот же псевдоним:

mysql> LOCK TABLE t AS myalias READ;
mysql> SELECT * FROM t;
ERROR 1100: Table 't' was not locked with LOCK TABLES
mysql> SELECT * FROM t AS myalias;

Освобождение блокировок таблиц

Когда блокировки таблиц, удерживаемые сеансом, освобождаются, все они освобождаются одновременно. Сеанс может явно освободить свои блокировки, или блокировки могут быть освобождены неявно в определенных условиях.

  • Сеанс может явно освободить свои блокировки с помощью UNLOCK TABLES.

  • Если сеанс выполняет оператор LOCK TABLES для получения блокировки, одновременно удерживая другие блокировки, существующие блокировки освобождаются неявно перед предоставлением новых.

  • Если сеанс начинает транзакцию (например, с помощью START TRANSACTION), выполняется неявное UNLOCK TABLES, что приводит к освобождению существующих блокировок. (Дополнительную информацию о взаимодействии блокировки таблиц и транзакций см. в разделе Взаимодействие блокировки таблиц и транзакций.)

Если соединение для клиентского сеанса завершается, будь то нормально или аномально, сервер неявно освобождает все блокировки таблиц, удерживаемые сеансом (транзакционные и не транзакционные). Если клиент повторно подключается, блокировки больше не действуют. Кроме того, если у клиента была активная транзакция, сервер откатывает транзакцию при отключении, а при повторном подключении новый сеанс начинается с включённым автоматическим подтверждением. По этой причине клиенты могут захотеть отключить автоматическое повторное подключение. При включенном автоматическом повторном подключении клиент не уведомляется о повторном подключении, но любые блокировки таблиц или текущая транзакция теряются. При отключённом автоматическом повторном подключении, если соединение прервано, возникает ошибка для следующей выполняемой команды. Клиент может обнаружить ошибку и предпринять соответствующие действия, такие как повторное получение блокировок или повторное выполнение транзакции. См. .

Примечание

Если вы используете ALTER TABLE для заблокированной таблицы, она может быть разблокирована. Например, если вы попытаетесь выполнить вторую операцию ALTER TABLE, результатом может быть ошибка Table 'tbl_name' was not locked with LOCK TABLES. Для решения этой проблемы заблокируйте таблицу снова перед второй модификацией. См. также Раздел B.3.6.1, «Проблемы с ALTER TABLE».

Взаимодействие блокировки таблиц и транзакций

LOCK TABLES и UNLOCK TABLES взаимодействуют с использованием транзакций следующим образом:

  • LOCK TABLES не является транзакционно-безопасной и неявно подтверждает любую активную транзакцию перед попыткой заблокировать таблицы.

  • UNLOCK TABLES неявно подтверждает любую активную транзакцию, но только если LOCK TABLES использовалась для получения блокировок таблиц. Например, в следующем наборе операторов, UNLOCK TABLES освобождает глобальную блокировку чтения, но не подтверждает транзакцию, так как блокировки таблиц не активны:

    FLUSH TABLES WITH READ LOCK;
    START TRANSACTION;
    SELECT ... ;
    UNLOCK TABLES;
    
  • Начало транзакции (например, с помощью START TRANSACTION) неявно подтверждает любую текущую транзакцию и освобождает существующие блокировки таблиц.

  • FLUSH TABLES WITH READ LOCK получает глобальную блокировку чтения, а не блокировки таблиц, поэтому она не подчиняется тому же поведению, что и LOCK TABLES и UNLOCK TABLES по отношению к блокировке таблиц и неявным подтверждениям. Например, START TRANSACTION не освобождает глобальную блокировку чтения. См. Раздел 15.7.8.3, «Оператор FLUSH».

  • Другие операторы, которые неявно приводят к подтверждению транзакций, не освобождают существующие блокировки таблиц. Список таких операторов см. в Разделе 15.3.3, «Операторы, вызывающие неявное подтверждение».

  • Правильный способ использования LOCK TABLES и UNLOCK TABLES с транзакционными таблицами, такими как InnoDB таблицы, заключается в начале транзакции с SET autocommit = 0 (а не START TRANSACTION) и последующим выполнением LOCK TABLES, и не вызывать UNLOCK TABLES, пока не подтвердите транзакцию явно. Например, если вам нужно записать в таблицу t1 и прочитать из таблицы t2, вы можете сделать это так:

    SET autocommit=0;
    LOCK TABLES t1 WRITE, t2 READ, ...;
    ... do something with tables t1 and t2 here ...
    COMMIT;
    UNLOCK TABLES;
    

    При вызове LOCK TABLES, InnoDB внутренне получает свою блокировку таблицы, а MySQL получает свою. InnoDB освобождает свою внутреннюю блокировку таблицы при следующем подтверждении, но для того, чтобы MySQL освободил свою блокировку таблицы, вам нужно вызвать UNLOCK TABLES. Вы не должны иметь autocommit = 1, потому что тогда InnoDB освобождает свою внутреннюю блокировку таблицы сразу после вызова LOCK TABLES, и очень легко могут произойти тупики. InnoDB вообще не получает внутреннюю блокировку таблицы, если autocommit = 1, чтобы помочь старым приложениям избежать ненужных тупиков.

  • ROLLBACK не освобождает блокировки таблиц.

LOCK TABLES и триггеры

Если вы явно блокируете таблицу с помощью LOCK TABLES, все таблицы, используемые в триггерах, также блокируются неявно:

  • Блокировки получают одновременно с теми, которые явно приобретены с помощью оператора LOCK TABLES.

  • Блокировка таблицы, используемой в триггере, зависит от того, используется ли таблица только для чтения. Если это так, достаточно блокировки чтения. В противном случае используется блокировка записи.

  • Если таблица явно блокируется для чтения с помощью LOCK TABLES, но должна быть заблокирована для записи, потому что она может быть изменена внутри триггера, используется блокировка записи, а не блокировка чтения. (То есть, неявная блокировка записи, необходимая из-за появления таблицы внутри триггера, приводит к преобразованию явного запроса на блокировку чтения таблицы в запрос на блокировку записи.)

Предположим, что вы блокируете две таблицы, t1 и t2, с помощью этого оператора:

LOCK TABLES t1 WRITE, t2 READ;

Если у t1 или t2 есть триггеры, таблицы, используемые внутри триггеров, также блокируются. Предположим, что t1 имеет определённый триггер так:

CREATE TRIGGER t1_a_ins AFTER INSERT ON t1 FOR EACH ROW
BEGIN
  UPDATE t4 SET count = count+1
      WHERE id = NEW.id AND EXISTS (SELECT a FROM t3);
  INSERT INTO t2 VALUES(1, 2);
END;

Результат оператора LOCK TABLES заключается в том, что t1 и t2 блокируются, так как они присутствуют в операторе, а t3 и t4 блокируются, так как они используются внутри триггера:

  • t1 блокируется для записи в соответствии с запросом на блокировку WRITE.

  • t2 блокируется для записи, даже если запрос относится к блокировке READ. Это происходит, потому что t2 вставляется в триггер, поэтому запрос на блокировку READ преобразуется в запрос на блокировку WRITE.

  • t3 блокируется для чтения, так как она только читается внутри триггера.

  • t4 блокируется для записи, так как она может быть обновлена внутри триггера.

Ограничения и условия блокировки таблиц

Для безопасного завершения сеанса, ожидающего блокировку таблицы, можно использовать команду KILL. См. Раздел 15.7.8.4, «Команда KILL».

Команды LOCK TABLES и UNLOCK TABLES не могут быть использованы внутри хранимых программ.

Таблицы в базе данных performance_schema не могут быть заблокированы с помощью команды LOCK TABLES, за исключением таблиц setup_xxx.

Область действия блокировки, созданной командой LOCK TABLES, ограничена одним сервером MySQL. Она не совместима с NDB Cluster, поскольку у него нет способа принудительной реализации блокировки на уровне SQL для нескольких экземпляров mysqld. Вместо этого можно принудительно реализовать блокировку в приложении API. Дополнительную информацию см. в Разделе 25.2.7.10, «Ограничения, связанные с несколькими узлами NDB Cluster».

Следующие команды запрещены во время действия команды LOCK TABLES: CREATE TABLE, CREATE TABLE ... LIKE, CREATE VIEW, DROP VIEW, а также DDL-команды для хранимых функций, процедур и событий.

Для некоторых операций необходимо получить доступ к системным таблицам в базе данных mysql. Например, команда HELP требует содержимого таблиц помощи на стороне сервера, а CONVERT_TZ() может потребовать чтения таблиц часовых поясов. Сервер неявно блокирует системные таблицы для чтения по мере необходимости, поэтому вам не нужно делать это явно. Эти таблицы обрабатываются как описано:

mysql.help_category
mysql.help_keyword
mysql.help_relation
mysql.help_topic
mysql.time_zone
mysql.time_zone_leap_second
mysql.time_zone_name
mysql.time_zone_transition
mysql.time_zone_transition_type

Если вы хотите явно установить блокировку WRITE на любую из этих таблиц с помощью команды LOCK TABLES, то заблокированной должна быть только эта таблица; никакие другие таблицы не могут быть заблокированы в рамках этой же команды.

Обычно блокировка таблиц не требуется, потому что все отдельные команды UPDATE являются атомарными; никакая другая сессия не может вмешиваться в выполнение других SQL-команд. Однако есть несколько случаев, когда блокировка таблиц может быть полезна:

  • Если вы собираетесь выполнить много операций с набором таблиц MyISAM, то блокировка этих таблиц значительно ускорит работу. Блокировка таблиц MyISAM ускоряет операции вставки, обновления или удаления, потому что MySQL не очищает кэш ключей для заблокированных таблиц до вызова UNLOCK TABLES. Обычно кэш ключей очищается после каждой SQL-команды.

    Недостатком блокировки таблиц является то, что ни одна сессия не сможет обновить заблокированную таблицу READ (включая таблицу с блокировкой) и ни одна сессия не сможет получить доступ к заблокированной таблице WRITE, кроме таблицы с блокировкой.

  • Если вы используете таблицы с движком, не поддерживающим транзакции, необходимо использовать LOCK TABLES, чтобы гарантировать, что никакая другая сессия не изменит таблицы между командой SELECT и UPDATE. Для безопасного выполнения показанного здесь примера требуется использование LOCK TABLES:

    LOCK TABLES trans READ, customer WRITE;
    SELECT SUM(value) FROM trans WHERE customer_id=some_id;
    UPDATE customer
      SET total_value=sum_from_previous_statement
      WHERE customer_id=some_id;
    UNLOCK TABLES;
    

    Без LOCK TABLES есть вероятность того, что другая сессия может вставить новую строку в таблицу trans между выполнением команд SELECT и UPDATE.

В многих случаях можно избежать использования LOCK TABLES, используя относительные обновления (UPDATE customer SET value=value+new_value) или функцию LAST_INSERT_ID().

Также в некоторых случаях можно избежать блокировки таблиц, используя функции консультационной блокировки на уровне пользователя GET_LOCK() и RELEASE_LOCK(). Эти блокировки хранятся в хеш-таблице на сервере и реализованы с помощью pthread_mutex_lock() и pthread_mutex_unlock() для высокой скорости. См. Раздел 14.14, «Функции блокировки».

Дополнительную информацию о политике блокировки см. в Разделе 10.11.1, «Внутренние методы блокировки».

© 2025 Oracle
Licensed under the GPLv2 License.
https://docs.oracle.com/cd/E17952_01/mysql-8.4-en/lock-tables.html

Spec-Zone.ru

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