Welcome 微信登录

首页 / 数据库 / MySQL / RDS MySQL 表上 Metadata lock 的产生和处理

1. Metadata lock wait 出现的场景

  • 创建、删除索引
  • 修改表结构
  • 表维护操作(optimize table、repair table 等)
  • 删除表
  • 获取表上表级写锁 (lock table tab_name write)
注:
  • 支持事务的 InnoDB 引擎表和 不支持事务的 MyISAM 引擎表,都会出现 Metadata Lock Wait 等待现象。
  • 一旦出现 Metadata Lock Wait 等待现象,后续所有对该表的访问都会阻塞在该等待上,导致连接堆积,业务受影响。
 

2. Metadata lock wait 的含义

为了在并发环境下维护表元数据的数据一致性,在表上有活动事务(显式或隐式)的时候,不可以对元数据进行写入操作。因此 MySQL 引入了 metadata lock ,来保护表的元数据信息。因此在对表进行上述操作时,如果表上有活动事务(未提交或回滚),请求写入的会话会等待在 Metadata lock wait 。

3. 导致 Metadata lock wait 等待的活动事务

  • 当前有对表的长时间查询
  • 显示或者隐式开启事务后未提交或回滚,比如查询完成后未提交或者回滚。
  • 表上有失败的查询事务

4. 解决方案

  • show processlist 查看会话有长时间未完成的查询,使用kill 命令终止该查询。
  • 查询 information_schema.innodb_trx 看到有长时间未完成的事务, 使用 kill 命令终止该查询。
select concat("kill ",i.trx_mysql_thread_id,";") from information_schema.innodb_trx i,(selectid, time from information_schema.processlist where time = (selectmax(time) from information_schema.processlist where state = "Waiting for table metadata lock" and substring(info, 1, 5) in ("alter" , "optim", "repai", "lock ", "drop ", "creat"))) pwhere timestampdiff(second, i.trx_started, now()) > p.timeand i.trx_mysql_thread_idnot in (connection_id(),p.id);-- 请根据具体的情景修改查询语句-- 如果导致阻塞的语句的用户与当前用户不同,请使用导致阻塞的语句的用户登录来终止会话注:关于清理会话,请参考:RDS MySQL 如何终止会话  http://www.linuxidc.com/Linux/2016-08/134768.htm
  • 如果上面两个检查没有发现,或者事务过多,建议使用下面的查询将相关库上的会话终止
     -- RDS for MySQL 5.6select concat("kill ", a.owner_thread_id, ";")frominformation_schema.metadata_locks aleft join(select b.owner_thread_idfrominformation_schema.metadata_locks b, information_schema.metadata_locks cwhereb.owner_thread_id = c.owner_thread_idand b.lock_status = "granted"and c.lock_status = "pending") d ON a.owner_thread_id = d.owner_thread_idwherea.lock_status = "granted"and d.owner_thread_id is null;-- RDS for MySQL 5.5select concat("kill ", p1.id, ";")frominformation_schema.processlist p1,(select id, timefrominformation_schema.processlistwheretime = (select max(time)frominformation_schema.processlistwherestate = "Waiting for table metadata lock"and substring(info, 1, 5) in ("alter" , "optim", "repai", "lock ", "drop ", "creat", "trunc"))) p2wherep1.time >= p2.timeand p1.command in ("Sleep" , "Query")and p1.id not in (connection_id() , p2.id);-- RDS for MySQL 5.5 语句请根据具体的 DDL 语句情况修改查询的条件;-- 如果导致阻塞的语句的用户与当前用户不同,请使用导致阻塞的语句的用户登录来终止会话 

5. 如何避免出现长时间 metadata lock wait 导致表上相关查询阻塞,影响业务

  • 在业务低峰期执行上述操作,比如创建删除索引。
  • 在到RDS的数据库连接建立后,设置会话变量 autocommit 为 1 或者 on,比如 set autocommit=1; 或 set autocommit=on; 。
  • 考虑使用事件来终止长时间运行的事务,比如下面的例子中会终止执行时间超过60分钟的事务。create event my_long_running_trx_monitoron schedule every 60 minutestarts "2015-09-15 11:00:00"on completion preserve enable dobegindeclare v_sql varchar(500);declare no_more_long_running_trx integer default 0; declare c_tid cursor forselect concat ("kill ",trx_mysql_thread_id,";") from information_schema.innodb_trx where timestampdiff(minute,trx_started,now()) >= 60;declare continue handler for not foundset no_more_long_running_trx=1; open c_tid;repeatfetch c_tid into v_sql; set @v_sql=v_sql; prepare stmt from @v_sql; execute stmt; deallocate prepare stmt;until no_more_long_running_trx end repeat;close c_tid;end;
    注:请根据您自身情况,自行修改运行间隔和事务执行时长。
  • 执行上述1中操作前,设置会话变量 lock_wait_timeout 为较小值,比如 set lock_wait_timeout=30; 命令可以设置 metadata lock wait 的最长时间为 30 秒;避免长时间等待元数据锁影响表上其他业务查询。
 本文永久更新链接地址