解决mysql线上发生锁表问题Waiting for table metadata lock(原创)

本文详细解析了MySQL中因在线添加索引导致的锁表问题,通过复现问题场景,展示了如何使用show processlist和innodb_trx命令定位并解决锁表问题,深入探讨了MDL锁的工作原理及其对业务的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

解决mysql线上发生锁表问题(原创)

问题描述

最近,有同事用navicat打开mysql数据库,给一张表在线添加索引后, 该表一直卡主,打不开, 总在提示"正在加载"。

问题复现

初步断定应该表被锁了。复现当时的情况:
session1:对user表进行修改操作,事务不要commit。
在这里插入图片描述
session2:对user表添加索引。
在这里插入图片描述
这时打开user表,复现了当时情况,一直卡主,打不开, 总在提示"正在加载"。
在这里插入图片描述

排查过程

1 通过show processlist命令,显示用户正在运行的线程情况,此命令可帮助查看当前有问题的查询语句。查看 “stats”字段,出现了"Waiting for table metadata lock"提示表被锁住了。“info”字段显示的是正在执行的sql。
在这里插入图片描述
2 这时候需要查看未提交的事务
select trx_state, trx_started, trx_mysql_thread_id, trx_query from information_schema.innodb_trx命令,找到有问题的线程id。
在这里插入图片描述
3 kill掉trx_mysql_thread_id,将有问题的id或在session 1上commit或者rollback。
再次通过show processlist命令,发现一切正常,user表可以正常打开。

分析原因

如果当前正在执行的sql,是长事务或事务未提交, 同时又进行DDL(CREATE、DROP、ALTER 等)操作。会发生锁表现象。
详情可参考msql的官方文档
https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-limitations.html

延伸思考

参考转自:https://cloud.tencent.com/developer/article/1369229
MDL简述
为了在并发环境下维护表元数据的数据一致性,在表上有活动事务(显式或隐式)的时候,不可以对元数据进行写入操作。因此从MySQL5.5版本开始引入了MDL锁(metadata lock),来保护表的元数据信息,用于解决或者保证DDL操作与DML操作之间的一致性。
对于引入MDL,其主要解决了2个问题,一个是事务隔离问题,比如在可重复隔离级别下,会话A在2次查询期间,会话B对表结构做了修改,两次查询结果就会不一致,无法满足可重复读的要求;另外一个是数据复制的问题,比如会话A执行了多条更新语句期间,另外一个会话B做了表结构变更并且先提交,就会导致slave在重做时,先重做alter,再重做update时就会出现复制错误的现象。所以在对表进行上述操作时,如果表上有活动事务(未提交或回滚),请求写入的会话会等待在Metadata lock wait 。
支持事务的InnoDB引擎表和不支持事务的MyISAM引擎表,都会出现Metadata Lock Wait等待现象。一旦出现Metadata Lock Wait等待现象,后续所有对该表的访问都会阻塞在该等待上,导致连接堆积,业务受影响。
常见MDL锁场景
①当前有执行DML操作时执行DDL操作
② 当前有对表的长时间查询或使用mysqldump/mysqlpump时,使用alter会被堵住
③ 显示或者隐式开启事务后未提交或回滚,比如查询完成后未提交或者回滚,DDL会被堵住
④ 表上有失败的查询事务,比如查询不存在的列,语句失败返回,但是事务没有提交,此时DDL仍然会被堵住
myisam、innodb对事务的支持
Myisam是不支持事务的,innodb是支持事务的,这个概念其实没有任何问题,但是这里只的都是对于数据的事务性操作的支持,通过如下简单的实验可以很清楚的理解(关于事务的相关概念和解释就不再赘述了,只是想区别一下mysiam不支持事务,但是主动开始事务中对Myisam的操作仍然会产生MDL锁)

### 解决 MySQL 数据库中添加新字段时遇到的元数据等待问题 当尝试对结构进行修改(如 `ALTER TABLE` 操作)时,如果其他会话正在访问同一张,则可能会遇到 `Waiting for table metadata lock` 的情况。这种情况下,新的 DDL 请求会被阻塞直到现有的读写操作完成。 #### 识别并终止长时间运行的查询或事务 对于那些持有元数据定超过预期时间的情况,可以通过监控当前活动进程列来查找可能引起冲突的操作: ```sql SHOW FULL PROCESSLIST; ``` 一旦发现可疑条目,特别是处于 `Sleep` 或者执行状态较久的任务,可以考虑安全地中止它们以解除潜在的竞争条件: ```sql KILL QUERY thread_id; -- 只杀死特定查询而不影响整个连接 -- 或者 KILL CONNECTION thread_id; -- 终止整个客户端会话 ``` 这里需要注意的是,在采取行动前应当确认目标确实造成了不必要的延迟,并评估中断的影响[^1]。 #### 缩短等待超时设置 为了防止应用程序因长时间未能获得所需资源而陷入无限期挂起的状态,默认配置下的 `lock_wait_timeout` 参数被设定得非常大(即一年)。可以根据实际需求适当降低此参数值以便更快地触发错误提示而非无休止地等待: ```sql SET GLOBAL lock_wait_timeout = 1800; -- 设置全局变量为半小时 ``` 不过这样做仅能改变后续请求的行为模式;对于已经发生的堵塞现象并无即时帮助[^4]。 #### 避免长事务的存在 确保所有涉及多步更新逻辑的地方都能尽快提交更改,减少并发期间内保持活跃事务的可能性。这不仅有助于缓解由共享升级带来的压力,也能间接改善整体性能现。具体做法包括但不限于优化 SQL 语句效率、批量处理小批次的数据变更等措施[^5]。 #### 使用在线 DDL 功能 自 MySQL 5.6 版本以后引入了 Online DDL 支持,使得某些类型的架构调整能够在不影响正常业务流程的前提下顺利完成。例如增加列这样的简单变动通常都可以无缝实施,前提是所使用的存储引擎支持此类特性并且遵循官方文档给出的最佳实践建议[^2]。 ```sql ALTER TABLE your_table ADD COLUMN new_column INT NOT NULL DEFAULT 0 ALGORITHM=INPLACE, LOCK=NONE; ``` 以上命令试图采用最高效的方式执行指定转换工作,同时尽可能维持对外服务不中断。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值