MySQL中Lock Tables和Unlock Tables的作用是什么

61次阅读
没有评论

共计 11444 个字符,预计需要花费 29 分钟才能阅读完成。

行业资讯    
数据库    
MySQL 中 Lock Tables 和 Unlock Tables 的作用是什么

这期内容当中丸趣 TV 小编将会给大家带来有关 MySQL 中 Lock Tables 和 Unlock Tables 的作用是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

锁定表的语法:

LOCK TABLES tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE} [, tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}] ...

LOCAL 修饰符表示可以允许在其他会话中对在当前会话中获取了 READ 锁的的表执行插入。但是当保持锁时,若使用 Server 外的会话来操纵数据库则不能使用 READ  LOCAL。另外,对于 InnoDB 表,READ LOCAL 与 READ 相同。

The LOCAL modifier enables nonconflicting INSERT statements (concurrent  inserts) by other sessions to execute while the lock is held. (See Section  8.11.3,“Concurrent Inserts”.) However, READ LOCAL cannot be used if you are  going to manipulate the database using processes external to the server while  you hold the lock. For InnoDB tables, READ LOCAL is the same as READ.

修饰符 LOW_PRIORITY 用于之前版本的 MySQL,它会影响锁定行为, 但是从 MySQL  5.6.5 以后,这个修饰符已经被弃用。如果使用它则会产生警告。

[LOW_PRIORITY] WRITE lock: The session that holds the lock can read and write the table. Only the session that holds the lock can access the table. No other session can access it until the lock is released. Lock requests for the table by other sessions block while the WRITE lock is held. The LOW_PRIORITY modifier has no effect. In previous versions of MySQL, it affected locking behavior, but this is no longer true. As of MySQL 5.6.5, it is deprecated and its use produces a warning. Use WRITE without LOW_PRIORITY instead.

解锁表的语法:

UNLOCK TABLES

LOCK TABLES 为当前会话锁定表。UNLOCK TABLES 释放被当前会话持有的任何锁。官方文档“13.3.5 LOCK TABLES and  UNLOCK TABLES Syntax”已经对 LOCK TALES 与 UNLOCK  TABLES 做了不少介绍,下面我们通过一些测试例子来深入的理解一下锁表与解锁表的相关知识点。我们先准备一下测试环境用的表和数据。

mysql  create table test( id int, name varchar(12)); Query OK, 0 rows affected (0.07 sec) mysql  insert into test -  select 10001,  kerry  union all -  select 10002,  richard  union all -  select 10003,  jimmy  ; Query OK, 3 rows affected (0.05 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql

当前会话 (会话 ID 为 61) 持有 test 表的 READ 锁后,那么当前会话只可以读该表,而不能往表中写入数据,否则就会报“Table test was  locked with a READ lock and can t be updated”这样的错误。

注意:如果使用 LOCK TABLE WRITE 锁定表后,则可以更新数据。详见后面介绍

mysql  select connection_id(); +-----------------+ | connection_id() | +-----------------+ | 61 | +-----------------+ 1 row in set (0.00 sec) mysql  show open tables where in_use   Empty set (0.00 sec) mysql  lock tables test read; Query OK, 0 rows affected (0.00 sec) mysql  show open tables where in_use   +----------+-------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+-------+--------+-------------+ | MyDB | test | 1 | 0 | +----------+-------+--------+-------------+ 1 row in set (0.01 sec) mysql  select * from test; +-------+---------+ | id | name | +-------+---------+ | 10001 | kerry | | 10002 | richard | | 10003 | jimmy | +-------+---------+ 3 rows in set (0.00 sec) mysql  insert into test -  values(10004,  ken  ERROR 1099 (HY000): Table  test  was locked with a READ lock and can t be updated mysql

其它会话也能查询表 test,但是不能修改表,如果执行 DML 操作的话,则会一直处于被阻塞状态(Waiting for table metadata  lock)。

另外,我们测试一下修饰符 LOCAL 的用途,如下所示:

mysql  create table test2( id int , name varchar(12)) engine=MyISAM; Query OK, 0 rows affected (0.05 sec) mysql  insert into test2 -  select 1001,  test  Query OK, 1 row affected (0.00 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql  select connection_id(); +-----------------+ | connection_id() | +-----------------+ | 66 | +-----------------+ 1 row in set (0.00 sec) mysql  lock tables test2 read local; Query OK, 0 rows affected (0.00 sec) mysql  select * from test2; +------+------+ | id | name | +------+------+ | 1001 | test | +------+------+ 1 row in set (0.00 sec) mysql  insert into test2 -  select 1002,  kkk  ERROR 1099 (HY000): Table  test2  was locked with a READ lock and can t be updated mysql

但是在其它会话当中,你可以看到表 test2 可以被插入。当然前提是表的存储引擎不能是 innodb 引擎,否则使用修饰符 LOCAL 和不用 LOCAL 是一样的,其它会话无法对表写入。

mysql  select connection_id(); +-----------------+ | connection_id() | +-----------------+ | 65 | +-----------------+ 1 row in set (0.00 sec) mysql  select * from test2; +------+------+ | id | name | +------+------+ | 1001 | test | +------+------+ 1 row in set (0.00 sec) mysql  insert into test2 -  select 1002,  kkk  Query OK, 1 row affected (0.00 sec) Records: 1 Duplicates: 0 Warnings: 0

那么其他会话是否也能读此表呢? 其它会话能否也能锁定该表 (LOCK TABLES READ LOCAL)? 其它会话是否也能锁定写(LOCK TABLE  WRITE) 呢?。关于这些疑问,其它会话也能读此表,其它表也能锁定该表(LOCK TABLES READ LOCAL),但是不能 LOCK TABLE  WRITE。

对于 MyISAM 表,现在用的比较少,我们还是用 InnoDB 类型的表来实验一下,在其中一个会话使用 lock table 锁定表 test,

mysql  select connection_id(); +-----------------+ | connection_id() | +-----------------+ | 61 | +-----------------+ 1 row in set (0.00 sec) mysql  lock table test read; Query OK, 0 rows affected (0.00 sec) mysql  show open tables where in_use   +----------+-------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+-------+--------+-------------+ | MyDB | test | 1 | 0 |

然后在会话 62 中进行下面测试:

mysql  select connection_id(); +-----------------+ | connection_id() | +-----------------+ | 62 | +-----------------+ 1 row in set (0.01 sec) mysql  select * from test; +-------+---------+ | id | name | +-------+---------+ | 10001 | kerry | | 10002 | richard | | 10003 | jimmy | +-------+---------+ 3 rows in set (0.00 sec) mysql  lock tables test read; Query OK, 0 rows affected (0.00 sec) mysql  show open tables where in_use   +----------+-------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+-------+--------+-------------+ | MyDB | test | 2 | 0 | +----------+-------+--------+-------------+ 1 row in set (0.00 sec) mysql  unlock tables; Query OK, 0 rows affected (0.00 sec) mysql  show open tables where in_use   +----------+-------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+-------+--------+-------------+ | MyDB | test | 1 | 0 | +----------+-------+--------+-------------+ 1 row in set (0.00 sec) mysql  lock tables test write;

如上测试所示,如果一个会话在一个表上获得一个 READ 锁后,所有其他会话只能从表中读。不能往表中写,其它会话也可在该表上获取一个 READ 锁,此时你会在 show  open tables 里面看到 in_use 的值增加。其实 LOCK TABLES  READ 是一个表锁,而且是共享锁。但是当一个会话获取一个表上的 READ 锁后,其它会话就不能获取该表的 WRITE 锁了,此时就会被阻塞,直到持有 READ 锁的会话释放 READ 锁。

该会话 (会话 61) 中则可以继续获取 WRITE 锁。当该会话获取 WRITE 锁后,其它会话则无法获取 READ 锁了

mysql  lock table test write; Query OK, 0 rows affected (0.00 sec)

另外需要注意的是,当前会话如果锁定了其中一个表,那么是无法查询其它表的。否则会报“ERROR 1100 (HY000): Table worklog  was not locked with LOCK TABLES”错误。

那么我们再来看看 WRITE 锁吧。测试前,先在上面两个会话中执行 unlock  tables 命令。然后获得表 TEST 上的一个 WRITE 锁,如下所示,当前会话可以读写表 TEST

mysql  unlock tables; Query OK, 0 rows affected (0.00 sec) mysql  select connection_id(); +-----------------+ | connection_id() | +-----------------+ | 61 | +-----------------+ 1 row in set (0.00 sec) mysql  show open tables where in_use   Empty set (0.00 sec) mysql  lock tables test write; Query OK, 0 rows affected (0.00 sec) mysql  select * from test; +-------+---------+ | id | name | +-------+---------+ | 10001 | kerry | | 10002 | richard | | 10003 | jimmy | +-------+---------+ 3 rows in set (0.00 sec) mysql  update test set name= ken  where id=10003; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql

其它会话无法读写表 TEST,都会被阻塞,当然也无法获取表 TEST 的 READ 锁或 WRITE 锁。也就是说当一个会话获得一个表上的一个 WRITE 锁后,那么只有持锁的会话才能 READ 或 WRITE 表,其他会话都会被阻止。

mysql  unlock tables; Query OK, 0 rows affected (0.00 sec) mysql  mysql  mysql  show open tables where in_use   +----------+-------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+-------+--------+-------------+ | MyDB | test | 1 | 0 | +----------+-------+--------+-------------+ 1 row in set (0.00 sec) mysql  select * from test;

mysql  select connection_id(); +-----------------+ | connection_id() | +-----------------+ | 63 | +-----------------+ 1 row in set (0.00 sec) mysql  show processlist; +----+------+-----------+------+---------+------+---------------------------------+--------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+------+-----------+------+---------+------+---------------------------------+--------------------+ | 61 | root | localhost | MyDB | Sleep | 86 | | NULL | | 62 | root | localhost | MyDB | Query | 40 | Waiting for table metadata lock | select * from test | | 63 | root | localhost | MyDB | Query | 0 | init | show processlist | | 64 | root | localhost | MyDB | Sleep | 2551 | | NULL | +----+------+-----------+------+---------+------+---------------------------------+--------------------+ 4 rows in set (0.00 sec)

UNLOCK TABLES 释放被当前会话持有的任何锁,但是当会话发出另外一个 LOCK  TABLES 时,或当服务器的连接被关闭时,当前会话锁定的所有表会隐式被解锁。下面我们也可以测试看看

mysql  lock tables test read; Query OK, 0 rows affected (0.00 sec) mysql  show open tables where in_use   +----------+-------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+-------+--------+-------------+ | MyDB | test | 1 | 0 | +----------+-------+--------+-------------+ 1 row in set (0.00 sec) mysql  lock tables worklog read; Query OK, 0 rows affected (0.00 sec) mysql  show open tables where in_use   +----------+---------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+---------+--------+-------------+ | MyDB | worklog | 1 | 0 | +----------+---------+--------+-------------+ 1 row in set (0.00 sec) mysql

那么我们如何在当前会话锁定多个表呢? 如下所示:

mysql  show open tables where in_use   Empty set (0.00 sec) mysql  lock tables test read, worklog read; Query OK, 0 rows affected (0.00 sec) mysql  show open tables where in_use   +----------+---------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+---------+--------+-------------+ | MyDB | worklog | 1 | 0 | | MyDB | test | 1 | 0 | +----------+---------+--------+-------------+ 2 rows in set (0.00 sec) mysql

另外,还有一些细节问题,LOCK TABLES 是否可以为视图、触发器、临时表加锁呢?

mysql  create table test2( id int, sex bit); Query OK, 0 rows affected (0.06 sec) mysql  insert into test2 -  select 10001, 1 union all -  select 10002, 0 union all -  select 10003, 1; Query OK, 3 rows affected (0.02 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql  create view v_test -  as -  select t1.id, t1.name, t2.sex -  from test t1 left join test2 t2 on t1.id =t2.id; Query OK, 0 rows affected (0.01 sec) mysql  lock tables v_test read; Query OK, 0 rows affected (0.00 sec) mysql  show open tables where in_use   +----------+-------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+-------+--------+-------------+ | MyDB | test2 | 1 | 0 | | MyDB | test | 1 | 0 | +----------+-------+--------+-------------+ 2 rows in set (0.00 sec) mysql

如上测试所示,对于 VIEW 加锁,LOCK TABLES 语句会为 VIEW 中使用的所有基表加锁。对触发器使用 LOCK  TABLE,那么就会锁定触发器中所包含的全部表(any tables used in triggers are also locked  implicitly)

mysql  unlock tables; Query OK, 0 rows affected (0.00 sec) mysql  create temporary table tmp like test; Query OK, 0 rows affected (0.04 sec) mysql  show open tables where in_use   Empty set (0.00 sec) mysql  select database(); +------------+ | database() | +------------+ | MyDB | +------------+ 1 row in set (0.00 sec) mysql  select * from tmp; Empty set (0.00 sec) mysql  insert into tmp -  select 1001,  kerry  ; Query OK, 1 row affected (0.01 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql

LOCK TABLES 与 UNLOCK  TABLES 只能为自己获取锁和释放锁,不能为其他会话获取锁,也不能释放由其他会话保持的锁。一个对象获取锁,需具备该对象上的 SELECT 权限和 LOCK  TABLES 权限。LOCK TABLES 语句为当前会话显式的获取表锁。最后,关于 LOCK TABLES 与事务当中锁有那些异同,可以参考官方文档:

LOCK TABLES and UNLOCK TABLES interact with the use of transactions as  follows:

LOCK TABLES is not transaction-safe and implicitly commits any active  transaction before attempting to lock the tables.

middot;UNLOCK TABLES implicitly commits any active transaction, but only if LOCK  TABLES has been used to acquire table locks. For example, in the following set  of statements,UNLOCK TABLES releases the global read lock but does not commit  the transaction because no table locks are in effect:

上述就是丸趣 TV 小编为大家分享的 MySQL 中 Lock Tables 和 Unlock Tables 的作用是什么了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注丸趣 TV 行业资讯频道。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-03发表,共计11444字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)