MySQL的全局锁和表级锁的具体使用


Posted in MySQL onAugust 23, 2021

前言

在真实的企业开发环境中使用MySQL,MySQL肯定不会只有我一个人使用,而是一个团队显式的使用MySQL,或者是业务隐式的使用MySQL,那么多个用户或者客户端连接使用的时候,我们应该考虑一个问题:如果保证数据并发访问的一致性呢?这一篇我就来聊聊MySQL的锁,不涉及MySQL的事务隔离级别。

全局锁

MySQL的全局锁会关闭所有打开的表,并使全部的表处于只读状态,它们的命令为:

# 全局锁,简称FTWRL
FLUSH TABLES WITH READ LOCK;

# 解锁命令
UNLOCK TABLES;

对FTWRL进行实验:(以下的所有实验都是在MySQL8.0.22完成的)

 

session1 session2
FLUSH TABLES WITH READ LOCK;  
select * from test limit 1;
(正常返回结果)
 
  select * from test limit 1;
(正常返回结果)
insert into test(a,b,c) values(6,6,6);
(报错)
 
  insert into test(a,b,c) values(8,8,8);# sql1
(阻塞)
UNLOCK TABLES;  
  insert into test(a,b,c) values(8,8,8);# sql1
(session1解锁后,sql1立马执行成功)

从以上实验可以得出:当执行FTWRL后,所有的表变成了只读状态,其他更新的操作将会被阻塞。

全局锁的主要作用就是做全库逻辑备份,也就是把数据库的每个表都select出来存成文本。

当备份过程中,整个数据库处于只读状态,风险也是及其的大。如果是在主库备份,将会导致所有的业务表都不能修改数据;如果是在从库备份,这个时候从库不能执行主库传过来的binlog,会导致主从延迟。

好在InnoDB存储引擎支持事务,mysqldump有一个参数single-transaction,可以在事务中创建一致性快照,然后进行所有表备份。在有这个参数下,备份期间可以进行数据修改,所以正常开发中建议使用InnoDB存储引擎。

表级锁

表级锁分为两种,一种是表锁,另一种是元数据锁。

表锁

表锁分为表读锁和表写锁,在MySQL的命令是:

# 表读锁
lock tables test read;

# 表写锁
lock tables test write;

接下来通过实验看下表读锁和表写锁有什么区别吧

表读锁

 

session1 session2
lock tables test read;  
select * from test limit1;
(正常返回结果)
 
  select * from test limit 1;
(正常返回结果)
insert into test(a,b,c) values(6,6,6);
(报错)
 
  insert into test(a,b,c) values(8,8,8); # sql1
(阻塞)
unlock tables;  
  insert into test(a,b,c) values(8,8,8); # sql1
(session1解锁后,sql1立马写入成功)

在session1会话加上了表读锁,这个时候session1和session2都可以正常的读数据,但是session1写数据会报错,session2写数据会被阻塞,等到session1解锁了,session2的写数据才能执行成功。

从这个实验可以得出:表加上了表读锁之后,本线程和其他线程都可以读数据,本线程写数据会报错,其他线程写数据会阻塞。

表写锁

 

session1 session2
lock tables test write;  
select * from test limi1;
(正常返回结果)
 
  select * from test limit 1; # sql1
(阻塞)
unlock tables;  
  select * from test limit; # sql1
(session1解锁后,sql1立马返回结果)
lock tables test write;  
insert into test(a,b,c) values(6,6,6);
(插入成功)
 
  insert into test(a,b,c) values(8,8,8);# sql 2
(阻塞)
unlock tables;  
  insert into test(a,b,c) values(8,8,8);# sql2
(session1解锁后,sql2立马执行成功)

从以上实验可以得出:表加上了表写锁之后,本线程可以进行读写操作,其他线程的读写操作都会被阻塞。

元数据锁(Metadata Locking,简称:MDL锁)

在MySQL中,数据库的DDL不属于事务范畴,如果你在session1中select一行数据,这个时候session2给这张表新增了一列xxx,这个时候可能会出现事务特性被破坏、binlog顺序错乱等bug(MySQL官网上有公布出类似的bug,感兴趣可以自行去了解)。

为了解决以上的问题,从MySQL5.5.3引入了元数据锁,MDL锁不需要显式使用,MySQL会默认加上,它的作用就是保证数据库读写正确性。以下全部用MDL表示元数据锁。

当你对一张表进行增删查改的时候会默认加上MDL读锁;当你对一张表进行表结构更改的时候会默认加上MDL写锁。

 

session1 session2 session3 session4
begin;      
select * from test lmi1;
(正常返回结果)
     
  select * from test limit 1;
(正常返回结果)
   
    alter table test add d int;
(阻塞)
 
      select * from test limit 1;
(阻塞)

一开始session1会话查询test的时候,获取到了MDL读锁,可以正常查询到数据。然后session2会话查询数据也会获取MDL读锁,不冲突,也可以正常查询到数据返回。

但是到了session3会话的时候,需要获取MDL写锁,这个时候因为session1的MDL读锁没有释放,所以会阻塞。后面session4也需要MDL读锁,但是因为session3被阻塞了,所以session4也会被阻塞。

假如这是一张线上业务表,这种场景将会使后面的任何操作都失效,表现出来就是这张表变得无法写和读。如果客户端配置了MySQL重试机制的话,会在超时的时候重新建立一个session会话重新请求,然后MySQL就会因为线程不停新增而崩溃。

从上面的例子可以知道MDL锁是在语句执行的时候默认加上的,但是语句执行完是不会释放的,只有等整个事务提交了才会释放MDL锁。

所以对于我们开发者来说,在工作中应该尽量避免慢查询、尽量保证事务及时提交、避免大事务等,对于DBA来说,也应该尽量避免在业务高峰期执行DDL操作。

总结

  • 全局锁会让所有的表变成只读状态,所有更新操作都会被阻塞
  • 表读锁是本线程和其他线程都可以读,本线程写会报错,其他线程写会阻塞
  • 表写锁是本线程可以读写,其他线程读写都会阻塞
  • 引入MDL锁解决事务和DDL同时执行引发的bug

参考资料

  • 深入浅出MySQL》第二版:20.3.8 什么时候使用表锁
  • 《MySQL实战45讲》林晓斌

到此这篇关于MySQL的全局锁和表级锁的具体使用的文章就介绍到这了,更多相关MySQL 全局锁和表级锁内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

MySQL 相关文章推荐
MySQL 如何分析查询性能
May 12 MySQL
MySQL如何使用使用Xtrabackup进行备份和恢复
Jun 21 MySQL
MySQL系列之十二 备份与恢复
Jul 02 MySQL
SQL实现LeetCode(176.第二高薪水)
Aug 04 MySQL
MySQL外键约束(FOREIGN KEY)案例讲解
Aug 23 MySQL
MySQL数据库完全卸载的方法
Mar 03 MySQL
MySQL之MyISAM存储引擎的非聚簇索引详解
Mar 03 MySQL
mysql 获取时间方式
Mar 20 MySQL
Mysql如何实现不存在则插入,存在则更新
Mar 25 MySQL
mysql 乱码 字符集latin1转UTF8
Apr 19 MySQL
MySQL如何修改字段类型和字段长度
Jun 10 MySQL
MySQL中的 inner join 和 left join的区别解析(小结果集驱动大结果集)
May 08 MySQL
MySQL令人大跌眼镜的隐式转换
Aug 23 #MySQL
SQL IDENTITY_INSERT作用案例详解
Aug 23 #MySQL
MySQL非空约束(not null)案例讲解
Aug 23 #MySQL
MySQL外键约束(FOREIGN KEY)案例讲解
Aug 23 #MySQL
MySQL 1130异常,无法远程登录解决方案详解
Aug 23 #MySQL
Node-Red实现MySQL数据库连接的方法
Aug 07 #MySQL
MySQL如何解决幻读问题
Aug 07 #MySQL
You might like
使用bcompiler对PHP文件进行加密的代码
2010/08/29 PHP
关于PHP递归算法和应用方法介绍
2013/04/15 PHP
PHP类的封装与继承详解
2015/09/29 PHP
ThinkPHP路由机制简介
2016/03/23 PHP
thinkPHP5框架自定义验证器实现方法分析
2018/06/11 PHP
Prototype Date对象 学习
2009/07/12 Javascript
最新的10款jQuery内容滑块插件分享
2011/09/18 Javascript
javascript获取隐藏元素(display:none)的高度和宽度的方法
2014/06/06 Javascript
使用node.js半年来总结的 10 条经验
2014/08/18 Javascript
js+csss实现的一个带复选框的下拉框
2014/09/29 Javascript
javascript中的previousSibling和nextSibling的正确用法
2015/09/16 Javascript
js实现仿微博滚动显示信息的效果
2015/12/21 Javascript
JS排序方法(sort,bubble,select,insert)代码汇总
2016/01/30 Javascript
浅谈JS正则表达式的RegExp对象和括号的使用
2016/07/28 Javascript
最好用的Bootstrap fileinput.js文件上传组件
2016/12/12 Javascript
express框架实现基于Websocket建立的简易聊天室
2017/08/10 Javascript
python爬取安居客二手房网站数据(实例讲解)
2017/10/19 Javascript
Angular js 实现添加用户、修改密码、敏感字、下拉菜单的综合操作方法
2017/10/24 Javascript
简化vuex的状态管理方案的方法
2018/06/02 Javascript
一步快速解决微信小程序中textarea层级太高遮挡其他组件
2019/03/04 Javascript
Element-ui DatePicker显示周数的方法示例
2019/07/19 Javascript
JavaScript多种滤镜算法实现代码实例
2019/12/10 Javascript
[44:41]Fnatic vs Liquid 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
[01:35:13]DOTA2-DPC中国联赛 正赛 DLG vs PHOENIX BO3 第一场 1月18日
2021/03/11 DOTA
Python实现基于HTTP文件传输实例
2014/11/08 Python
Nginx搭建HTTPS服务器和强制使用HTTPS访问的方法
2015/08/16 Python
python之mock模块基本使用方法详解
2019/06/27 Python
Django框架视图层URL映射与反向解析实例分析
2019/07/29 Python
Python3 JSON编码解码方法详解
2019/09/06 Python
Python使用Numpy模块读取文件并绘制图片
2020/05/13 Python
美国领先的家庭健康检测试剂盒提供商:LetsGetChecked
2019/03/18 全球购物
美术指导助理求职信
2014/04/20 职场文书
服务承诺口号
2014/05/22 职场文书
2015年环境监察工作总结
2015/07/23 职场文书
浅谈Redis在直播场景的实践方案
2021/04/27 Redis
国产动画《万圣街》日语配音版制作决定!
2022/03/20 国漫