MySQL数据库中的锁、解锁以及删除事务


Posted in MySQL onMay 06, 2022

背景

在程序员的职业生涯中,总会遇到数据库表被锁的情况,前些天就又撞见一次。由于业务突发需求,各个部门都在批量操作、导出数据,而数据库又未做读写分离,结果就是:数据库的某张表被锁了!

用户反馈系统部分功能无法使用,紧急排查,定位是数据库表被锁,然后进行紧急处理。这篇文章给大家讲讲遇到类似紧急状况的排查及解决过程,建议点赞收藏,以备不时之需。

故障追踪

用户反馈某功能页面报502错误,于是第一时间看服务是否正常,数据库是否正常。在控制台看到数据库CPU飙升,堆积大量未提交事务,部分事务已经阻塞了很长时间,基本定位是数据库层出现问题了。

查看阻塞事务列表,发现其中有锁表现象,本想利用控制台直接结束掉阻塞的事务,但控制台账号权限有限,于是通过客户端登录对应账号将锁表事务kill掉,才避免了情况恶化。

下面就聊聊,如果当突然面对类似的情况,我们该如何紧急响应?

解决方案

想象一个场景,当然也是软件工程师职业生涯中会遇到的一种场景:原本运行正常的程序,某一天突然数据库的表被锁了,业务无法正常运转,那么我们该如何快速定位是哪个事务锁了表,如何结束对应的事物?

首先最简单粗暴的方式就是:重启MySQL。对的,网管解决问题的神器——“重启”。至于后果如何,你能不能跑了,要你自己三思而后行了!

重启是可以解决表被锁的问题的,但针对线上业务很显然不太具有可行性。

下面来看看不用跑路的解决方案:

第一步:查看表使用

遇到数据库阻塞问题,首先要查询一下表是否在使用。

show open tables where in_use > 0 ;

如果查询结果为空,那么说明表没在使用,说明不是锁表的问题。

mysql>  show open tables where in_use > 0 ;
Empty set (0.00 sec)

如果查询结果不为空,比如出现如下结果:

mysql>  show open tables where in_use > 0 ;
+----------+-------+--------+-------------+
| Database | Table | In_use | Name_locked |
+----------+-------+--------+-------------+
| test     | t     |      1 |           0 |
+----------+-------+--------+-------------+
1 row in set (0.00 sec)

则说明表(test)正在被使用,此时需要进一步排查。

第二步:查看进程

查看数据库当前的进程,看看是否有慢SQL或被阻塞的线程。

执行命令:

show processlist;

该命令只显示当前用户正在运行的线程,当然,如果是root用户是能看到所有的。

在上述实践中,阿里云控制台之所以能够查看到所有的线程,猜测应该使用的就是root用户,而笔者去kill的时候,无法kill掉,是因为登录的用户非root的数据库账号,无法操作另外一个用户的线程。

第三步:查看当前运行的所有事务

如果情况紧急,此步骤可以跳过,主要用来查看核对:

SELECT * FROM information_schema.INNODB_TRX;

第四步:查看当前出现的锁

如果情况紧急,此步骤可以跳过,主要用来查看核对:

SELECT * FROM information_schema.INNODB_LOCKs;

第五步:查询锁等待的对应关系

SELECT * FROM information_schema.INNODB_LOCK_waits;

看事务表INNODB_TRX中是否有正在锁定的事务线程,看看ID是否在show processlist的sleep线程中。如果在,说明这个sleep的线程事务一直没有commit或者rollback,而是卡住了,需要手动kill掉。

搜索的结果中,如果在事务表发现了很多任务,最好都kill掉。

第六步:kill掉事务

执行kill命令:

kill 1011;

对应的线程都执行完kill命令之后,后续事务便可正常处理。

针对紧急情况,通常也会直接操作第一、第二、第六步。

MySQL的锁

这里再补充一些MySQL锁相关的知识点:数据库锁设计的初衷是处理并发问题,作为多用户共享的资源,当出现并发访问的时候,数据库需要合理地控制资源的访问规则,而锁就是用来实现这些访问规则的重要数据结构。

根据加锁的范围,MySQL里面的锁大致可以分成全局锁、表级锁和行锁三类。MySQL中表级别的锁有两种:一种是表锁,一种是元数据锁(metadata lock,MDL)。

表锁是在Server层实现的,ALTER TABLE之类的语句会使用表锁,忽略存储引擎的锁机制。表锁通过lock tables… read/write来实现,而对于InnoDB来说,一般会采用行级锁。毕竟锁住整张表影响范围太大了。

另外一个表级锁是MDL(metadata lock),用于并发情况下维护数据的一致性,保证读写的正确性,不需要显式的使用,在访问一张表时会被自动加上。

MySQL锁表场景

常见的一种锁表场景就是有事务操作处于:Waiting for table metadata lock状态。

Waiting for table metadata lock

MySQL在进行alter table等DDL操作时,有时会出现Waiting for table metadata lock的等待场景。

一旦alter table TableA的操作停滞在Waiting for table metadata lock状态,后续对该表的任何操作(包括读)都无法进行,因为它们也会在Opening tables的阶段进入到Waiting for table metadata lock的锁等待队列。如果核心表出现了锁等待队列,就会造成灾难性的后果。

场景一:长事务运行,阻塞DDL,继而阻塞所有同表的后续操作。

通过show processlist可以看到表上有正在进行的操作(包括读),此时alter table语句无法获取到metadata 独占锁,会进行等待。

场景二:为提交事务,阻塞DDL,继而阻塞所有同表的后续操作。

通过show processlist看不到表上有任何操作,但实际上存在有未提交的事务,可以在information_schema.innodb_trx中查看到。在事务没有完成之前,表上的锁不会释放,alter table同样获取不到metadata的独占锁。

处理方法:通过 select * from information_schema.innodb_trx\G, 找到未提交事物的sid,然后kill掉,让其回滚。

场景三:显式事务失败操作获得锁,未释放

通过show processlist看不到表上有任何操作,在information_schema.innodb_trx中也没有任何进行中的事务。很可能是因为在一个显式的事务中,对表进行了一个失败的操作(比如查询了一个不存在的字段),这时事务没有开始,但是失败语句获取到的锁依然有效,没有释放。从performance_schema.events_statements_current表中可以查到失败的语句。

处理方法:通过performance_schema.events_statements_current找到其sid,kill 掉该session,也可以kill掉DDL所在的session。

总之,alter table的语句是很危险的(核心是未提交事务或者长事务导致的),在操作之前要确认对要操作的表没有任何进行中的操作、没有未提交事务、也没有显式事务中的报错语句。

如果有alter table的维护任务,在无人监管的时候运行,最好通过lock_wait_timeout设置好超时时间,避免长时间的metedata锁等待。

小结

关于MySQL的锁表其实还有很多其他场景,我们在实践的过程中尽量避免锁表情况的发生,当然这需要一定经验的支撑。但更重要的是,如果发现锁表我们要能够快速的响应,快速的解决问题,避免影响正常业务,避免情况进一步恶化。所以,本文中的解决思路大家一定要收藏或记忆一下,做到有备无患,避免突然状况下抓瞎。

总结

到此这篇关于MySQL数据库表被锁、解锁以及删除事务的文章就介绍到这了,更多相关MySQL表解锁内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!


Tags in this post...

MySQL 相关文章推荐
MySQL 视图(View)原理解析
May 19 MySQL
MySQL 百万级数据的4种查询优化方式
Jun 07 MySQL
解决Mysql的left join无效及使用的注意事项说明
Jul 01 MySQL
MySQL中存储时间的最佳实践指南
Jul 01 MySQL
MySQL系列之五 视图、存储函数、存储过程、触发器
Jul 02 MySQL
MySQL Shell import_table数据导入的实现
Aug 07 MySQL
防止web项目中的SQL注入
Dec 06 MySQL
mysql中整数数据类型tinyint详解
Dec 06 MySQL
MySQL数据库中的锁、解锁以及删除事务
May 06 MySQL
MySQL串行化隔离级别(间隙锁实现)
Jun 16 MySQL
MySQL导致索引失效的几种情况
Jun 25 MySQL
分享很少见很有用的SQL功能CORRESPONDING
Aug 05 MySQL
mysql性能优化以及配置连接参数设置
May 06 #MySQL
Mysql InnoDB 的内存逻辑架构
详细介绍MySQL中limit和offset的用法
May 06 #MySQL
MySQL数据库Innodb 引擎实现mvcc锁
May 06 #MySQL
讲解MySQL增删改操作
May 06 #MySQL
解决Mysql报错 Table 'mysql.user' doesn't exist
MYSQL常用函数介绍
May 05 #MySQL
You might like
在Linux系统下一键重新安装WordPress的脚本示例
2015/06/30 PHP
PHP 中提示undefined index如何解决(多种方法)
2016/03/16 PHP
Centos 6.5下PHP 5.3安装ffmpeg扩展的步骤详解
2017/03/02 PHP
php之可变函数的实例详解
2017/09/13 PHP
PHP进阶学习之反射基本概念与用法分析
2019/06/18 PHP
权威JavaScript 中的内存泄露模式
2007/08/13 Javascript
js实现运行代码需要刷新的解决方法
2007/08/18 Javascript
javascript游戏开发之《三国志曹操传》零部件开发(四)用地图块拼成大地图
2013/01/23 Javascript
自定义jQuery选项卡插件实例
2013/03/27 Javascript
3分钟写出来的Jquery版checkbox全选反选功能
2013/10/23 Javascript
解决jquery插件冲突的问题
2014/01/23 Javascript
登陆成功后自动计算秒数执行跳转
2014/01/23 Javascript
javascript获取和判断浏览器窗口、屏幕、网页的高度、宽度等
2014/05/08 Javascript
javascript框架设计之浏览器的嗅探和特征侦测
2015/06/23 Javascript
JS实现漂亮的淡蓝色滑动门效果代码
2015/09/23 Javascript
JavaScript设计模式之单体模式全面解析
2016/09/09 Javascript
微信小程序 wx:key详细介绍
2016/10/28 Javascript
JS使用正则截取两个字符串之间的字符串实现方法详解
2017/01/06 Javascript
Node.js readline模块与util模块的使用
2018/03/01 Javascript
JS获取指定月份的天数两种实现方法
2018/06/22 Javascript
Node.js 使用request模块下载文件的实例
2018/09/05 Javascript
react脚手架如何配置less和ant按需加载的方法步骤
2018/11/28 Javascript
bootstrap下拉分页样式 带跳转页码
2018/12/29 Javascript
Node 模块原理与用法详解
2020/05/13 Javascript
[59:48]LGD vs IG 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
[01:03:56]Mineski vs TNC 2018国际邀请赛淘汰赛BO1 8.21
2018/08/22 DOTA
ubuntu系统下 python链接mysql数据库的方法
2017/01/09 Python
linux平台使用Python制作BT种子并获取BT种子信息的方法
2017/01/20 Python
python脚本作为Windows服务启动代码详解
2018/02/11 Python
python实现抠图给证件照换背景源码
2019/08/20 Python
浅谈Tensorflow 动态双向RNN的输出问题
2020/01/20 Python
pycharm 2020.2.4 pip install Flask 报错 Error:Non-zero exit code的问题
2020/12/04 Python
全球摩托车装备领导者:RevZilla
2017/09/04 全球购物
建筑工程技术应届生自荐信
2013/09/27 职场文书
毕业论文致谢部分怎么写
2015/05/14 职场文书
Mysql事务索引知识汇总
2022/03/17 MySQL