记1例MySQL 8.0.15版本Bug引起的风波
一、背景
2016年09月12日,对于数据库领域来说是一个重要的值得纪念的日子,MySQL第一个DM(development milestone)版本8.0.0发布,这是Oracle收购MySQL后第一次将版本由5系列直接提升为8系列,MySQL 8.0是全球最受欢迎的开源数据库的一个非常令人值得兴奋的新版本,功能、性能全面改进。
MySQL 8最让人惊艳的就是MGR特性,这也是大家都想尝试的功能,它是一个MySQL自带的高可用方案,它的优势就是配置简单,如果用的朋友会知道它比经典的MySQL + MHA的架构要轻很多,部署步骤也少很多,原生支持的特性也让这个功能更可靠,同时支持单主和多主的特点,也让大家对它的应用场景充满期待。
我们实际生产中采用的MGR部署架构是单主模式,1台Primary节点负责write,2台Secondary负责read。
二、现象
在平稳运行几个月后,某天突然收到报警,其中有几个重要的信息拿出来跟大家分享下:
1、程序端报错:
ERROR dbtools.get_table_mysql_connect:865 (1135, "Can't create a new thread (errno 11); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug")
2、数据库报错:
数据库只有root用户可以登录上去,其它用户无法登录,root执行命令后返回报错信息如下
1135, "Can't create a new thread (errno 11); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug"
3、操作系统层面异常:
Mysql打开了大约产生了12万个内部临时表文件
三、第1次分析
这个现象最常见的是因为tmpdir设置成/tmp,最终会导致根目录空间不足,数据最终无法对外提供服务。
而这次又跟以前不一样,不是磁盘空间问题导致,根据现象看是跟内部临时表产生后不释放有关系,正常的现象应该是内部临时表用完后系统会自动回收,不应该会产生这么多的文件不释放的现象,经过与其它系统对比后,这个值在800~900之间,但这组集群的内部临时表文件在12万+的量级,也是逆天了。
最后经过查找官方文档,有这样一段描述:
Storage Engine for On-Disk Internal Temporary Tables
Starting with MySQL 8.0.16, the server always uses the InnoDB storage engine for managing internal temporary tables on disk.
In MySQL 8.0.15 and earlier, the internal_tmp_disk_storage_engine variable was used to define the storage engine used for on-disk internal temporary tables. This variable was removed in MySQL 8.0.16, and the storage engine used for this purpose is no longer user-configurable.
In MySQL 8.0.15 and earlier: For common table expressions (CTEs), the storage engine used for on-disk internal temporary tables cannot be MyISAM. If internal_tmp_disk_storage_engine=MYISAM, an error occurs for any attempt to materialize a CTE using an on-disk temporary table.
In MySQL 8.0.15 and earlier: When using internal_tmp_disk_storage_engine=INNODB, queries that generate on-disk internal temporary tables that exceed InnoDB row or column limits return Row size too large or Too many columns errors. The workaround is to set internal_tmp_disk_storage_engine to MYISAM.
其中的一段话引起了我们的注意
This variable was removed in MySQL 8.0.16, and the storage engine used for this purpose is no longer user-configurable.
也就是说这个参数肯定是有问题,所以才会被8.0.16所废弃掉。深入查询这个参数后发现,从5.7.5开始,新增一个系统选项 internal_tmp_disk_storage_engine 可定义磁盘临时表的引擎类型为 InnoDB,而在这以前,只能使用 MyISAM。
从8.0.16开始临时表的存储引擎默认使用 innodb且此参数不可再由用户配置,故 internal_tmp_disk_storage_engine 已经被移除。
四、第1次处理
我们首先怀疑是这个参数的问题,于是我们将整个集群维护了一次,将这个参数的值改成了5.7.5以前的默认值,即改为MyISAM。
在维护期间也发现了一个奇怪的问题,就是当连接数据库的程序已经停止后,这些内部临时表文件还是不释放,文件数量仍然维持在一个高水位,很不正常,mysqld服务停止后这些文件才被释放掉。
处理后的次日,本以为这个问题解决了,早上一查mysql打开的文件数,结果不太理想
[root@localhost] lsof -u mysql|wc -l
21323
文件数量达到了2万+的水平,按这个量级6天左右,仍然会达到12万这个数量级,同样会触发上次的故障。
五、第2次分析
Baidu里找遍了国内的资料,没有太多可参考的内容。
只能去oracle官方查了,结果很快的,我们在社区中找到了这样一个标题的文章“Mysql daemon not releasing deleted temp file”,具体链接是https://community.oracle.com/message/15435569?tstart=0
这篇文章跟我们遇到的现象一模一样,同样也指出了这是一个已知的Bug
you are hitting a known bug that affected MySQL 5.7.22 - 5.7.25 and 8.0.14 - 8.0.15. It has been fixed in 5.7.26 and 8.0.16. Oracle BUG id is 28039829 "File handles are leaked for SELECT queries involving complex joins." The way MySQL uses on-disk internal temp tables (those produced during a query, not the explicit temporary tables that a user can create), to not leave any temp files behind in case of a server crash, by design MySQL creates the tmp file, and then immediately unlinks it (so it appears to the OS as deleted), but MySQL keeps the descriptor open to the file.
通过这段描述能看到这是一个已知的BUG,并且BUG编号是28039829,受影响的版本是5.7.22 - 5.7.25 以及 8.0.14 - 8.0.15,并且在5.7.26 和8.0.16中被修复了。
问题终于水落石出了,因为是MySQL8.0.15产品本身的问题,所以只能放弃这个版本,通过升级到8.0.16才能更好的解决掉这个问题。同样也感到很巧的是,baidu中搜索不到相关问题的现象和解决方案,但我们团队遇到了,我们可能是这个问题在国内的第一个吃螃蟹的团队,这个经验也是非常宝贵的,所以拿出来跟大家分享,增强一下国内社区的内容。
六、第2次处理
这次处理的方案就很明确了,升级到8.0.16。但出于严谨的考虑,也担心这个问题会不会仅是在MGR场景下才会发生,所以我们制定的方案是先由8.0.15版本的MGR集群升级到经典的8.0.16 Master-Slave架构,这样就可以先验证MySQL 8.0.16本身是否会修复此问题。
经过1周的观察后,发现故障没有复现,说明我们的升级是生效的,也首先确定了,8.0.16的普通主从架构是没有这个问题的。
然后我们又将8.0.16的主从架构,再次切换为8.0.16的MGR集群,以便验证这并不是MGR集群才会有的问题。
经过1周的观察后,发现故障没有复现,说明这个BUG跟MGR集群也无关,是单纯的8.0.15的Bug。
后续我们也会在源码方面会进一步分析,希望能进一步定位哪些应用场景会触发这个BUG,以便更好的理解这个问题。
七、总结
经过2周的时间,经过不断的分析尝试,一次版本升级,2次部署架构调整,这个问题终于解决完毕,对国内MySQL 8.0.15版本在生产环境的应用有很大的参考价值。
感谢恒昌公司技术中心运维部多个小组的通力协作,尤其是张锋的大力支持,还有李勇在服务器资源方面的积极协助,才能让这个问题水落石出,才能为国内社区积累这个宝贵经验。
再次感谢投稿的几位作者,同时也欢迎更多优质作者来稿!后台私信小编即可。
——更多精彩👇——