Mysql的meta data lock 源码分析-初篇

 

之前有较深入的研究过mysql的innodb在行上的加锁机制,但未曾仔细研究过metadata-lock的加锁机制,猛然发现,metadata-lock的加锁机制远比dml锁复杂,瞬间崩溃。...



之前有较深入地研究过mysql的innodb 在行上的加锁机制,但未曾仔细研究过metadata-lock的机制,因为metadata-lock等待出现的场景常见的就两种:

1。Flush table 导致了meta-data lock wait.

2. 变更表结构导致了metadata lock  等待 . 类比oracle, mysql的metadata lock相似于oracle的数据字典锁。

出现metadata lock等待的现象跟原因,一般都非常直观且好理解,因此以为其实现方式也比较简单。 经过研究源码(注:版本mysql 5.7.10),作者发现之前的想法幼稚了。实际上其相关的逻辑跟数据结构比dml锁更复杂-----------瞬间崩溃。(注:这个是作者的学习笔记,学到哪里写到哪里,供各位朋友参考)。 首先我们来看一下这个metadata lock 种类的枚举类型enum_mdl_type ,总共有MDL_INTENTION_EXCLUSIVE,MDL_SHARED,MDL_SHARED_HIGH_PRIO,MDL_SHARED_READ,MDL_SHARED_WRITE, MDL_SHARED_WRITE_LOW_PRIO, MDL_SHARED_UPGRADABLE, MDL_SHARED_READ_ONLY, MDL_SHARED_NO_WRITE,MDL_SHARED_NO_READ_WRITE,MDL_EXCLUSIVE 总共10种--------居然有这么多? 比dml锁的类型多很多吧。

下面是源码中enum_mdl_type的定义以及注释,请各位朋友仔细查看各种锁类型的作用-----估计会比较晕。 晕,没有关系,我们先了解个大概,知道整个meta-data lock锁实现的原理,届时遇到真正出现无法定位的meta-data-lock等待的问题时,再具体问题再具体分析。

.............................省略代码部分....................................................................................

上面是整个meta data lock 类型的枚举数据结构。 跳跃一下,下面是作者在进行调式时,设置的一些重要的函数断点。 b MDL_context::upgrade_shared_lock thread 3 b MDL_context::acquire_locks thread 3b MDL_context::owns_equal_or_stronger_lock thread 3b MDL_context::find_lock_owner thread 3b MDL_ticket::get_key thread 3 b MDL_key::init_psi_keys thread 3b MDL_lock::create thread 3 b MDL_context::acquire_lock thread 3b MDL_context::try_acquire_lock_impl thread 3b MDL_lock::get_incompatible_waiting_types_bitmap_idx thread 3b MDL_lock::can_grant_lock thread 3b MDL_ticket::has_stronger_or_equal_type thread 3(gdb) p m_strategy->m_waiting_incompatible[m_current_waiting_incompatible_idx] $30 = {0, 1024, 0, 1536, 1792, 1920, 1024, 1552, 1024, 1024, 0}(gdb) p m_strategy->m_granted_incompatible $38 = {0, 1024, 1024, 1536, 1920, 1920, 1856, 1584, 1904, 2040, 2046}
下面这个函数就是用来检查是否可以授予申请的enum_mdl_type 类型的meta-data 锁。 bool MDL_ticket::is_incompatible_when_granted(enum_mdl_type type) const {return (MDL_BIT(m_type) &

m_lock->incompatible_granted_types_bitmap()[type]); }
继续回到文章的常规上下文。。 最开始我们介绍了meta data lock的类型, 我们申请锁时,需要验证各种类型的锁之间是否兼容,代码层面是如何实现的呢? 实际上,就是通过位图来表示每一种类型与其他类型的兼容型,两个类型的锁的兼容位图取&, 如果得到的值为非0,则冲突。 因此,当某种锁类型的兼容位图越多位为1,则跟越多的其他的类型的锁冲突。 例如,MDL_SHARED 类型的锁,其冲突位图为MDL_BIT(MDL_EXCLUSIVE), 通过前面的枚举类型我们知道,MDL_EXCLUSIVE 排在第10位,则其冲突位图为1000000000,仅仅第10位为1, 也就是仅仅跟MDL_EXCLUSIVE 冲突。 相反, MDL_EXCLUSIVE 类型的锁的冲突位图如下,取与后的值为1111111111, 所有位都1, 表示跟上述任何类型的锁冲突。 MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) | MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_READ_ONLY) | MDL_BIT(MDL_SHARED_UPGRADABLE) | MDL_BIT(MDL_SHARED_WRITE_LOW_PRIO) | MDL_BIT(MDL_SHARED_WRITE) | MDL_BIT(MDL_SHARED_READ) | MDL_BIT(MDL_SHARED_HIGH_PRIO) | MDL_BIT(MDL_SHARED) 下面是源码, 请查看代码以及相关的注释。 特别说明一下,MDL_lock::m_object_lock_strategy冲突数组包含好几组,其中最容易被感知的两组:一组表示申请的锁类型跟已经授权的类型是否冲突 ,另外一组是申请的类型是否跟等待中的锁类型是否冲突。 还有其他的数组,这些另外再作说明,有兴趣的朋友可以先看一下注释。 经过简单的了解之后,发现meta-data lock 的锁机制还真心比dml锁复杂,光冲突数据就有怎么多组,真晕。 第一组:      Request  |  Granted requests for lock            |
type    | S  SH  SR  SW  SWLP  SU  SRO  SNW  SNRW  X  |
----------+---------------------------------------------+
S         | +   +   +   +    +    +   +    +    +    -  |
SH        | +   +   +   +    +    +   +    +    +    -  |
SR        | +   +   +   +    +    +   +    +    -    -  |
SW        | +   +   +   +    +    +   -    -    -    -  |
SWLP      | +   +   +   +    +    +   -    -    -    -  |
SU        | +   +   +   +    +    -   +    -    -    -  |
SRO       | +   +   +   -    -    +   +    +    -    -  |
SNW       | +   +   +   -    -    -   +    -    -    -  |
SNRW      | +   +   -   -    -    -   -    -    -    -  |
X         | -   -   -   -    -    -   -    -    -    -  |

第二组:

Request  |         Pending requests for lock          |

type    | S  SH  SR  SW  SWLP  SU  SRO  SNW  SNRW  X |

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

S         | +   +   +   +    +    +   +    +     +   - |

SH        | +   +   +   +    +    +   +    +     +   + |

SR        | +   +   +   +    +    +   +    +     -   - |

SW        | +   +   +   +    +    +   +    -     -   - |

SWLP      | +   +   +   +    +    +   -    -     -   - |

SU        | +   +   +   +    +    +   +    +     +   - |

SRO       | +   +   +   -    +    +   +    +     -   - |

SNW       | +   +   +   +    +    +   +    +     +   - |

SNRW      | +   +   +   +    +    +   +    +     +   - |

X         | +   +   +   +    +    +   +    +     +   + |

+表示兼容   -表示不兼容

上面说明的,仅仅是对象类的metadata lock 的兼容数组,此外,还有范围类的metadata lock, 范围类的metadata lock的类型有global, commit,tablespace, schema namespaces.

其兼容矩阵跟对象类型的metadata lock完全不一样。有兴趣的朋友请查看源代码中的const MDL_lock::MDL_lock_strategy MDL_lock::m_scoped_lock_strategy  的结构。

。。。。。。。。。。。。。。。。。。

点到为止, 有兴趣的朋友自己研究,写不下去了。。。。。。。。,太复杂了。

最后扩展一个比较有用的实例。 大家都知道从mysql 5.6 开始,支持在线的ddl , 那请问,是不是ddl与dml 是完全没有干扰的呢? 这个答案是否定的,一定有干扰,但在什么时候会干扰,为啥?

以   alter table unlockt drop  e;  sql 为例。下面是跟踪的信息,看不懂直接忽略过,看作者文字。

$44 = {type = MDL_INTENTION_EXCLUSIVE, duration = MDL_STATEMENT, next_in_list = 0x7f42e8006670, prev_in_list = 0x7f439eeec278, ticket = 0x0, ke

$45 = {type = MDL_INTENTION_EXCLUSIVE, duration = MDL_TRANSACTION, next_in_list = 0x7f42e8006430

$47 = {type = MDL_SHARED_UPGRADABLE, duration = MDL_TRANSACTION, next_in_list = 0x0, prev_in_list = 0x7f42e8006678

MDL_context::upgrade_shared_lock (this=0x7f42e8000c08, mdl_ticket=0x7f42e80116e0, new_type=MDL_EXCLUSIVE, lock_wait_timeout=31536000)

(gdb) bt

#0 MDL_context::upgrade_shared_lock (this=0x7f42e8000c08, mdl_ticket=0x7f42e80116e0, new_type=MDL_EXCLUSIVE, lock_wait_timeout=31536000)

at /data/mysql/mysql-5.7.10/sql/mdl.cc:3784

#1  0x0000000001568797 in mysql_inplace_alter_table (thd=0x7f42e8000b70, table_list=0x7f42e8006090, table=0x7f42e89aee80, altered_table=0x7f42e893aff0,

ha_alter_info=0x7f439eeeca10, inplace_supported=HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE, target_mdl_request=0x7f439eeec450, alter_ctx=0x7f439eeecfa0)

at /data/mysql/mysql-5.7.10/sql/sql_table.cc:7195

#2  0x000000000156d993 in mysql_alter_table (thd=0x7f42e8000b70, new_db=0x7f42e8006618 "xcytest", new_name=0x0, create_info=0x7f439eeede30, table_list=0x7f42e8006090,

alter_info=0x7f439eeedd80) at /data/mysql/mysql-5.7.10/sql/sql_table.cc:9383

#3  0x00000000016c3623 in Sql_cmd_alter_table::execute (this=0x7f42e8006648, thd=0x7f42e8000b70) at /data/mysql/mysql-5.7.10/sql/sql_alter.cc:316

#4  0x00000000014ddc79 in mysql_execute_command (thd=0x7f42e8000b70, first_level=true) at /data/mysql/mysql-5.7.10/sql/sql_parse.cc:4796

#5  0x00000000014df9b0 in mysql_parse (thd=0x7f42e8000b70, parser_state=0x7f439eeef6a0) at /data/mysql/mysql-5.7.10/sql/sql_parse.cc:5508

#6  0x00000000014d5468 in dispatch_command (thd=0x7f42e8000b70, com_data=0x7f439eeefdf0, command=COM_QUERY) at /data/mysql/mysql-5.7.10/sql/sql_parse.cc:1425

#7  0x00000000014d43fd in do_command (thd=0x7f42e8000b70) at /data/mysql/mysql-5.7.10/sql/sql_parse.cc:993

#8  0x00000000015feadf in handle_connection (arg=0x73ee250) at /data/mysql/mysql-5.7.10/sql/conn_handler/connection_handler_per_thread.cc:301

#9  0x0000000001c24b20 in pfs_spawn_thread (arg=0x7635a90) at /data/mysql/mysql-5.7.10/storage/perfschema/pfs.cc:2192

#10 0x00007f4438366df5 in start_thread () from /lib64/libpthread.so.0

#11 0x00007f44372321ad in clone () from /lib64/libc.so.6

发现有通过函数MDL_context::upgrade_shared_lock 将锁升级的过程,将MDL_SHARED_UPGRADABLE 升级为MDL_EXCLUSIVE 锁。之后又有降级的过程。

Breakpoint 29, MDL_ticket::has_stronger_or_equal_type (this=0x7f42e80116e0, type=MDL_SHARED_UPGRADABLE) at /data/mysql/mysql-5.7.10/sql/mdl.cc:2706

2706        granted_incompat_map= m_lock->incompatible_granted_types_bitmap();

(gdb) bt

#0  MDL_ticket::has_stronger_or_equal_type (this=0x7f42e80116e0, type=MDL_SHARED_UPGRADABLE) at /data/mysql/mysql-5.7.10/sql/mdl.cc:2706

#1  0x00000000013f39f3 in MDL_ticket::downgrade_lock (this=0x7f42e80116e0, new_type=MDL_SHARED_UPGRADABLE) at /data/mysql/mysql-5.7.10/sql/mdl.cc:4357

#2  0x0000000001568b5a in mysql_inplace_alter_table (thd=0x7f42e8000b70, table_list=0x7f42e8006090, table=0x7f42e89aee80, altered_table=0x7f42e893aff0,

ha_alter_info=0x7f439eeeca10, inplace_supported=HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE, target_mdl_request=0x7f439eeec450, alter_ctx=0x7f439eeecfa0)

at /data/mysql/mysql-5.7.10/sql/sql_table.cc:7304

#3  0x000000000156d993 in mysql_alter_table (thd=0x7f42e8000b70, new_db=0x7f42e8006618 "xcytest", new_name=0x0, create_info=0x7f439eeede30, table_list=0x7f42e8006090,

alter_info=0x7f439eeedd80) at /data/mysql/mysql-5.7.10/sql/sql_table.cc:9383

#4  0x00000000016c3623 in Sql_cmd_alter_table::execute (this=0x7f42e8006648, thd=0x7f42e8000b70) at /data/mysql/mysql-5.7.10/sql/sql_alter.cc:316

#5  0x00000000014ddc79 in mysql_execute_command (thd=0x7f42e8000b70, first_level=true) at /data/mysql/mysql-5.7.10/sql/sql_parse.cc:4796

#6  0x00000000014df9b0 in mysql_parse (thd=0x7f42e8000b70, parser_state=0x7f439eeef6a0) at /data/mysql/mysql-5.7.10/sql/sql_parse.cc:5508

#7  0x00000000014d5468 in dispatch_command (thd=0x7f42e8000b70, com_data=0x7f439eeefdf0, command=COM_QUERY) at /data/mysql/mysql-5.7.10/sql/sql_parse.cc:1425

#8  0x00000000014d43fd in do_command (thd=0x7f42e8000b70) at /data/mysql/mysql-5.7.10/sql/sql_parse.cc:993

#9  0x00000000015feadf in handle_connection (arg=0x73ee250) at /data/mysql/mysql-5.7.10/sql/conn_handler/connection_handler_per_thread.cc:301

#10 0x0000000001c24b20 in pfs_spawn_thread (arg=0x7635a90) at /data/mysql/mysql-5.7.10/storage/perfschema/pfs.cc:2192

#11 0x00007f4438366df5 in start_thread () from /lib64/libpthread.so.0

#12 0x00007f44372321ad in clone () from /lib64/libc.so.6

通过函数MDL_ticket::downgrade_lock 将MDL_EXCLUSIVE降级为MDL_SHARED_UPGRADABLE,但在最后又升级为MDL_EXCLUSIVE

Breakpoint 29, MDL_ticket::has_stronger_or_equal_type (this=0x7f42e80116e0, type=MDL_EXCLUSIVE) at /data/mysql/mysql-5.7.10/sql/mdl.cc:2706

2706        granted_incompat_map= m_lock->incompatible_granted_types_bitmap();

(gdb) bt

#0  MDL_ticket::has_stronger_or_equal_type (this=0x7f42e80116e0, type=MDL_EXCLUSIVE) at /data/mysql/mysql-5.7.10/sql/mdl.cc:2706

#1  0x00000000013f2c46 in MDL_context::upgrade_shared_lock (this=0x7f42e8000c08, mdl_ticket=0x7f42e80116e0, new_type=MDL_EXCLUSIVE, lock_wait_timeout=31536000)

at /data/mysql/mysql-5.7.10/sql/mdl.cc:3781

#2  0x0000000001453296 in wait_while_table_is_used (thd=0x7f42e8000b70, table=0x7f42e89aee80, function=HA_EXTRA_PREPARE_FOR_RENAME)

at /data/mysql/mysql-5.7.10/sql/sql_base.cc:2515

#3  0x0000000001568c0b in mysql_inplace_alter_table (thd=0x7f42e8000b70, table_list=0x7f42e8006090, table=0x7f42e89aee80, altered_table=0x7f42e893aff0,

ha_alter_info=0x7f439eeeca10, inplace_supported=HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE, target_mdl_request=0x7f439eeec450, alter_ctx=0x7f439eeecfa0)

at /data/mysql/mysql-5.7.10/sql/sql_table.cc:7318

#4  0x000000000156d993 in mysql_alter_table (thd=0x7f42e8000b70, new_db=0x7f42e8006618 "xcytest", new_name=0x0, create_info=0x7f439eeede30, table_list=0x7f42e8006090,

alter_info=0x7f439eeedd80) at /data/mysql/mysql-5.7.10/sql/sql_table.cc:9383

#5  0x00000000016c3623 in Sql_cmd_alter_table::execute (this=0x7f42e8006648, thd=0x7f42e8000b70) at /data/mysql/mysql-5.7.10/sql/sql_alter.cc:316

#6  0x00000000014ddc79 in mysql_execute_command (thd=0x7f42e8000b70, first_level=true) at /data/mysql/mysql-5.7.10/sql/sql_parse.cc:4796

#7  0x00000000014df9b0 in mysql_parse (thd=0x7f42e8000b70, parser_state=0x7f439eeef6a0) at /data/mysql/mysql-5.7.10/sql/sql_parse.cc:5508

#8  0x00000000014d5468 in dispatch_command (thd=0x7f42e8000b70, com_data=0x7f439eeefdf0, command=COM_QUERY) at /data/mysql/mysql-5.7.10/sql/sql_parse.cc:1425

#9  0x00000000014d43fd in do_command (thd=0x7f42e8000b70) at /data/mysql/mysql-5.7.10/sql/sql_parse.cc:993

#10 0x00000000015feadf in handle_connection (arg=0x73ee250) at /data/mysql/mysql-5.7.10/sql/conn_handler/connection_handler_per_thread.cc:301

#11 0x0000000001c24b20 in pfs_spawn_thread (arg=0x7635a90) at /data/mysql/mysql-5.7.10/storage/perfschema/pfs.cc:2192

#12 0x00007f4438366df5 in start_thread () from /lib64/libpthread.so.0

#13 0x00007f44372321ad in clone () from /lib64/libc.so.6

因此,在升级为MDL_EXCLUISVE的过程中,容易造成metadata lock wait 现象。

    关注个人公众号,或者访问http://www.xuchunyang.com  ,及时获取作者分享的纯干货原创文章。

    


    关注 数据库随笔


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册