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, которое получает метаданные блокировки (см. Раздел 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
'. Для решения этой проблемы заблокируйте таблицу снова перед второй модификацией. См. также Раздел B.3.6.1, «Проблемы с ALTER TABLE».tbl_name' was not locked with LOCK
TABLES
Взаимодействие блокировки таблиц и транзакций
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_statementWHERE customer_id=some_id; UNLOCK TABLES;Без
LOCK TABLESвозможно, что другая сессия может вставить новую строку в таблицуtransмежду выполнением операторовSELECTиUPDATE.
Во многих случаях можно избежать использования LOCK TABLES, используя относительные обновления (UPDATE
customer SET
) или функцию value=value+new_valueLAST_INSERT_ID().
Также можно избежать блокировки таблиц в некоторых случаях, используя функции уровня пользователя для консультационной блокировки GET_LOCK() и RELEASE_LOCK(). Эти блокировки сохраняются в таблице хэшей на сервере и реализуются с использованием pthread_mutex_lock() и pthread_mutex_unlock() для высокой скорости. См. Раздел 14.14, «Функции блокировки».
Дополнительную информацию о политике блокировки см. в Разделе 10.11.1, «Внутренние методы блокировки».
© 2025 Oracle
Licensed under the GPLv2 License.