Mysql事物与Metadata lock 问题

环境说明:

MySQL 5.6.16

OS:Linux RedHat 6.2 64bit

1.问题描述

目前新上一个使用MySQL数据库项目,在数据库中,每隔5分钟做truncate某个表操作,经常出现metadata lock锁等待,导致后面的对这个表的所有操作(包括读)全部metadata lock等待。严重影响了数据库运行。

且metadata lock锁等待不同于普通的行级锁,等待超时时间默认为365天,而普通的行级锁超时是120s

mysql> show variables like ‘%lock_wait%‘;

+--------------------------+----------+

| Variable_name            | Value    |

+--------------------------+----------+

| innodb_lock_wait_timeout | 120      |

| lock_wait_timeout        | 31536000 |

+--------------------------+----------+

2 rows in set (0.00 sec)

因此如果metadata lock锁的源头不释放,则会一直阻塞,必须需要人为干预。

2.为什么需要Metadata lock

Metadata lock介绍:参考官方手册:http://dev.mysql.com/doc/refman/5.6/en/metadata-locking.html

MySQL 5.5.3 and up uses metadata locking to manage access to objects (tables, triggers, and so forth). Metadata locking is used to ensure data consistency but does involve some overhead, which increases as query volume increases. Metadata contention increases the more that multiple queries attempt to access the same objects.

参考MySQL bug989:http://bugs.mysql.com/bug.php?id=989

该bug是一个比较著名的问题:

我们知道,binlog内操作的记录是基于事务的提交顺序进行的,如果有一个事务未执行完成,而这个时候drop了表,这样在从库的执行顺序就会出现问题。

因此MySQL在5.5.3版本后引入了Metadata lock锁,事务释放后才会释放Metadata lock,这样在事务完成期间,是不能进行DDL操作的。

3.Metadata lock监控

当对表的DDL操作很慢的时候,可以通过如下方法查看当前是否是在等待Metadata lock:

mysql> select * from information_schema.processlist where state = ‘Waiting for table metadata lock‘;

+----+------+-----------+------+---------+------+---------------------------------+--------------------------+

| ID | USER | HOST      | DB   | COMMAND | TIME | STATE                           | INFO                     |

+----+------+-----------+------+---------+------+---------------------------------+--------------------------+

|  7 | root | localhost | NULL | Query   |   56 | Waiting for table metadata lock | truncate table baofeng.a |

|  9 | root | localhost | NULL | Query   |    4 | Waiting for table metadata lock | select * from baofeng.a  |

+----+------+-----------+------+---------+------+---------------------------------+--------------------------+

2 rows in set (0.00 sec)

其中线程ID为7的truncate被其他事务阻塞,而线程ID为9的被truncate table阻塞,因此该查询主要看哪个会话在做DDL操作,其他的会话的state为metadata lock均为被该DDL阻塞。

那么问题来了,怎么去判断DDL被什么锁住了?

这个从目前来看比较困难,不能直观的去判断,网上有人做了一个插件可以实现(在MariaDB 10中默认已提供类似功能):

http://xiezhenye.com/2013/07/mysql-plugin-mdl-locks.html

[[email protected] tmp]# tar -xzvf mysql-5.6.16.tar.gz

[[email protected] tmp]# unzip mysql-plugin-mdl-info-master.zip

[[email protected] tmp]# cp -r ./mysql-plugin-mdl-info-master/src ./mysql-5.6.16/plugin/mdl_info

[[email protected] tmp]# cd ./mysql-5.6.16

[[email protected] mysql-5.6.16]# cmake \

-DCMAKE_INSTALL_PREFIX=/home/mysql/mysql \

-DMYSQL_DATADIR=/home/mysql/data \

-DMYSQL_TCP_PORT=3306

#注意,mysql是以源代码编译出的debug版本,那编译插件的时候不要加 -DBUILD_CONFIG=mysql_release

[[email protected] mysql-5.6.16]# cd plugin/mdl_info/

[[email protected] mdl_info]# make

[[email protected] mdl_info]# make install

mysql> INSTALL PLUGIN MDL_LOCKS SONAME ‘mdl_info.so‘;

ERROR 1127 (HY000): Can‘t find symbol ‘MDL_LOCKS‘ in library

mysql>

mysql>

mysql> show variables like ‘%plugin%‘;

+---------------+-------------------------------+

| Variable_name | Value                         |

+---------------+-------------------------------+

| plugin_dir    | /home/mysql/mysql/lib/plugin/ |

+---------------+-------------------------------+

1 row in set (0.00 sec)

mysql> INSTALL PLUGIN MDL_info SONAME ‘mdl_info.so‘;

Query OK, 0 rows affected (0.02 sec)

#这里plugin_name为MDL_INFO,而非文档中说的MDL_LOCKS

mysql> select * from information_schema.mdl_info;

+-----------+-------------+---------------------+-----------+----------+------+

| THREAD_ID | DURATION    | TYPE                | NAMESPACE | DATABASE | NAME |

+-----------+-------------+---------------------+-----------+----------+------+

|         6 | TRANSACTION | SHARED_READ         | TABLE     | baofeng  | a    |

|         7 | STATEMENT   | INTENTION_EXCLUSIVE | GLOBAL    |          |      |

|         7 | TRANSACTION | INTENTION_EXCLUSIVE | SCHEMA    | baofeng  |      |

+-----------+-------------+---------------------+-----------+----------+------+

3 rows in set (0.00 sec)

这里可以看到线程6阻塞了线程7的truncate操作

通过如下语句查看相应的会话情况:

mysql> select

->     a.*,b.user,b.host,b.command,b.time,b.state,b.info

-> from

->     information_schema.mdl_info a,

->     information_schema.PROCESSLIST b

-> where

->     a.thread_id = b.id\G;

*************************** 1. row ***************************

THREAD_ID: 6

DURATION: TRANSACTION

TYPE: SHARED_READ

NAMESPACE: TABLE

DATABASE: baofeng

NAME: a

user: root

host: localhost

command: Sleep

time: 1035

state:

info: NULL

*************************** 2. row ***************************

THREAD_ID: 7

DURATION: STATEMENT

TYPE: INTENTION_EXCLUSIVE

NAMESPACE: GLOBAL

DATABASE:

NAME:

user: root

host: localhost

command: Query

time: 990

state: Waiting for table metadata lock

info: truncate table baofeng.a

*************************** 3. row ***************************

THREAD_ID: 7

DURATION: TRANSACTION

TYPE: INTENTION_EXCLUSIVE

NAMESPACE: SCHEMA

DATABASE: baofeng

NAME:

user: root

host: localhost

command: Query

time: 990

state: Waiting for table metadata lock

info: truncate table baofeng.a

3 rows in set (0.00 sec)

ERROR:

No query specified

4.导致Metadata Lock的场景

参考:http://www.tuicool.com/articles/ja6zia

场景1:

会话1正在对表a进行DML操作(包括query),这个时候会话2执行DDL操作,需要获取metadata独占锁,因此等待会话1。

这个时候可以通过show processlist能查看该会话(该会话的state不会waiting for table metadata lock)

场景2:

会话1对表a进行DML(包括query)事务操作后,没有commit/rollback,这个时候show processlist是看到的只是会话处于sleep状态,执行的SQL显示为空。而这个时候会话2执行DDL操作,同样获取不到metadata独占锁,就会等待。可以通过查询系统事务表有体现:

mysql> select * from information_schema.innodb_trx\G;

*************************** 1. row ***************************

trx_id: 11134

trx_state: RUNNING

trx_started: 2014-12-23 10:23:44

trx_requested_lock_id: NULL

trx_wait_started: NULL

trx_weight: 0

trx_mysql_thread_id: 9

trx_query: NULL

trx_operation_state: NULL

trx_tables_in_use: 0

trx_tables_locked: 0

trx_lock_structs: 0

trx_lock_memory_bytes: 360

trx_rows_locked: 0

trx_rows_modified: 0

trx_concurrency_tickets: 0

trx_isolation_level: READ COMMITTED

trx_unique_checks: 1

trx_foreign_key_checks: 1

trx_last_foreign_key_error: NULL

trx_adaptive_hash_latched: 0

trx_adaptive_hash_timeout: 10000

trx_is_read_only: 0

trx_autocommit_non_locking: 0

1 row in set (0.00 sec)

ERROR:

No query specified

但是如果事务很多,则没办法判断是哪个会话导致。

该场景最为普遍,而且是最频发,后面重点会对该场景进行测试。

场景3:

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

官方手册上对此的说明如下:

If the server acquires metadata locks for a statement that is syntactically valid but fails during execution, it does not release the locks early. Lock release is still deferred to the end of the transaction because the failed statement is written to the binary log and the locks protect log consistency.

也就是说除了语法错误,其他错误语句获取到的锁在这个事务提交或回滚之前,仍然不会释放掉。because the failed statement is written to the binary log and the locks protect log consistency 但是解释这一行为的原因很难理解,因为错误的语句根本不会被记录到二进制日志

5.如何快速处理Metadata lock

如果是手工执行的DDL操作,例如加字段、drop表等,可以手工Cancel,先查找对当前该表是否在执行长事务操作,或者有未提交事务。确认没问题后再执行DDL操作。

如果是应用程序中执行的DDL操作,例如truncate,这个时候没办法调整应用,则可以利用MDL插件,去查询当前的DDL被哪个会话阻塞,kill掉该会话线程。这个方法前提是需要安装MDL插件,目前我们的环境还都没有安装。

6.如何避免Metadata Lock

6.1.关注autocommit

autocommit分成2个层次:

  • 数据库autocommit
  • 客户端工具的autocommit

MySQL默认的autocommit为1,即自动提交,这种方式不太安全,因为事务默认不受人为控制,因此建议关闭autocommit。咨询了支付宝的MySQL DBA,支付宝的MySQL的autocommit全部是关闭的。

客户端工具分为2种,一种是继承数据库的autocommit模式,例如SQLyog、Mysql命令行接口;还有一种是自己独立的autocommit,例如MySQL workbench,设置工具本身的autocommit,而无视数据库层面autocommit。

无论是开发还是维护,一定要弄清楚自己的客户端的autocommit模式。

无论是使用哪种客户端工具,首先要弄清楚当前环境下的autocommit方式是什么,如果不是autocommit,一定要确保所有的操作都需要显示的commit/rollback,否则即使是select查询某个表,甚至是语义(select一个错误的字段)报错,也会造成对其他会话对该表的DDL的metadata lock等待。

6.2.开发中注意事项

1、首先要确认驱动中的autocommit级别,例如JDBC中,默认conn.setAutoCommit()=true,

当在该模式下,无论做select还是DML操作,均会自动提交,不会造成应用阻塞DDL操作。

2、当我们需要开启事务,设置conn.setAutoCommit(false),任何SQL操作(包括读)操作后 需要显式的调用conn.commit(),或者事务完成后conn.setAutoCommit(true)开启默认自动提交,才会释放元数据锁。

3、注意SQL执行后,一定要确保在很短的时间内显式commit/rollback或者conn.setAutoCommit(true)

做了相关测试,数据库的autocommit参数的设置结果,与应用中的conn.setAutoCommit(false/true)没有任何关系。

来自为知笔记(Wiz)

时间: 2024-10-10 09:53:39

Mysql事物与Metadata lock 问题的相关文章

RDS MySQL 表上 Metadata lock 的产生和处理

1. Metadata lock wait 出现的场景 2. Metadata lock wait 的含义 3. 导致 Metadata lock wait 等待的活动事务 4. 解决方案 5. 如何避免出现长时间 Metadata lock wait 导致表上相关查询阻塞,影响业务 1. Metadata lock wait 出现的场景 创建.删除索引 修改表结构 表维护操作(optimize table.repair table 等) 删除表 获取表上表级写锁 (lock table tab

MySQL出现Waiting for table metadata lock的原因以及解决方法

转自:http://ctripmysqldba.iteye.com/blog/1938150 (有修改) MySQL在进行alter table等DDL操作时,有时会出现Waiting for table metadata lock的等待场景.而且,一旦alter table TableA的操作停滞在Waiting for table metadata lock的状态,后续对TableA的任何操作(包括读)都无法进行,因为他们也会在Opening tables的阶段进入到Waiting for

MySQL出现Waiting for table metadata lock的原因以及解决方法(转)

MySQL在进行alter table等DDL操作时,有时会出现Waiting for table metadata lock的等待场景.而且,一旦alter table TableA的操作停滞在Waiting for table metadata lock的状态,后续对TableA的任何操作(包括读)都无法进行,因为他们也会在Opening tables的阶段进入到Waiting for table metadata lock的锁等待队列.如果是产品环境的核心表出现了这样的锁等待队列,就会造成

MySQL metadata lock

什么是元数据 描述数据库中的数据的数据都是元数据,如库名.表明.列名.版本名,和show语句展示的大多数内容都是元数据,以及在information_shema中记录数据库对象的表中的内容也是元数据 为什么MySQL要设置元数据锁 为了保证可以并发访问数据库对象及保证数据的一致性,所以应用metadata lock,如session1正在扫描t表数据,此会话持有t表的元数据锁,这时session2话尝试要drop t表,在尝试获取t表元数据锁的时候被阻塞,假如没有MDL的设计,那么在sessio

mysql metadata lock锁

很多情况下,很多问题从理论上或者管理上而言都是可以避免或者说很好解决的,但是一旦涉及到现实由于管理或者协调或者规范执行的不够到位,就会出现各种各样本不该出现的问题,这些问题的通常在生产环境并不会出现,但是现实是无论在任何环节出现,都得去找到解决方法,很多时候原因是一部分,预防措施也是一部分,但解决方法也是必须的,因为不可能跟所有的开发人员说你按照我说的做就没有问题了,因为总会有人疏忽了或者忽视了. 前两天,测试环境升级脚本,跑到一半就报锁超时了,好几次后测试让协助而解决下.看了下,是个trunc

mysql metadata lock(二)

上一篇<mysql metadata lock(一)>介绍了为什么引入MDL,MDL作用以及MDL锁导致阻塞的几种典型场景,文章的最后还留下了一个小小的疑问.本文将更详细的介绍MDL,主要侧重介绍MDL的原理和实现.一般而言,商业数据库系统实现锁,一般将锁划分为读锁(共享锁)和写锁(排它锁),为了进一步提高并发性,还会加入意向共享锁和意向排它锁.但是偏偏mysql的MDL搞地比较复杂,但目的也是为了提高并发度.MDL包含有9种类型,详细参考表1.主要其实也是两大类,只是对共享锁做了进一步细分.

关于mysql的metadata lock

昨天晚上上线,却发现一个ddl语句长时间没有生效 查processlist, 发现包括ddl语句在内的众多查询提示 “Waiting for table metadata lock” 唯一没有该提示的查询为一个全表查询,并且Time项数值最大. kill掉这个查询的线程,后面的ddl语句正常进行了 之前一直听说metadata lock,就是元数据锁,也叫字典锁或者表结构锁.但是没有遇到过. 后来又试了一下——只要在session1里有未完成的增删查改事务,如果在另一个session2中出现加表

mysql出现Waiting for table metadata lock的原因及解决方案

最近经常遇到mysql数据库死锁,郁闷死,show processlist; 时 Waiting for table metadata lock 能一直锁很久 下面有官网的一段话,可以理解下http://dev.mysql.com/doc/refman/5.5/en/metadata-locking.html 8.10.4. Metadata LockingMySQL 5.5.3 and up uses metadata locking to manage access to objects (

【MySQL经典案例分析】 Waiting for table metadata lock

本文由云+社区发表 一. 问题是这样来的 ? 2018年某个周末,接到连续数据库的告警,告警信息如下: 二. 苦逼的探索过程 1.总体的思路 看到too many connection的报错信息,基本上可以把问题定位在: (1)机器负载飙升,导致SQL执行效率下降,导致连接推积 (2)业务访问量突增(或者有SQL注入现象),导致连接数打满 (3)出现"死锁"或者锁竞争严重,导致大量SQL堆积 2.排查过程 (1)机器的各项性能指标都显示正常, 没有出现高负载现象,暂时先排除了这种原因