注:之处的locke table *** 作是由服务层自身实现的表级锁,并非是存储引擎的锁类
在项目的一次需求中,需要对一个表增加字段,然而在执行增加字段的sql语句时,卡住了很久都没提交到Mysql完成,而此时对外接口服务请求也卡住了,这时中断卡住的alter table 语句,服务慢慢恢复正常,如果不搞清楚这个问题的根源,不敢增加字段,因为会直接影响到服务
通过show processlist 查看到在alter table语句执行卡住过程中,累计了大量状态为 Waiting for table metadata lock 的记录
然后查看当前的事务状态 执行 select * from information_schema.innodb_trx\G
发现了其中一条已经运行了很久的事务,我怀疑跟这个运行很久的而且没有提交的事务有关。
在本地mysql开多个终端测试
session 1: 开启事务,执行select 语句,但不提交事务
session 2:执行增加字段sql
执行被阻塞了
可以看到alter table语句的状态为Waiting for table metadata lock
session 3 : 再次查询t1表
也被阻塞了
select * from t1 再次查询t1表也是 Waiting for table metadata lock状态,说明由于 metadata lock的存在,会导致后面正常的查询都会因为等待锁而阻塞
再查看当前事务运行状态:
可以看到,session1的事务由于还没提交,所以这里能看到它的状态还是running
这时我们commit session1的事务,看看效果
session 1:
session 2:
session 3:
可以看到session1的事务提交后,session2 和session3 都正常执行了, 他们完成的时间分别是30秒和7秒
通过上面的还原测试,可以知道是由于事务没有提交而给表加了锁,导致后面alter语句因为等待锁而阻塞,从而影响后面的正常请求。
那说明我们的项目是默认开启了事务吗?
继续排查,项目是使用flask-sqlchemy的插件来管理mysql接入,然后查了下文档
在实例化sqlchemy的时候,会创建一个用于跟Mysql交互的session对象,看看源码
从 SignallingSession类的定义看来,autocommit=False,说明默认都给所有的sql执行开启事务,也就是说,哪怕是纯select语句,不需要加锁的select,我们的项目默认也需要开启事务,这对于Mysql MVCC的版本控制来说,是没必要的。
解决办法:就是在实例化SQLAlchemy的时候,给一个参数,修改的session的autocommit=True:
来自官网的介绍:
意思就是为了保证事务的串行执行,而启用的一个锁,这个锁只会在事务结束的时候释放,因此在事务提交或回滚钱,任何对这个表做的DDL *** 作,都是会阻塞的
这个 Metadata lock 是MySQL在5.5.3版本后引入了,为的是防止5.5.3以前的一个bug的出现:
当一个会话在主库执行DML *** 作还没提交时,另一个会话对同一个对象执行了DDL *** 作如drop table,而由于MySQL的binlog是基于事务提交的先后顺序进行记录的,因此在从库上应用时,就出现Q了先drop table,然后再向table中insert的情况,导致从库应用出错。
怎么解决mysql数据库只读,table spSELECT
CONCAT( 'UPDATE ', table_name, ' SET flag = 0'AS `准备要执行的sql`
FROM
information_schema.tables
WHERE
table_schema = 'database 的名字'
查询完毕以后,复制出查询结果, 粘贴一下, 执行。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)