给表新增字段时,发现锁表了,查看进程,提示Waiting for table metadata lock
,等待锁释放;然而蛋疼的是几分钟过去了,依然没有任何的进展,特此记录下这个问题的定位过程以及MDL的相关背景知识python
看到上面的表现,基本问题就来了mysql
<!-- more -->git
首先须要确认什么地方加锁,从mysql出发,应该怎么定位?github
对于mysql而言,通常来说上锁和事物时伴生关系,因此咱们的直观出发点就是查找db当前正在执行的事物sql
-- 查询当前正在执行的事物的sql SELECT * FROM information_schema.INNODB_TRX;
输出结果以下,首先拿到事物对应的进程idbash
拿到id以后,则能够分析对应的进程信息session
-- 查询进程信息 show processlist -- 查询全部的进程信息 show full processlist
而后定位到具体的进程async
而后登录到目标机器,查看端口号对应的进程,经过lsof
命令查看tcp
lsof -i tcp:52951
从图中能够看出,是一个python进程的mysql链接开启的事物,进程id为5436oop
接着查看进程对应的信息
ps aux | grep 5436
这个脚本正是测试aiomysql的python脚本,内容比较简单
import asyncio import aiomysql loop = asyncio.get_event_loop() @asyncio.coroutine def test_example(): conn = yield from aiomysql.connect(host='127.0.0.1', port=3306, user='root', password='', db='test', loop=loop, autocommit=False) cur = yield from conn.cursor() yield from cur.execute("SELECT * from test_table") print(cur.description) r = yield from cur.fetchall() print(r) yield from cur.close() conn.close() loop.run_until_complete(test_example())
对python不太熟,直接借助google查一下,发现有一样的问题
这个问题抛出,在经过with打开链接获取游标后,执行mysql,可是没有commit以前,会锁表,这个期间修改表都会出现等待
下面近给出了解答,并无看到更多的深层次的说明,先记录下,解决办法就是在建立链接池的时候,选择自动提交方式,而后就不会有这个问题了
pool = await aiomysql.create_pool( host="localhost", user="test", password="test", db="test", autocommit=True, cursorclass=DictCursor, loop=loop)
找到一篇文章说MDL的,推荐详细阅读 MySQL表结构变动你不可不知的Metadata Lock详解
抓一下核心的要点,简单说一下看完这篇文章以后的朴素理解
MetaData Lock 简称为MDL,简单来讲就是表的元数据锁;当修改表结构的时候,就须要持有这个锁
MDL的主要做用只有一点,保护一个正在执行的事物表结构不被修改
有一个原则,MDL是事物级别的,只有事物结束以后才会释放,而这里面说的事物分为两类
直接看上面的说明,不太直观,一个经典的case以下
session1 开启了一个事物,执行查询操做;可是如今session2 要删除表,若是执行成功,那么session1的第二次查询就跪了,这样就违背了事物的原则,全部在5.5版本引入了MDL,来保证在事物执行期间,表结构不被修改
当咱们出现修改表结构,就须要获取MDL的排他锁,所以只有这个表没有事物在执行时,才能获取成功;当持有独占锁以后,这个表的其余操做将被阻塞(即不能插入数据,修改数据,也不能开启事物操做)
所以在执行DDL时,一直出现等待MDL的时候,常见的缘由有下面三个
经过 show processlist看到表上有正在进行的操做(包括读),此时修改表时也会等待获取MDL,这种时候解决办法要么就是等待执行完毕,要么就是直接kill掉进程
经过 show processlist没有找到表上的操做,可是经过information_schema.innodb_trx
发现有未提交的事物,
经过 show processlist 和事物查询都没有的状况下,可能的场景是一个显示的事物中,对表的操做出现了异常,虽然事物失败,可是持有的锁尚未释放,也会致使这个缘由
能够在performance_schema.events_statements_current
表中查询失败的语句
前面两小节,分别说明什么是MDL(朴素理解为表的元数据锁),以及当修改表时出现长时间的等待MDL的缘由分析;正常看完以后,应该会有下面的疑惑
对于MDL的类型,从网上截一张图
接下来须要分析下不一样锁模式对应的sql
属性 | 含义 | 事例 |
---|---|---|
MDL_INTENTION_EXCLUSIVE(IX) |
意向排他锁用于global和commit的加锁。 | truncate table t1; insert into t1 values(3,’abcde’); 会加以下锁 (GLOBAL,MDL_STATEMENT,MDL_INTENTION_EXCLUSIVE)(SCHEMA,MDL_TRANSACTION,MDL_INTENTION_EXCLUSIVE) |
MDL_SHARED(S) |
只访问元数据 好比表结构,不访问数据。 | set golbal_read_only =on 加锁 (GLOBAL,MDL_EXPLICIT,MDL_SHARED) |
MDL_SHARED_HIGH_PRIO(SH) |
用于访问information_scheam 表,不涉及数据。 |
select * from information_schema.tables;show create table xx; desc xxx; 会加以下锁: (TABLE,MDL_TRANSACTION,MDL_SHARED_HIGH_PRIO) |
MDL_SHARED_READ(SR) |
访问表结构而且读表数据 | select * from t1; lock table t1 read; 会加以下锁: (TABLE,MDL_TRANSACTION,MDL_SHARE_READ) |
MDL_SHARED_WRITE(SW) |
访问表结构而且写表数据 | insert/update/delete/select .. for update 会加以下锁:(TABLE,MDL_TRANSACTION,MDL_SHARE_WRITE) |
MDL_SHARED_UPGRADABLE(SU) |
是mysql5.6引入的新的metadata lock,能够说是为了online ddl 才引入的。特色是容许DML,防止DDL; | alter table/create index/drop index 会加该锁; 加入下锁 (TABLE,MDL_TRANSACTION,MDL_SHARED_UPGRADABLE) |
MDL_SHARED_NO_WRITE(SNW) |
可升级锁,访问表结构而且读写表数据,而且禁止其它事务写。 | alter table t1 modify c bigint; (非onlineddl) (TABLE,MDL_TRANSACTION,MDL_SHARED_NO_WRITE) |
MDL_SHARED_NO_READ_WRITE(SNRW) |
可升级锁,访问表结构而且读写表数据,而且禁止其它事务读写。 | lock table t1 write; 加锁 (TABLE,MDL_TRANSACTION,MDL_SHARED_NO_READ_WRITE |
MDL_EXCLUSIVE(X) |
防止其余线程读写元数据 | CREATE/DROP/RENAME TABLE ,其余online DDL在rename阶段也持有X锁(TABLE,MDL_TRANSACTION,MDL_EXCLUSIVE) |
上面的内容,可能信息量比较大,特别是MDL的锁分类状况,很难抓住重点,针对咱们平常接触中,简单给出小结
MDL_SW
锁MDL_SR
锁几个简称的说明
insert, update, delete
语句select
语句几个常见疑问解答
相关博文或者问答
一灰灰的我的博客,记录全部学习和工做中的博文,欢迎你们前去逛逛
尽信书则不如,已上内容,纯属一家之言,因我的能力有限,不免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
一灰灰blog
知识星球