本篇内容介绍了“分析MySQL中FTWRL一个奇怪的堵塞现象”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!首先建立一张有几条数据的表就可以了,我这里是baguait1表了。步骤2 “flush table with read lock;”操作等待状态为“Waiting for global read lock”,如下:这里比较奇怪了,实际上我很久以前就遇到过和测试过但是没有仔细研究过,这次刚好详细看看。步骤2 “flush table with read lock;”操作等待状态为 “Waiting for table flush”,状态如下:步骤4 “select * from testmts.baguait1 limit 1”操作等待状态为 “Waiting for table flush”,这个想象看起来非常奇怪没有任何特殊的其他操作,select居然堵塞了。如果仔细对比两个案例实际上区别仅仅在于 步骤1中的select 语句是否加了for update,案例2中我们发现即便我们将“flush table with read lock;”会话KILL掉也会堵塞随后的关于本表上全部操作(包括select),这个等待实际上会持续到步骤1的sleep操作完成过后。对于线上数据库的话,如果在长时间的select大表期间执行“flush table with read lock;”就会出现这种情况,这种情况会造成全部关于本表的操作等待,即便你发现后杀掉了FTWRL会话也无济于事,等待会持续到select操作完成后,除非你KILL掉长时间的select操作。为什么会出现这种情况呢?我们接下来慢慢分析。关于本案例中我使用sleep函数来代替select 大表操作做为测试,在这里这个代替是成立的。为什么成立呢我们来看一下sleep函数的生效点如下:这里看出sleep的生效点实际上每次Innodb层返回一行数据经过where条件判断后,再触发sleep函数,也就是每行免费主机域名经过where条件过滤的数据在发送给客户端之前都会进行一次sleep操作。这个时候实际上该打开表的和该上MDL LOCK的都已经完成了,因此使用sleep函数来模拟大表select操作导致的FTWRL堵塞是可以的。实际上这部分我们可以在函数mysql_execute_command寻找case SQLCOM_FLUSH 的部分,实际上主要调用函数为reload_acl_and_cache,其中核心部分为:更具体的关闭表的操作和释放table缓存的部分包含在函数close_cached_tables中,我就不详细写了。但是我们需要明白table缓存实际上包含两个部分:table cache define:每一个表第一次打开的时候都会建立一个静态的表定义结构内存,当多个会话同时访问同一个表的时候,从这里拷贝成相应的instance供会话自己使用。由参数table_definition_cache定义大小,由状态值Open_table_definitions查看当前使用的个数。对应函数get_table_share。table cache instance:同上所述,这是会话实际使用的表定义结构是一个instance。由参数table_open_cache定义大小,由状态值Open_tables查看当前使用的个数。对应函数open_table_from_share。这里我统称为table缓存,好了下面是我总结的FTWRl的大概步骤:第一步: 加MDL LOCK类型为GLOBAL 级别为S。如果出现等待状态为‘Waiting for global read lock’。注意select语句不会上GLOBAL级别上锁,但是DML/DDL/FOR UPDATE语句会上GLOBAL级别的IX锁,IX锁和S锁不兼容会出现这种等待。下面是这个兼容矩阵:第二步:推进全局表缓存版本。源码中就是一个全局变量 refresh_version++。
第三步:释放没有使用的table 缓存。可自行参考函数close_cached_tables函数。
第四步:判断是否有正在占用的table缓存,如果有则等待,等待占用者释放。等待状态为’Waiting for table flush’。这一步会去判断table缓存的版本和全局表缓存版本是否匹配,如果不匹配则等待如下:而等待的结束就是占用的table缓存的占用者释放,这个释放操作存在于函数close_thread_table中,如下:最终会调用函数MDL_wait::set_status将FTWRL唤醒,也就是说对于正在占用的table缓存释放者不是FTWRL会话而是占用者自己。不管怎么样最终整个table缓存将会被清空,如果经过FTWRL后去查看Open_table_definitions和Open_tables将会发现重新计数了。下面是唤醒函数的代码,也很明显:第五步:加MDL LOCK类型COMMIT 级别为S。如果出现等待状态为‘Waiting for commit lock’。如果有大事务的提交很可能出现这种等待。步骤1 我们使用select for update语句,这个语句会加GLOBAL级别的IX锁,持续到语句结束(注意实际上还会加对象级别的MDL_SHARED_WRITE(SW)锁持续到事务结束,和FTWRL无关不做描述)步骤2 我们使用FTWRL语句,根据上面的分析需要获取GLOBAL级别的S锁,不兼容,因此出现了等待‘Waiting for global read lock’步骤3 我们KILL掉了FTWRL会话,这种情况下会话退出,FTWRL就像没有执行过一样不会有任何影响,因为它在第一步就堵塞了。步骤4 我们的select操作不会受到任何影响步骤1 我们使用select 语句,这个语句不会在GLOBAL级别上任何的锁(注意实际上还会加对象级别的MDL_SHARED_READ(SR)锁持续到事务结束,和FTWRL无关不做描述)步骤2 我们使用FTWRL语句,根据上面的分析我们发现FTWRL语句可以获取了GLOBAL 级别的S锁,因为单纯的select 语句不会在GLOBAL级别上任何锁。同时会将全局表缓存版本推进然后释放掉没有使用的table 缓存,但是在第四步中会发现baguait1的表缓存正在被占用,因此出现了等待,等待状态为’Waiting for table flush’。步骤3 我们KILL掉了FTWRL会话,这种情况下虽然GLOBAL 级别的S锁会释放,但是全局表缓存版本已经推进了,同时没有使用的table 缓存已经释放掉了。步骤4 再次执行一个baguait1表上的select 查询操作,这个时候在打开表的时候会去判断是否table缓存的版本和全局表缓存版本匹配如果不匹配进入等待,等待为‘Waiting for table flush’,下面是这个判断:整个等待操作和FTWRL一样,会等待占用者释放table缓存后才会醒来继续。因此后续本表的所有select/DML/DDL都会堵塞,代价极高,即便KILL掉FTWRL会话也无用。长时间的免费主机域名DDLDMLFOR UPDATE堵塞FTWRL,因为FTWRL需要获取 GLOBAL的S锁,而这些语句都会对GLOBAL持有IX(MDL_INTENTION_EXCLUSIVE)锁,根据兼容矩阵不兼容。等待为:Waiting for global read lock 。本文的案例1就是这种情况。长时间的select堵塞FTWRL, 因为FTWRL会释放所有空闲的table缓存,如果有占用者占用某些table缓存,则会等待占用者自己释放这些table缓存。等待为:Waiting for table flush 。本文的案例2就是这种情况,会堵塞随后关于本表的任何语句,即便KILL FTWRL会话也不行,除非KILL掉长时间的select操作才行。实际上flush table也会存在这种堵塞情况。长时间的commit(如大事务提交)也会堵塞FTWRL,因为FTWRL需要获取COMMIT的S锁,而commit语句会对commit持有IX(MDL_INTENTION_EXCLUSIVE)锁,根据兼容矩阵不兼容。FTWRL会堵塞DDLDMLFOR UPDATE操作,堵塞点为 GLOBAL级别 的S锁,等待为:Waiting for global read lock 。FTWRL会堵塞commit操作,堵塞点为COMMIT的S锁,等待为Waiting for commit lock 。FTWRL不会堵塞select操作,因为select不会在GLOBAL级别上锁。最后提醒一下很多备份工具都要执行FTWRL操作,一定要注意它的堵塞场景和特殊场景。(1)使用的断点MDL_context::acquire_lock 获取DML LOCKopen_table_from_share 获取table cache instancealloc_table_share 分配table define(share)get_table_share 获取table define(share)close_cached_tables flush table关闭全部table cache instance 和table definereload_acl_and_cache flush with read lock 进行MDL LOCK加锁为GLOBAL TYPE:S ,同时调用close_cached_tables 同时获取COMMIT级别 TYPE SMDL_wait::set_status 唤醒操作close_thread_table 占用者判断释放my_hash_delete hash删除操作,从table cache instance 和table define中释放table缓存都是需要调用这个删除操作的。(2)FTWRL堵塞栈帧由于select堵塞栈帧:(3)杀点FTWRL会话后其他select操作等待栈帧:(4)占用者释放唤醒FTWRL栈帧:“分析MySQL中FTWRL一个奇怪的堵塞现象”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注云技术网站,小编将为大家输出更多高质量的实用文章!
相关推荐: CentOs7中mysql5.7如何实现主从复制配置
这篇文章主要介绍CentOs7中mysql5.7如何实现主从复制配置,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完! MySQL5.7主、从配置具体步骤: 点击(此处)折叠或打开 1.两台MySQL服务器 主:192.168.253.13…