前言

好久没更新,主要是因为Inside君最近沉迷于一部动画片——《新葫芦娃兄弟》。终于抽得闲,完成了本篇关于MySQL MDL锁的深入分析与介绍。虽然之前有很多小伙伴分析过,但总感觉少了点什么,故花了点时间翻看了下源码。Inside君或许不是最牛掰的内核开发人员,但自认为应该是业界最会讲故事的码农,希望本篇能做到通俗易懂,因为MDL锁其实并不好理解。如果同学们还有问题,也可以直接看源码文件mdl.cc。

MDL锁与实现

MySQL5.5版本引入了MDL锁(metadata lock),用于解决或者保证DDL操作与DML操作之间的一致性。例如下面的这种情形:

会话1 会话2
BEGIN;  
SELECT * FROM XXX  
  DROP TABLE XXX
SELECT * FROM XXX  

若没有MDL锁的保护,则事务2可以直接执行DDL操作,并且导致事务1出错,5.1版本即是如此。5.5版本加入MDL锁就在于保护这种情况的发生,由于事务1开启了查询,那么获得了MDL锁,锁的模式为SHARED_READ,事务2要执行DDL,则需获得EXCLUSIVE锁,两者互斥,所以事务2需要等待。

InnoDB层已经有了IS、IX这样的意向锁,有同学觉得可以用来实现上述例子的并发控制。但由于MySQL是Server-Engine架构,所以MDL锁是在Server中实现。另外,MDL锁还能实现其他粒度级别的锁,比如全局锁、库级别的锁、表空间级别的锁,这是InnoDB存储引擎层不能直接实现的锁。

但与InnoDB锁的实现一样,MDL锁也是类似对一颗树的各个对象从上至下进行加锁(对树进行加锁具体见:《MySQL技术内幕:InnoDB存储引擎》)。但是MDL锁对象的层次更多,简单来看有如下的层次:

上图中显示了最常见的4种MDL锁的对象,并且注明了常见的SQL语句会触发的锁。与InnoDB层类似的是,某些类型的MDL锁会从上往下一层层进行加锁。比如LOCK TABLE … WRITE这样的SQL语句,其首先会对GLOBAL级别加INTENTION_EXCLUSIVE锁,再对SCHEMA级别加INTENTION_EXCLUSIVE锁,最后对TABLE级别加SHARED_NO_READ_WRITE锁。

这里最令人意外的是还有COMMIT对象层次的锁,其实这主要用于XA事务中。比如分布式事务已经PREPARE成功,但是在XA COMMIT之前有其他会话执行了FLUSH TABLES WITH READ LOCK这样的操作,那么分布式事务的提交就需要等待。

除了上图标注的对象,其实还有TABLESPACE、FUNCTION、PROCEDURE、EVENT等其他对象类型,其实都是为了进行并发控制。只是这些在MySQL数据库中都不常用,故不再赘述(当然也是为了偷懒)。

  1. GLOBAL=0,TABLESPACE,SCHEMA,TABLE,FUNCTION,PROCEDURE,TRIGGER,EVENT,COMMIT,USER_LEVEL_LOCK,LOCKING_SERVICE,NAMESPACE_END

目前MDL有如下锁模式,锁之间的兼容性可见源码mdl.cc:

锁模式 对应SQL
MDL_INTENTION_EXCLUSIVE GLOBAL对象、SCHEMA对象操作会加此锁
MDL_SHARED FLUSH TABLES with READ LOCK
MDL_SHARED_HIGH_PRIO 仅对MyISAM存储引擎有效
MDL_SHARED_READ SELECT查询
MDL_SHARED_WRITE DML语句
MDL_SHARED_WRITE_LOW_PRIO 仅对MyISAM存储引擎有效
MDL_SHARED_UPGRADABLE ALTER TABLE
MDL_SHARED_READ_ONLY LOCK xxx READ
MDL_SHARED_NO_WRITE FLUSH TABLES xxx,yyy,zzz READ
MDL_SHARED_NO_READ_WRITE FLUSH TABLE xxx WRITE
MDL_EXCLUSIVE ALTER TABLE xxx PARTITION BY …

MDL锁的性能与并发改进

讲到这同学们会发现MDL锁的开销并不比InnoDB层的行锁要小,而且这可能是一个更为密集的并发瓶颈。MySQL 5.6和5.5版本通常通过调整如下两个参数来进行并发调优:

  • metadata_locks_cache_size: MDL锁的缓存大小
  • metadata_locks_hash_instances:通过分片来提高并发度,与InnoDB AHI类似

MySQL 5.7 MDL锁的最大改进之处在于将MDL锁的机制通过lock free算法来实现,从而提高了在多核并发下数据库的整体性能提升。

MDL锁的诊断

MySQL 5.7版本之前并没有提供一个方便的途径来查看MDL锁,github上有一名为mysql-plugin-mdl-info的项目,通过插件的方式来查看,非常有想法的实现,大赞。好在官方也意识到了这个问题,于是在MySQL 5.7中的performance_schea库下新增了一张表metadata_locks,用其来查看MDL锁那是相当的方便:

不过默认PS并没有打开此功能,需要手工将wait/lock/metadata/sql/mdl监控给打开:

  1. SELECT * FROM performance_schema.setup_instruments;
  1.  
  1. UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME ='global_instrumentation';
  2. UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME ='wait/lock/metadata/sql/mdl';
  3. select * from performance_schema.metadata_locks\G

会话1

  1. mysql> lock table xx read;
  2. Query OK, 0 rows affected (0.21 sec)

会话2:

  1. SELECT * FROM performance_schema.metadata_locks;
  1. mysql> SELECT * FROM performance_schema.metadata_locks\G;
  2. *************************** 1. row ***************************
  3. OBJECT_TYPE: TABLE
  4. OBJECT_SCHEMA: performance_schema
  5. OBJECT_NAME: metadata_locks
  6. OBJECT_INSTANCE_BEGIN: 240554768
  7. LOCK_TYPE: SHARED_READ
  8. LOCK_DURATION: TRANSACTION
  9. LOCK_STATUS: GRANTED
  10. SOURCE: sql_parse.cc:5927
  11. OWNER_THREAD_ID: 38
  12. OWNER_EVENT_ID: 10
  13. 1 row in set (0.00 sec)
  14.  
  15. ERROR:
  16. No query specified
  1. enum enum_mdl_type {
  2. /*
  3. An intention exclusive metadata lock. Used only for scoped locks.
  4. Owner of this type of lock can acquire upgradable exclusive locks on
  5. individual objects.
  6. Compatible with other IX locks, but is incompatible with scoped S and
  7. X locks.
  8. */
  9. MDL_INTENTION_EXCLUSIVE= 0,
  10. /*
  11. A shared metadata lock.
  12. To be used in cases when we are interested in object metadata only
  13. and there is no intention to access object data (e.g. for stored
  14. routines or during preparing prepared statements).
  15. We also mis-use this type of lock for open HANDLERs, since lock
  16. acquired by this statement has to be compatible with lock acquired
  17. by LOCK TABLES ... WRITE statement, i.e. SNRW (We can't get by by
  18. acquiring S lock at HANDLER ... OPEN time and upgrading it to SR
  19. lock for HANDLER ... READ as it doesn't solve problem with need
  20. to abort DML statements which wait on table level lock while having
  21. open HANDLER in the same connection).
  22. To avoid deadlock which may occur when SNRW lock is being upgraded to
  23. X lock for table on which there is an active S lock which is owned by
  24. thread which waits in its turn for table-level lock owned by thread
  25. performing upgrade we have to use thr_abort_locks_for_thread()
  26. facility in such situation.
  27. This problem does not arise for locks on stored routines as we don't
  28. use SNRW locks for them. It also does not arise when S locks are used
  29. during PREPARE calls as table-level locks are not acquired in this
  30. case.
  31. */
  32. MDL_SHARED,
  33. /*
  34. A high priority shared metadata lock.
  35. Used for cases when there is no intention to access object data (i.e.
  36. data in the table).
  37. "High priority" means that, unlike other shared locks, it is granted
  38. ignoring pending requests for exclusive locks. Intended for use in
  39. cases when we only need to access metadata and not data, e.g. when
  40. filling an INFORMATION_SCHEMA table.
  41. Since SH lock is compatible with SNRW lock, the connection that
  42. holds SH lock lock should not try to acquire any kind of table-level
  43. or row-level lock, as this can lead to a deadlock. Moreover, after
  44. acquiring SH lock, the connection should not wait for any other
  45. resource, as it might cause starvation for X locks and a potential
  46. deadlock during upgrade of SNW or SNRW to X lock (e.g. if the
  47. upgrading connection holds the resource that is being waited for).
  48. */
  49. MDL_SHARED_HIGH_PRIO,
  50. /*
  51. A shared metadata lock for cases when there is an intention to read data
  52. from table.
  53. A connection holding this kind of lock can read table metadata and read
  54. table data (after acquiring appropriate table and row-level locks).
  55. This means that one can only acquire TL_READ, TL_READ_NO_INSERT, and
  56. similar table-level locks on table if one holds SR MDL lock on it.
  57. To be used for tables in SELECTs, subqueries, and LOCK TABLE ... READ
  58. statements.
  59. */
  60. MDL_SHARED_READ,
  61. /*
  62. A shared metadata lock for cases when there is an intention to modify
  63. (and not just read) data in the table.
  64. A connection holding SW lock can read table metadata and modify or read
  65. table data (after acquiring appropriate table and row-level locks).
  66. To be used for tables to be modified by INSERT, UPDATE, DELETE
  67. statements, but not LOCK TABLE ... WRITE or DDL). Also taken by
  68. SELECT ... FOR UPDATE.
  69. */
  70. MDL_SHARED_WRITE,
  71. /*
  72. A version of MDL_SHARED_WRITE lock which has lower priority than
  73. MDL_SHARED_READ_ONLY locks. Used by DML statements modifying
  74. tables and using the LOW_PRIORITY clause.
  75. */
  76. MDL_SHARED_WRITE_LOW_PRIO,
  77. /*
  78. An upgradable shared metadata lock which allows concurrent updates and
  79. reads of table data.
  80. A connection holding this kind of lock can read table metadata and read
  81. table data. It should not modify data as this lock is compatible with
  82. SRO locks.
  83. Can be upgraded to SNW, SNRW and X locks. Once SU lock is upgraded to X
  84. or SNRW lock data modification can happen freely.
  85. To be used for the first phase of ALTER TABLE.
  86. */
  87. MDL_SHARED_UPGRADABLE,
  88. /*
  89. A shared metadata lock for cases when we need to read data from table
  90. and block all concurrent modifications to it (for both data and metadata).
  91. Used by LOCK TABLES READ statement.
  92. */
  93. MDL_SHARED_READ_ONLY,
  94. /*
  95. An upgradable shared metadata lock which blocks all attempts to update
  96. table data, allowing reads.
  97. A connection holding this kind of lock can read table metadata and read
  98. table data.
  99. Can be upgraded to X metadata lock.
  100. Note, that since this type of lock is not compatible with SNRW or SW
  101. lock types, acquiring appropriate engine-level locks for reading
  102. (TL_READ* for MyISAM, shared row locks in InnoDB) should be
  103. contention-free.
  104. To be used for the first phase of ALTER TABLE, when copying data between
  105. tables, to allow concurrent SELECTs from the table, but not UPDATEs.
  106. */
  107. MDL_SHARED_NO_WRITE,
  108. /*
  109. An upgradable shared metadata lock which allows other connections
  110. to access table metadata, but not data.
  111. It blocks all attempts to read or update table data, while allowing
  112. INFORMATION_SCHEMA and SHOW queries.
  113. A connection holding this kind of lock can read table metadata modify and
  114. read table data.
  115. Can be upgraded to X metadata lock.
  116. To be used for LOCK TABLES WRITE statement.
  117. Not compatible with any other lock type except S and SH.
  118. */
  119. MDL_SHARED_NO_READ_WRITE,
  120. /*
  121. An exclusive metadata lock.
  122. A connection holding this lock can modify both table's metadata and data.
  123. No other type of metadata lock can be granted while this lock is held.
  124. To be used for CREATE/DROP/RENAME TABLE statements and for execution of
  125. certain phases of other DDL statements.
  126. */
  127. MDL_EXCLUSIVE,
  128. /* This should be the last !!! */
  129. MDL_TYPE_END};
  130.  
  131. /** Duration of metadata lock. */
  132.  
  133. enum enum_mdl_duration {
  134. /**
  135. Locks with statement duration are automatically released at the end
  136. of statement or transaction.
  137. */
  138. MDL_STATEMENT= 0,
  139. /**
  140. Locks with transaction duration are automatically released at the end
  141. of transaction.
  142. */
  143. MDL_TRANSACTION,
  144. /**
  145. Locks with explicit duration survive the end of statement and transaction.
  146. They have to be released explicitly by calling MDL_context::release_lock().
  147. */
  148. MDL_EXPLICIT,
  149. /* This should be the last ! */
  150. MDL_DURATION_END };
  151.  
  152. /** Maximal length of key for metadata locking subsystem. */
  153. #define MAX_MDLKEY_LENGTH (1 + NAME_LEN + 1 + NAME_LEN + 1)
  154.  
  155. /**
  156. Metadata lock object key.
  157.  
  158. A lock is requested or granted based on a fully qualified name and type.
  159. E.g. They key for a table consists of <0 (=table)> + <database> + <table name>.
  160. Elsewhere in the comments this triple will be referred to simply as "key"
  161. or "name".
  162. */
  163.  
  164. struct MDL_key
  165. {
  166. public:
  167. #ifdef HAVE_PSI_INTERFACE
  168. static void init_psi_keys();
  169. #endif
  170.  
  171. /**
  172. Object namespaces.
  173. Sic: when adding a new member to this enum make sure to
  174. update m_namespace_to_wait_state_name array in mdl.cc!
  175.  
  176. Different types of objects exist in different namespaces
  177. - GLOBAL is used for the global read lock.
  178. - TABLESPACE is for tablespaces.
  179. - SCHEMA is for schemas (aka databases).
  180. - TABLE is for tables and views.
  181. - FUNCTION is for stored functions.
  182. - PROCEDURE is for stored procedures.
  183. - TRIGGER is for triggers.
  184. - EVENT is for event scheduler events.
  185. - COMMIT is for enabling the global read lock to block commits.
  186. - USER_LEVEL_LOCK is for user-level locks.
  187. - LOCKING_SERVICE is for the name plugin RW-lock service
  188. Note that although there isn't metadata locking on triggers,
  189. it's necessary to have a separate namespace for them since
  190. MDL_key is also used outside of the MDL subsystem.
  191. Also note that requests waiting for user-level locks get special
  192. treatment - waiting is aborted if connection to client is lost.
  193. */
  194. enum enum_mdl_namespace { GLOBAL=0,
  195. TABLESPACE,
  196. SCHEMA,
  197. TABLE,
  198. FUNCTION,
  199. PROCEDURE,
  200. TRIGGER,
  201. EVENT,
  202. COMMIT,
  203. USER_LEVEL_LOCK,
  204. LOCKING_SERVICE,
  205. /* This should be the last ! */
  206. NAMESPACE_END
    };
  207. 。。。
  1. if (!MY_TEST(table_options & TL_OPTION_ALIAS))
  2. {
  3. MDL_REQUEST_INIT(& ptr->mdl_request,
  4. MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type,
  5. MDL_TRANSACTION);
  1. TABLE_LIST *ptr;

MySQL 5.7对于MDL的最后一个改进在mysqldump,有同学能知道是在哪嘛?第一个答对的同学可获Inside君签名的《MySQL内核:InnoDB存储引擎 卷1》书籍一本。

深入理解MYSQL的MDL元数据锁的更多相关文章

  1. 深入理解MDL元数据锁

    前言:  当你在MySQL中执行一条SQL时,语句并没有在你预期的时间内执行完成,这时候我们通常会登陆到MySQL数据库上查看是不是出了什么问题,通常会使用的一个命令就是 show processli ...

  2. (转载)深入理解MDL元数据锁

    作者:MySQL技术本文为作者原创,转载请注明出处:https://www.cnblogs.com/kunjian/p/11993708.html 前言: 当你在MySQL中执行一条SQL时,语句并没 ...

  3. 深入理解MySQL的并发控制、锁和事务【转】

    本文主要是针对MySQL/InnoDB的并发控制和加锁技术做一个比较深入的剖析,并且对其中涉及到的重要的概念,如多版本并发控制(MVCC),脏读(dirty read),幻读(phantom read ...

  4. 一步步搞懂MySQL元数据锁(MDL)

    某日,路上收到用户咨询,为了清除空间,想删除某200多G大表数据,且已经确认此表不再有业务访问,于是执行了一条命令'delete from bigtable',但好长时间也没删完,经过咨询后,获知dr ...

  5. Mysql加锁过程详解(7)-初步理解MySQL的gap锁

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  6. 深入理解 MySQL ——锁、事务与并发控制

    本文首发于vivo互联网技术微信公众号 mp.weixin.qq.com/s/JFSDqI5ya… 作者:张硕 本文对 MySQL 数据库中有关锁.事务及并发控制的知识及其原理做了系统化的介绍和总结, ...

  7. [转帖]2019-03-26 发布 深入理解 MySQL ——锁、事务与并发控制

    深入理解 MySQL ——锁.事务与并发控制 https://segmentfault.com/a/1190000018658828 太长了 没看完.. 数据库 并发  mysql 639 次阅读   ...

  8. [转帖]深入理解 MySQL—锁、事务与并发控制

    深入理解 MySQL—锁.事务与并发控制 http://www.itpub.net/2019/04/28/1723/ 跟oracle也类似 其实所有的数据库都有相同的机制.. 学习了机制才能够更好的工 ...

  9. 深入理解MySQL系列之锁

    按锁思想分类 悲观锁 优点:适合在写多读少的并发环境中使用,虽然无法维持非常高的性能,但是在乐观锁无法提更好的性能前提下,可以做到数据的安全性 缺点:加锁会增加系统开销,虽然能保证数据的安全,但数据处 ...

随机推荐

  1. js中ajax如何解决跨域请求

    js中ajax如何解决跨域请求,在讲这个问题之前先解释几个名词 1.跨域请求 所有的浏览器都是同源策略,这个策略能保证页面脚本资源和cookie安全 ,浏览器隔离了来自不同源的请求,防上跨域不安全的操 ...

  2. Javascript模板及其中的数据逻辑分离思想(MVC)

    #Javascript模板及其中的数据逻辑分离思想 ##需求描述 项目数据库的题目表描述了70-120道题目,并且是会变化的,要根据数据库中的数据描述,比如,选择还是填空题,是不是重点题,题目总分是多 ...

  3. ios自定义View自动布局时计算大小

    https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/Impleme ...

  4. Codeforces Educational Codeforces Round 15 D. Road to Post Office

    D. Road to Post Office time limit per test 1 second memory limit per test 256 megabytes input standa ...

  5. 关于使用base36的技巧 生成 优惠券兑换码的相关讨论

    关于优惠券的生成后台的制作问题,已经拖了很久了还没有合并.但是持续暴露出来的问题 也很多,我的代码以及前面的一个人的代码被持续review暴露出了大量问题.昨天晚上在

  6. 数据库 CHECKDB 发现了x个分配错误和 x 个一致性错误

    --1.在SQL查询分析器中执行以下语句:(注以下所用的POS为数据库名称,请用户手工改为自己的数据库名) use pos dbcc checkdb --2.查看查询结果,有很多红色字体显示,最后结果 ...

  7. Northwind数据库表字段介绍

    ① Categories:种类表 相应字段: CategoryID :类型ID: CategoryName:类型名; Description:类型说明; Picture:产品样本 ② Customer ...

  8. 第三百四十八天 how can I 坚持

    回来的倒不晚,算了不想抱怨了. 晚上回来吃过饭,又看了遍<活着>,把一切悲剧都放在一个人身上了,很朴实,好感人. 一天就写了一个借口,也是醉了. 我的天气预报,我的struts.sprin ...

  9. HDU 4612 Warm up(2013多校2 1002 双连通分量)

    Warm up Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Su ...

  10. JAVA应用apache httpclient探测http服务

    代码很简单,apache都已经提供了封装. import org.apache.commons.httpclient.HttpClient; import org.apache.commons.htt ...