最近一个朋友在做大表truncte的时候出现了堵塞。SEMAPHORES几乎都是DICT_SYS
对于show processlist显示如下,并且killed标记已经不做响应了。
从时间来看我们发现truncate命令执行的时间最长,那么可能是它造成的问题,但是为什么会造成问题呢。结合pstack简单分析一下
这里先贴出堵塞源头的truncate语句的栈,如下:
实际上我们的truncate有很多步骤,其中步骤9为
这一步中包含3个代价较高的部分,从DEBUG DICT_SYC的加锁和释放来看,5.7.22版本如下:
如上故障的栈正是在进行AHI的维护,并且在dict_drop_index_tree中开头就对ut_ad(mutex_own(&dict_sys->mutex))进行了断言,也就是说这里肯定是在持有dict_sys锁的情况下进行的,那么我们来看一下到底发生了什么,我贴出大概的执行的步骤如下:
稍微总结一下如下,下面做这些的目的是精准的清理掉AHI中的信息:
我们发现这实际上是一个5层循环,代价最高的落在最后的循环每个page中每一行的每个字段上,如果page存在于这正是这个栈带给我们的信息。
这些步骤需要持有DICT_SYS这样一个全局字典结构的保护锁进行,而在很多地方比如如下的函数中都会持有这个锁进行互斥保护:
这2个函数也是本例中等待的会话,并且在进行mutex的带的时候并不会被kill标记唤醒,因此kill是不能生效的。我在查询BUG的发现也有不少人遇到类似的问题如下:
https://bugs.mysql.com/bug.php?id=91977
但是bug的状态一直没有变为closed,在这个BUG的最后也有人问出问题:
其次我们来也简单讨论下truncate在innodb buffer维护上的代价,对于5.7来讲这个地方和drop是有区别的,函数接口 buf_LRU_flush_or_remove_pages 用于确认是否维护 LRU list,其中有三种类型:
因此5.7最好使用drop+create代替trucnate。
随后我对一个稍微大的表truncate,测试发现代价如我们描述:
正如我们描述的,我这里实际上没有建立AHI,但是全表扫描了一次,那么大量的page加载到了innodb buffer。这里btr_search_drop_page_hash_when_freed实际就是AHI维护的上层接口,如果有大量的AHI的存在,代价确实客观,并且这个是持有DICT_SYS的 *** 作。
对于AHI的维护方面直到8.0.21当时我看一个增加分区慢的case的时候依旧存在一些问题提交的BUG如下:
https://bugs.mysql.com/bug.php?id=101900
因此建议如下:
我主要使用了如下断点
5.7 drop栈
8.0 为BUF_REMOVE_NONE
某天晚上,数据库 hang 住,现象是:
无奈之下通过强制 kill 掉进程,重启数据库恢复。
这里暂且不说 hang 住的原因,仅分析数据库 hang 住,但是 MHA 未触发切换。
先说下结论,MHA 默认使用长连接对数据库做 ping 健康 检测(执行 select 1 as Value ),4次无法连接 MySQL 则触发切换。 前面数据库 hang 住只是新的连接无法建立,但是老连接却没有影响,且 MHA 的 健康 检测语句很简单,只在 server 层进行了检测,不涉及到 InnoDB 层,所以 MHA 认为 MySQL 是 健康 的,并没有作出任何决策。
MHA 从 0.53 版本开始支持 ping_type 参数设置如何检查 master 的可用性。支持3个 value :
通过将 ping_type 修改设置为 connect ,MHA 每次进程状态检测,需要新建连接,新链接无法成功建立,就触发了切换。
三种检测机制代码:
MHA 配置文件
模拟服务器CPU满负载,数据库无法建立新连接 编写一个简单的c程序,如下:
编译:
执行:
另外再跑两个 mysqlslap 压测程序:
有兴趣的同学可自行测试一下
调用链路:
MHA 监控进程启动后,会持续监控主节点的状态,主要的 健康 检测函数是 wait_until_unreachable()。
在这个函数中会有一个死循环,持续地进行 健康 检测
1.首先,测试连接,连接正确返回0,否则返回1。
2.测试连接成功后,则进行 健康 状态检测(前面说的3种方式);如果连续4次连接失败,则在第4次的时候会使用第二脚本进行检测(如果定义了的话),如果检测通过,则认为 master 挂掉
关键函数 wait_until_unreachable()代码:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)