innodb锁机制探究(一)---元数据锁
元数据锁(metadata lock,简称MDL)是用来保证并发访问数据库对象场景下的一致性而设定的。简单理解,它是为了管理数据库元数据而产生的一种锁。关于元数据锁,有以下的知识点需要注意:
1、 我们可以在performance_schema中的表metadata_locks中查看元数据的锁定信息。
2、元数据锁设计一些开销,随着查询量的增加而增加,当多个查询尝试访问相同的对象时,元数据锁的争用情况就会增加。
3、如果我们的DML语句中,存在多个表,那么他们获取锁的顺序是按照语句中的顺序来的。语句中会一个一个的获取表的元数据锁,并在这个过程中执行死锁检测。
4、DDL语句中,一般是通过按照名称顺序来获取显示命名的表上的元数据锁,从而减少并发的DDL语句。(这一句可能不好理解,后面有例子)
5、如果我们的语句中包含外键,则外键相关的表也会被当前的锁定。
说了这么多,下面看几个元数据锁的例子,默认隔离级别是RR:
可以看到,会话1中开启事务,没有提交,此时在会话2中进行drop table的操作,再在会话1上查看连接,可以看到waiting for table metadata lock的字样,就是说明会话2在等待元数据锁。如果没有元数据锁的保护,那么我们可能在事务进行的过程中就会发现指定的表突然不存在了,这是我们不想看到的。
上面第4点中,说明了按照表名称来获取元数据锁,我们举例子如下:
rename table tbla to tbld,tblc to tbla;
rename table tbla to tblb,tblc to tbla;
上面两个语句,第一句获取元数据的顺序是tbla、tblc、tbld,因为tbld的表名顺序在tblc之后,第二句中获取元数据锁的顺序是tbla、tblb、tblc因为tblb的顺序在tblc的前面。不同的获取锁的顺序,会导致最终的执行结果有可能不相同,这一点需要特别注意。
为了确保事务可序列化,服务器不允许一个会话对在另一会话中未完成的显式或隐式启动的事务中使用的表执行DDL语句,服务器通过获取事务中使用的表上的元数据锁并将这些锁的释放推迟到事务结束之前来实现,表上的元数据锁可防止更改表的结构。
MDL锁和innodb存储引擎层面的IS和IX这种意向锁的区别是MDL锁是服务器中实现的,而不是在插件层面。除此之外,MDL锁可以实现全局锁、库级别的锁以及表空间级别的锁,这是插件式存储引擎锁不能实现的。按照MDL锁中级别进行分类,MDL锁可以分为以下几类:
属性 含义 范围/对象
GLOBAL 全局锁 范围 Waiting for global read lock
COMMIT 提交保护锁 范围 Waiting for commit lock
SCHEMA 库锁 对象 Waiting for schema metadata lock
TABLE 表锁 对象 Waiting for table metadata lock
FUNCTION 函数锁 对象 Waiting for stored function metadata lock
PROCEDURE 存储过程锁 对象 Waiting for stored procedure metadata lock
TRIGGER 触发器锁 对象 Waiting for trigger metadata lock
EVENT 事件锁 对象 Waiting for event metadata lock
下面我们看看在哪些情况下会发生元数据锁:
1、当事务中有dml操作的时候,事务没有提交的情况下,进行alter table或者drop table这种DML操作,会出现元数据锁等待,例如正在进行一个insert into ... select的操作;
2、当前在使用mysqldump进行备份,如果备份没有结束,则使用drop或者alter操作的时候,会用到元数据锁;
3、显示或者隐式开启事务后没有提交或者回滚,此时使用
alter或者drop的操作室,会用到元数据锁(如同上面的例子)。
4、表上有失败的查询事务,例如查询了一个不存在的列,按道理是没有返回表中的数据的,但是这个时候,alter和drop操作依然会被堵住,像下面这样:
当出现元数据锁导致多个回话中的多个进程不可用的时候,通常会使用kill的方式来杀掉mysql中客户端进程id。
关于元数据锁,还有很多内容,今天先这么多吧,后续的遇到了再慢慢写,对这个锁有个直观的认知就可以了。