LOCK TABLES 和 UNLOCK TABLES 语句语法如下:
LOCK TABLES
tbl_name [[AS] alias] lock_type
[, tbl_name [[AS] alias] lock_type] ...
lock_type: {
READ [LOCAL]
| [LOW_PRIORITY] WRITE
}
UNLOCK TABLES
MySQL 使客户端会话能够明确获取表锁,以便与其他会话合作访问表,或者在会话需要独占访问表时防止其他会话修改表。会话只能为自己获取或释放锁。一个会话不能为另一个会话获取锁或释放另一个会话持有的锁。
数据库锁表如何解锁?锁可用于模拟事务或在更新表时获得更高的速度。这在表锁定限制和条件中有更详细的解释 。
LOCK TABLES
显式获取当前客户端会话的表锁。可以为基表或视图获取表锁。你必须拥有该 LOCK TABLES
权限,以及SELECT
每个要锁定的对象的 权限。
数据库如何解锁表?对于视图锁定,LOCK TABLES
将视图中使用的所有基表添加到要锁定的表集中并自动锁定它们。对于被锁定的任何视图下的表,LOCK TABLES
检查视图定义者(对于SQL SECURITY DEFINER
视图)或调用者(对于所有视图)是否对表具有适当的权限。
如果LOCK TABLES
使用显式锁定表,触发器中使用的任何表也会被隐式锁定,如 LOCK TABLES 和 Triggers 中所述。
如果使用 显式锁定表LOCK TABLES
,则任何与外键约束相关的表都将被隐式打开和锁定。对于外键检查,LOCK TABLES READ
对相关表采用共享只读锁 ( )。对于级联更新,LOCK TABLES WRITE
在操作中涉及的相关表上采用无共享写锁 ( )。
UNLOCK TABLES
显式释放当前会话持有的任何表锁。LOCK TABLES
在获取新锁之前隐式释放当前会话持有的任何表锁。
for 的另一个用途 UNLOCK TABLES
是释放通过FLUSH TABLES WITH READ LOCK
语句获取的全局读锁,它使你可以锁定所有数据库中的所有表。见第 13.7.8.3 节,“FLUSH 语句”。(如果你的文件系统(例如 Veritas)可以及时拍摄快照,这是一种非常方便的获取备份的方法。)
表锁只能防止其他会话进行不适当的读取或写入。持有WRITE
锁的会话可以执行表级操作,例如 DROP TABLE
或 TRUNCATE TABLE
。对于持有会话READ
锁,DROP TABLE
并且TRUNCATE TABLE
操作是不允许的。
以下讨论仅适用于非TEMPORARY
表格。LOCK TABLES
允许(但忽略) TEMPORARY
表。该表可以由创建它的会话自由访问,而不管其他什么锁定可能有效。不需要锁定,因为没有其他会话可以看到该表。
- 表锁获取
- 表锁释放
- 表锁定和事务的交互
- 锁表和触发器
- 表锁定限制和条件
表锁获取
LOCK TABLES 和 UNLOCK TABLES 语句:要在当前会话中获取表锁,请使用LOCK TABLES
获取元数据锁的 语句(请参阅 第 8.11.4 节,“元数据锁定”)。
以下锁定类型可用:
READ [LOCAL]
锁:
- 持有锁的会话可以读取表(但不能写入)。
- 多个会话可以同时获取
READ
表的锁。 - 其他会话可以在不显式获取
READ
锁的情况下读取该表。 - 该
LOCAL
修饰符允许INSERT
其他会话在持有锁时执行非冲突 语句(并发插入)。(请参阅第 8.11.3 节,“并发插入”。)但是,READ LOCAL
如果你要在持有锁的同时使用服务器外部的进程来操作数据库, 则不能使用。对于InnoDB
表,READ LOCAL
与READ
.
[LOW_PRIORITY] WRITE
锁:
- 持有锁的会话可以读写表。
- 只有持有锁的会话才能访问该表。在释放锁之前,没有其他会话可以访问它。
WRITE
持有锁时 ,其他会话对表的锁请求会阻塞。- 该
LOW_PRIORITY
修饰符无效。在以前的 MySQL 版本中,它会影响锁定行为,但现在不再如此。它现在已被弃用,其使用会产生警告。使用WRITE
withoutLOW_PRIORITY
代替。
WRITE
锁通常比READ
锁具有更高的优先级,以确保尽快处理更新。这意味着如果一个会话获得READ
锁,然后另一个会话请求WRITE
锁,后续的 READ
锁请求会一直等待,直到请求WRITE
锁的会话获得锁并释放它。(对于max_write_lock_count
系统变量的小值可能会发生此策略的例外 ;请参阅第 8.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
操作,结果可能是错误。要处理此问题,请在第二次更改之前再次锁定表。另请参见 第 B.3.6.1 节“ALTER TABLE 的问题”。 Table '
tbl_name
' was not locked with LOCK TABLES
LOCK TABLES 和 UNLOCK 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
不释放全局读锁。见第 13.7.8.3 节,“FLUSH 语句”。- 其他隐式导致事务被提交的语句不会释放现有的表锁。有关此类语句的列表,请参阅第 13.3.3 节,“导致隐式提交的语句”。
- 使用
LOCK TABLES
和UNLOCK TABLES
处理事务表(例如InnoDB
表)的正确方法是使用SET autocommit = 0
(notSTART TRANSACTION
) 后跟开始事务LOCK TABLES
,并且UNLOCK TABLES
在明确提交事务之前不调用 。例如,如果你需要写入 tablet1
并从 table 读取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
if 根本不获取内部表锁autocommit = 1
,以帮助旧应用程序避免不必要的死锁。 ROLLBACK
不释放表锁。
数据库如何解锁表?锁表和触发器
如果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
and t2
被锁定是因为它们出现在语句中,而t3
andt4
被锁定是因为它们在触发器中使用:
t1
根据WRITE
锁定请求被锁定以进行写入 。t2
被锁定写入,即使请求是READ
锁定的。发生这种情况是因为t2
插入到触发器内部,所以READ
请求被转换为WRITE
请求。t3
被锁定以进行读取,因为它只能从触发器内读取。t4
被锁定写入,因为它可能会在触发器内更新。
表锁定限制和条件
你可以安全地用于KILL
终止等待表锁定的会话。见 第 13.7.8.4 节,“KILL 语句”。
LOCK TABLES
并且 UNLOCK TABLES
不能在存储的程序中使用。
performance_schema
数据库中的 表不能用 锁定LOCK TABLES
,表除外 。 setup_
xxx
生成的锁的范围LOCK TABLES
是单个 MySQL 服务器。它与 NDB Cluster 不兼容,它无法在mysqld 的多个实例之间强制执行 SQL 级锁。你可以改为在 API 应用程序中强制锁定。有关更多信息,请参阅 第 23.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
语句显式地在这些表中的任何一个上加锁,该表必须是唯一被锁定的表;没有其他表可以用相同的语句锁定。
LOCK TABLES 和 UNLOCK TABLES 语句:通常,你不需要锁定表,因为所有单个 UPDATE
语句都是原子的;没有其他会话可以干扰任何其他当前正在执行的 SQL 语句。但是,在某些情况下,锁定表可能会提供优势:
- 如果要对一组
MyISAM
表运行许多操作 ,锁定要使用的表要快得多。锁定MyISAM
表可以加快插入、更新或删除它们的速度,因为 MySQL 在UNLOCK TABLES
调用之前不会刷新锁定表的密钥缓存 。通常,在每个 SQL 语句之后刷新键缓存。锁定表的缺点是没有会话可以更新READ
-locked 表(包括持有锁的表),并且WRITE
除了持有锁的表之外,没有会话可以访问 -locked 表。 - 如果你将表用于非事务性存储引擎,则必须使用
LOCK TABLES
if 你想确保没有其他会话修改 aSELECT
和 之间的表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()
以高速实现。请参阅 第 12.15 节,“锁定功能”。