文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

MySQL中Lock Tables和Unlock Tables浅析

2024-12-03 09:56

关注

本文转载自微信公众号「DBA闲思杂想录」,作者潇湘隐者。转载本文请联系DBA闲思杂想录公众号。  

在MySQL中提供了锁定表(lock tables)和解锁表(unlock tables)的语法功能,ORACLE与SQL Server数据库当中没有这种语法。相信刚接触MySQL的人,都想详细、深入的了解一下这个功能.下面就尽量全面的解析、总结一下MySQL中lock tables与unlock tables的功能,如有不足或不正确的地方,欢迎指点一二。

锁定表的语法:

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

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

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

  1. [LOW_PRIORITY] WRITE lock: 
  2.  
  3. The session that holds the lock can read and write the table
  4.  
  5. Only the session that holds the lock can access the tableNo other session can access it until the lock is released. 
  6.  
  7. Lock requests for the table by other sessions block while the WRITE lock is held. 
  8.  
  9. The LOW_PRIORITY modifier has no effect. In previous versions of MySQL, it affected locking behavior, but this is no longer trueAs 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做了不少介绍,下面我们通过一些测试例子来深入的理解一下锁表与解锁表的相关知识点。我们先准备一下测试环境用的表和数据。

  1. mysql> create table test( id intname varchar(12)); 
  2. Query OK, 0 rows affected (0.07 sec) 
  3.  
  4. mysql> insert into test 
  5.     -> select 10001, 'kerry'   union all 
  6.     -> select 10002, 'richard' union all 
  7.     -> select 10003, 'jimmy' ; 
  8. Query OK, 3 rows affected (0.05 sec) 
  9. Records: 3  Duplicates: 0  Warnings: 0 
  10.  
  11. mysql>  

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

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

  1. mysql> select connection_id(); 
  2. +-----------------+ 
  3. | connection_id() | 
  4. +-----------------+ 
  5. |              61 | 
  6. +-----------------+ 
  7. 1 row in set (0.00 sec) 
  8.  
  9. mysql> show open tables where in_use >=1; 
  10. Empty set (0.00 sec) 
  11.  
  12. mysql> lock tables test read
  13. Query OK, 0 rows affected (0.00 sec) 
  14.  
  15. mysql> show open tables where in_use >=1; 
  16. +----------+-------+--------+-------------+ 
  17. Database | Table | In_use | Name_locked | 
  18. +----------+-------+--------+-------------+ 
  19. | MyDB     | test  |      1 |           0 | 
  20. +----------+-------+--------+-------------+ 
  21. 1 row in set (0.01 sec) 
  22.  
  23. mysql> select * from test; 
  24. +-------+---------+ 
  25. | id    | name    | 
  26. +-------+---------+ 
  27. | 10001 | kerry   | 
  28. | 10002 | richard | 
  29. | 10003 | jimmy   | 
  30. +-------+---------+ 
  31. rows in set (0.00 sec) 
  32.  
  33. mysql> insert into test 
  34.     -> values(10004, 'ken'); 
  35. ERROR 1099 (HY000): Table 'test' was locked with a READ lock and can't be updated 
  36. mysql>  

 

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

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

  1. mysql> create table test2( id int , name varchar(12)) engine=MyISAM; 
  2. Query OK, 0 rows affected (0.05 sec) 
  3.  
  4. mysql> insert into test2 
  5.     -> select 1001, 'test'
  6. Query OK, 1 row affected (0.00 sec) 
  7. Records: 1  Duplicates: 0  Warnings: 0 
  8. mysql> select connection_id(); 
  9. +-----------------+ 
  10. | connection_id() | 
  11. +-----------------+ 
  12. |              66 | 
  13. +-----------------+ 
  14. 1 row in set (0.00 sec) 
  15.  
  16. mysql> lock tables test2 read local
  17. Query OK, 0 rows affected (0.00 sec) 
  18.  
  19. mysql> select * from test2; 
  20. +------+------+ 
  21. | id   | name | 
  22. +------+------+ 
  23. | 1001 | test | 
  24. +------+------+ 
  25. 1 row in set (0.00 sec) 
  26.  
  27. mysql> insert into test2 
  28.     -> select 1002, 'kkk'
  29. ERROR 1099 (HY000): Table 'test2' was locked with a READ lock and can't be updated 
  30. mysql>  

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

  1. mysql> select connection_id(); 
  2. +-----------------+ 
  3. | connection_id() | 
  4. +-----------------+ 
  5. |              65 | 
  6. +-----------------+ 
  7. 1 row in set (0.00 sec) 
  8.  
  9. mysql> select * from test2; 
  10. +------+------+ 
  11. | id   | name | 
  12. +------+------+ 
  13. | 1001 | test | 
  14. +------+------+ 
  15. 1 row in set (0.00 sec) 
  16.  
  17. mysql> insert into test2 
  18.     -> select 1002, 'kkk'
  19. Query OK, 1 row affected (0.00 sec) 
  20. 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,

  1. mysql> select connection_id(); 
  2. +-----------------+ 
  3. | connection_id() | 
  4. +-----------------+ 
  5. |              61 | 
  6. +-----------------+ 
  7. 1 row in set (0.00 sec) 
  8.  
  9. mysql> lock table test read
  10. Query OK, 0 rows affected (0.00 sec) 
  11.  
  12. mysql> show open tables where in_use >=1; 
  13. +----------+-------+--------+-------------+ 
  14. Database | Table | In_use | Name_locked | 
  15. +----------+-------+--------+-------------+ 
  16. | MyDB     | test  |      1 |           0 | 

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

  1. mysql> select connection_id(); 
  2. +-----------------+ 
  3. | connection_id() | 
  4. +-----------------+ 
  5. |              62 | 
  6. +-----------------+ 
  7. 1 row in set (0.01 sec) 
  8.  
  9. mysql> select * from test; 
  10. +-------+---------+ 
  11. | id    | name    | 
  12. +-------+---------+ 
  13. | 10001 | kerry   | 
  14. | 10002 | richard | 
  15. | 10003 | jimmy   | 
  16. +-------+---------+ 
  17. rows in set (0.00 sec) 
  18.  
  19. mysql> lock tables test read
  20. Query OK, 0 rows affected (0.00 sec) 
  21.  
  22. mysql> show open tables where in_use >=1; 
  23. +----------+-------+--------+-------------+ 
  24. Database | Table | In_use | Name_locked | 
  25. +----------+-------+--------+-------------+ 
  26. | MyDB     | test  |      2 |           0 | 
  27. +----------+-------+--------+-------------+ 
  28. 1 row in set (0.00 sec) 
  29.  
  30. mysql> unlock tables; 
  31. Query OK, 0 rows affected (0.00 sec) 
  32.  
  33. mysql> show open tables where in_use >=1; 
  34. +----------+-------+--------+-------------+ 
  35. Database | Table | In_use | Name_locked | 
  36. +----------+-------+--------+-------------+ 
  37. | MyDB     | test  |      1 |           0 | 
  38. +----------+-------+--------+-------------+ 
  39. 1 row in set (0.00 sec) 
  40.  
  41. mysql> lock tables test write; 

 

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

 

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

  1. mysql> lock table test write; 
  2. 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

  1. mysql> unlock tables; 
  2. Query OK, 0 rows affected (0.00 sec) 
  3.  
  4. mysql> select connection_id(); 
  5. +-----------------+ 
  6. | connection_id() | 
  7. +-----------------+ 
  8. |              61 | 
  9. +-----------------+ 
  10. 1 row in set (0.00 sec) 
  11.  
  12. mysql> show open tables where in_use >=1; 
  13. Empty set (0.00 sec) 
  14.  
  15. mysql> lock tables test write; 
  16. Query OK, 0 rows affected (0.00 sec) 
  17.  
  18. mysql> select * from test; 
  19. +-------+---------+ 
  20. | id    | name    | 
  21. +-------+---------+ 
  22. | 10001 | kerry   | 
  23. | 10002 | richard | 
  24. | 10003 | jimmy   | 
  25. +-------+---------+ 
  26. rows in set (0.00 sec) 
  27.  
  28. mysql> update test set name='ken' where id=10003; 
  29. Query OK, 1 row affected (0.01 sec) 
  30. Rows matched: 1  Changed: 1  Warnings: 0 
  31.  
  32. mysql>  

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

  1. mysql> unlock tables; 
  2. Query OK, 0 rows affected (0.00 sec) 
  3.  
  4. mysql>  
  5. mysql>  
  6. mysql> show open tables where in_use >=1; 
  7. +----------+-------+--------+-------------+ 
  8. Database | Table | In_use | Name_locked | 
  9. +----------+-------+--------+-------------+ 
  10. | MyDB     | test  |      1 |           0 | 
  11. +----------+-------+--------+-------------+ 
  12. 1 row in set (0.00 sec) 
  13.  
  14. mysql> select * from test; 

  1. mysql> select connection_id(); 
  2. +-----------------+ 
  3. | connection_id() | 
  4. +-----------------+ 
  5. |              63 | 
  6. +-----------------+ 
  7. 1 row in set (0.00 sec) 
  8.  
  9. mysql> show processlist; 
  10. +----+------+-----------+------+---------+------+---------------------------------+--------------------+ 
  11. | Id | User | Host      | db   | Command | Time | State                           | Info               | 
  12. +----+------+-----------+------+---------+------+---------------------------------+--------------------+ 
  13. | 61 | root | localhost | MyDB | Sleep   |   86 |                                 | NULL               | 
  14. | 62 | root | localhost | MyDB | Query   |   40 | Waiting for table metadata lock | select * from test | 
  15. | 63 | root | localhost | MyDB | Query   |    0 | init                            | show processlist   | 
  16. | 64 | root | localhost | MyDB | Sleep   | 2551 |                                 | NULL               | 
  17. +----+------+-----------+------+---------+------+---------------------------------+--------------------+ 
  18. rows in set (0.00 sec) 

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

  1. mysql> lock tables test read
  2. Query OK, 0 rows affected (0.00 sec) 
  3.  
  4. mysql> show open tables where in_use >=1; 
  5. +----------+-------+--------+-------------+ 
  6. Database | Table | In_use | Name_locked | 
  7. +----------+-------+--------+-------------+ 
  8. | MyDB     | test  |      1 |           0 | 
  9. +----------+-------+--------+-------------+ 
  10. 1 row in set (0.00 sec) 
  11.  
  12. mysql> lock tables worklog read
  13. Query OK, 0 rows affected (0.00 sec) 
  14.  
  15. mysql> show open tables where in_use >=1; 
  16. +----------+---------+--------+-------------+ 
  17. Database | Table   | In_use | Name_locked | 
  18. +----------+---------+--------+-------------+ 
  19. | MyDB     | worklog |      1 |           0 | 
  20. +----------+---------+--------+-------------+ 
  21. 1 row in set (0.00 sec) 
  22.  
  23. mysql>  

 

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

  1. mysql> show open tables where in_use >=1; 
  2. Empty set (0.00 sec) 
  3.  
  4. mysql> lock tables test read, worklog read
  5. Query OK, 0 rows affected (0.00 sec) 
  6.  
  7. mysql> show open tables where in_use >=1; 
  8. +----------+---------+--------+-------------+ 
  9. Database | Table   | In_use | Name_locked | 
  10. +----------+---------+--------+-------------+ 
  11. | MyDB     | worklog |      1 |           0 | 
  12. | MyDB     | test    |      1 |           0 | 
  13. +----------+---------+--------+-------------+ 
  14. rows in set (0.00 sec) 
  15.  
  16. mysql>  

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

  1. mysql> create table test2( id int, sex bit); 
  2. Query OK, 0 rows affected (0.06 sec) 
  3.  
  4. mysql> insert into test2 
  5.     -> select 10001, 1 union all 
  6.     -> select 10002, 0 union all 
  7.     -> select 10003, 1; 
  8. Query OK, 3 rows affected (0.02 sec) 
  9. Records: 3  Duplicates: 0  Warnings: 0 
  10. mysql> create view v_test 
  11.     -> as 
  12.     -> select t1.id, t1.name, t2.sex 
  13.     -> from test t1 left join test2 t2 on t1.id =t2.id; 
  14. Query OK, 0 rows affected (0.01 sec) 
  15. mysql> lock tables v_test read
  16. Query OK, 0 rows affected (0.00 sec) 
  17.  
  18. mysql> show open tables where in_use >=1; 
  19. +----------+-------+--------+-------------+ 
  20. Database | Table | In_use | Name_locked | 
  21. +----------+-------+--------+-------------+ 
  22. | MyDB     | test2 |      1 |           0 | 
  23. | MyDB     | test  |      1 |           0 | 
  24. +----------+-------+--------+-------------+ 
  25. rows in set (0.00 sec) 
  26.  
  27. mysql>  

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

  1. mysql> unlock tables; 
  2. Query OK, 0 rows affected (0.00 sec) 
  3.  
  4. mysql> create temporary table tmp like test; 
  5. Query OK, 0 rows affected (0.04 sec) 
  6.  
  7. mysql> show open tables where in_use >=1; 
  8. Empty set (0.00 sec) 
  9.  
  10. mysql> select database(); 
  11. +------------+ 
  12. database() | 
  13. +------------+ 
  14. | MyDB       | 
  15. +------------+ 
  16. 1 row in set (0.00 sec) 
  17.  
  18. mysql> select * from tmp; 
  19. Empty set (0.00 sec) 
  20.  
  21. mysql> insert into tmp 
  22.     -> select 1001, 'kerry' ; 
  23. Query OK, 1 row affected (0.01 sec) 
  24. Records: 1  Duplicates: 0  Warnings: 0 
  25.  
  26. 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:

 

 

来源:DBA闲思杂想录 内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯