MySQL强人“锁”难《死磕MySQL系列 三》

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: MySQL强人“锁”难《死磕MySQL系列 三》

前言

image.png


最近数据库老是出现下面死锁情况,借着这俩种情况出发详细的理解一下MySQL中的锁。


Lock wait timeout exceeded; try restarting transaction


image.png


Deadlock found when trying to get lock; try restarting transaction


image.png


一、MySQL中有那些锁

全局锁


根据全局两个字,就可以肯定的是给一个整体加上锁。全局锁就是对整个数据库实例加锁。


对于flush tables with read lock,执行完成后整库就处于只读状态,所有语句将被堵塞,包括增删改查、创建表、修改表结构等语句。


表锁


表锁大家都非常熟悉了,执行命令lock tables kaka read ,kaka2 write直到unlock tables之前,其它线程是无法对kaka写kaka2读的。


执行命令的这个线程也只可以对kaka读,kaka2写。


行锁


行锁是在引擎层由各个引擎自己实现的。在MySQL中Innodb存储引擎支持行锁,若不支持行锁意味着并发控制只能使用表锁,对于这种引擎的表,同一张表上任何时刻只能有一个更新在执行,这就会影响到业务并发度。(由于篇幅的原因,下期细谈)


二、全局锁

演示执行flush tables with read lock命令后数据库处于什么状态。


终端1执行全局锁命令


image.png


端口2执行删除操作,它不会直接执行成功,而是在端口1解锁后返回。


这个SQL需要3分钟的执行时间,这3分钟就是咔咔打开终端2并连接数据库的时间。


image.png


现在见证了开篇所说的全局锁直接让整个库处于只读状态,这里只演示了删除操作其它的几个操作自己尝试一下。


在蒋老师的文章中看到全局锁最典型的场景是用于逻辑备份,即是将整个库的每一个表都select存储成文本。


现在,你想想这种场景是在什么需要下出现的。


假如只有一个主库,执行了全局锁整库处于只读状态,那么业务基本停摆,产品无法使用。


此时你会有疑问我在从库上备份啊!备份期间,不能执行主库同步过来的binlog的,数据量如果非常大,将引发主从延迟过大,必须进行全量备份。


以上是全局锁引发的负面情况,但再看备份不加全局锁会出现什么问题。


相信大多数小伙伴都开发过支付类项目,接下来就用支付案例让大家很清晰的理解备份不加全局锁引发的问题。


发起一个逻辑备份。如果一个用户在备份期间购买了你公司的服务,在业务逻辑先扣除用户余额,然后给用户添加你公司对应的产品。


显然,这个逻辑没有问题的,但在特殊案例下执行备份操作就会引发问题。


若在时间顺序上先备份用户余额,然后用户发起购买,接着备份用户购买的产品表。


一个非常清晰的问题出现了,用户余额没减成功但用户却获得了对应的产品。


从用户的角度出发那是赚大发了,但这种执行顺序如果反过来的话就会产生不一样的结果。


先备份用户产品表,然后备份用户余额表,就会出现用户钱花了东西没得着,这还得了,用户都是衣食父母这不是再割父母的韭菜。


也就是说,在备份不加锁的话,不同表之间的执行备份的顺序不同,如果某个表在备份的过程中进行了更新并且成功备份而关联的表已经备份完成无法再进行跟新,此时就会出现数据不一致。


在MVCC那篇文章中提到了一个非常重要的概念一致性视图(read view),一致性视图是根据快照读那一刻所有未提交事务的集合,前提是隔离级别为可重复。


这时你应该知道要说什么了,没错就是官方大大给提供的逻辑备份工具mysqldump。


mysqldump的备份原理是通过协议连接到 MySQL 数据库,将需要备份的数据查询出来,将查询出的数据转换成对应的insert 语句,当我们需要还原这些数据时,只要执行这些 insert 语句,即可将对应的数据还原。


例如备份test库的命令为mysqldump -uroot -p test > /backup/mysqldump/test.db。


当mysqldump使用参数–single-transaction时,备份数据之前会启动一个事物,拿到一致性视图(read view),所以在整个备份的过程中是支持更新的。




既然有了官方大大提供的mysqldump工具为何还要使用flush tables with read lock来将整表锁住呢?


别忘记了刚提到的可以在备份过程中进行更新,可以更新的前提是可以得到一致性视图,获取一致性视图的前提是开启事务。这里你应该清楚,不是所有存储引擎都支持事物。


如果有的表使用了别的存储引擎不支持事物,那么就只能使用flush tables with read lock方法,说到这里希望大家尽量在创建表时都选择Innodb存储引擎。


看着好一会了,还能记得咱们要干什么吗?需求是全库处于只读状态。


如果你搭建过MySQL的主从架构,就会知道主库用来写数据,从库用来读数据并且从库不支持写入操作,可以实现这样的效果都是来自于参数readonly。


同样执行set global readonly=true也可以达到整库只读状态,那么为什么从一开始没有给大家说这个方案,那是有原因的。


一是,刚刚提到的搭建主从架构需要使用readonly来判断主库与从库。


二是,在异常处理的方式不同。如果使用flush talbes with read lock命令客户端异常后MySQL会自动释放全局锁,让整个库回到正常状态。而整库设置为readonly后,一旦发生异常就会一直处于只读状态,导致整库长时间处于不可写状态。


所以说数据库一旦加上全局锁后数据的增删改、修改表结构、修改字段等操作都会被锁住。


三、表锁

表锁跟全局锁释放的命令一致unlock tables,同样客户端断开的时候也会自动释放。


在老一辈的革命前辈处理并发都是用的表锁,应该都知道锁表的影响虽不及锁库影响大,但在今天锁的粒度已经支持到行锁了(前提是使用Innodb存储引擎,就没必要再使用行锁来处理并发了。


再来看表锁中的另一位哥们“元数据锁”(metalock)简称“MDL”,这个锁估计很少人知道,因为在实际开发过程中是不会有实际的语法来开启或关闭。


这个特性是在MySQL5.5版本后引入的,就是为了解决A线程正在查询一个表的数据,在这期间B线程修改了表的数据结构,那么就会造成查询的结果跟表结构对不上,这肯定是不行的。


当你访问一个表时会默认加上MDL写锁,不管在任何时候记住读锁与读锁之间不互斥,读锁与写锁,写锁与写锁之间互斥,知道行锁的共享锁、排它锁也是这么个理。


那么MDL 不需要显示调用,那它是在什么时候释放的?


回答是:“MDL 是在事务提交后才会释放,这意味着事务执行期间,MDL 是一直持有的。”


那么看一个场景。


首先,线程A开启事务并执行查询语句时,对表加上了MDL锁。


然后,线程B执行的是查询,并不会堵塞住,因为读与读并不冲突。


接着,线程C修改表结构,此时的线程A还未提交事务,MDL还未释放,这时的线程因无法获取到MDl写锁,就会被阻塞。


最后线程D执行查询会发生什么呢?


答案是堵塞。


到这里按照正常的逻辑,线程C没有获取到MDL的写锁,线程D是可以申请到MDL读锁的,那为什么还会堵塞呢!


这是因为申请MDL锁的操作会形成一个队列,队列中写锁获取优先级高于读锁,一旦出现MDL写锁等待,会阻塞后续该表的所有CURL操作。


到这里你有没有后背发凉,一旦你在一个未提交事务之后执行了DDL操作,那么等到的结果就是MySQL挂掉,客户端会有重试机制,DDL后所有CURD会在超时后重新发起请求,这个库的线程会很快爆满。


既然这样如何给表安全的执行DDL操作呢?


首先,必须解决到长事务,事务不提交MDL锁就无法释放。


然后,在MySQL系统表里找到infomation_schema库中的innodb_trx,可以查看当前正在执行中的事务ID,这个表在事务那期文章中也没少提。


接着,你是不是想kill掉这些长事务然后执行DDL不就得了。


试想一下,当你kill掉的下一刻一个新的事务又进来了,同时你又执行了DDL操作,后果是什么应该清楚了哈!这种操作肯定是不行的。


官方大大怎么会允许这种情况发生呢!


于是当你执行DDL操作时alter table kaka wait 30 add name可以加一个等待时间,如果在这个等待时间拿到MDL写锁最好,拿不到也不能堵塞后边的业务逻辑,先放弃。再重试执行这个命令。


四、总结

image.png

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
SQL 关系型数据库 MySQL
MySQL 锁
MySQL里常见的几种锁
52 3
|
25天前
|
存储 关系型数据库 MySQL
优化 MySQL 的锁机制以提高并发性能
【10月更文挑战第16天】优化 MySQL 锁机制需要综合考虑多个因素,根据具体的应用场景和需求进行针对性的调整。通过不断地优化和改进,可以提高数据库的并发性能,提升系统的整体效率。
45 1
|
1月前
|
关系型数据库 MySQL Java
MySQL数据锁:Record Lock,Gap Lock 和 Next-Key Lock
本文基于 MySQL 8.0.30 版本及 InnoDB 引擎,深入解析三种行锁机制:记录锁(Record Lock)、间隙锁(Gap Lock)和临键锁(Next-key Lock)。记录锁锁定索引记录,确保事务唯一修改;间隙锁锁定索引间的间隙,防止新记录插入;临键锁结合两者,锁定范围并记录自身,有效避免幻读现象。通过具体示例展示了不同锁的作用机制及其在并发控制中的应用。
106 2
|
1月前
|
存储 关系型数据库 MySQL
MySQL数据库锁:共享锁和独占锁
本文详细介绍了`InnoDB`存储引擎中的两种行级别锁:共享锁(S锁)与排他锁(X锁)。通过具体示例展示了这两种锁的工作机制及其在`InnoDB`与`MyISAM`引擎中的表现差异。文章还提供了锁的兼容性矩阵,帮助读者更好地理解锁之间的互斥关系。最后总结了两种锁的特点及适用场景。适合希望深入了解`MySQL`并发控制机制的读者阅读。
39 1
|
25天前
|
存储 关系型数据库 MySQL
MySQL锁,锁的到底是什么?
【10月更文挑战第16天】MySQL 锁锁定的是与数据和资源相关的对象,其目的是为了保证数据的一致性、避免冲突,并在并发环境下合理协调事务或操作的执行。理解锁的对象和意义对于优化数据库性能、处理并发问题至关重要。
54 0
|
2月前
|
监控 关系型数据库 MySQL
MySQL锁机制与解决死锁问题
MySQL锁机制与解决死锁问题
267 5
|
28天前
|
关系型数据库 MySQL 数据库
mysql锁详解
通过理解并合理运用MySQL中的锁机制,开发者可以有效管理数据库并发访问,平衡性能与数据一致性需求。更多关于MySQL锁的深入探讨和最佳实践,请参考专业的数据库管理资源[[深入MySQL锁机制详解
32 0
|
2月前
|
关系型数据库 MySQL 数据库
Mysql的锁
本文介绍了MySQL中表级锁和行级锁的区别,其中MyISAM仅支持表级锁,而InnoDB支持表级锁和行级锁,默认为行级锁。表级锁锁定整个表,实现简单,资源消耗少,但并发度低;行级锁仅锁定相关记录,减少冲突,提高并发度,但加锁开销大。此外,还介绍了共享锁和排他锁的概念及意向锁的作用。
|
2月前
|
存储 SQL 关系型数据库
MySQL 的锁机制,那么多的锁,该怎么区分?
MySQL 的锁机制,那么多的锁,该怎么区分?
38 0
|
3月前
|
监控 关系型数据库 MySQL
在Linux中,mysql的innodb如何定位锁问题?
在Linux中,mysql的innodb如何定位锁问题?