暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

多实例下,mysql服务自启动有概率失败的原因

背景

我打开我的 vmware 虚拟机,一台虚拟机里部署了 4 个 MySQL 实例,发现有一个实例没有起来。由于几天前我就遇到过一次,毕竟是自测环境,当时没有太在意,这次再次遇到这个问题,我决定查明真相。

排查的第一步,查看错误日志

首先我的 4 个实例分别运行在 3307、3308、3309、3310,我就称之为 3307、3308、3309、3310 实例好了。这次案件,我的 3307 实例起不来了,那当然第一步就是查看错误日志了,我找到了这段关键日志

[root@192-168-199-132 ~]# less /database/mysql/data/3307/err.log
...(此处忽略N行)
[ERROR] Another process with pid 893 is using unix socket file.
[ERROR] Unable to setup unix socket lock file.
[ERROR] Aborting

[Note] Binlog end
[Note] Shutting down plugin 'validate_password'
[Note] Shutting down plugin 'rpl_semi_sync_slave'
[Note] Shutting down plugin 'rpl_semi_sync_master'
...(此处忽略N行)
[Note] Shutting down plugin 'MRG_MYISAM'
[Note] Shutting down plugin 'sha256_password'
[Note] Shutting down plugin 'mysql_native_password'
[Note] Shutting down plugin 'binlog'
[Note] /database/mysql/base/5.7.39/bin/mysqld: Shutdown complete

复制

可以看出,自启动服务是有跑的,只是遇到了 ERROR,导致停止了启动,关闭了数据库。

【关键信息】

[ERROR] Another process with pid 893 is using unix socket file.
[ERROR] Unable to setup unix socket lock file.

复制

从 ERROR 可以看出,3307 实例启动失败,是因为有一个 pid 为 893 的进程在使用 3307 的 socket 文件,导致设置 3307 的 socket lock 文件失败。

sock.lock 是什么,为什么要设置他?

上周的文章,我已经分析了 sock.lock 是什么,他解决了什么问题。为什么开启 MySQL 时需要设置它,详细文章见《MySQL5.7为什么多了个sock.lock文件》(点击跳转)

这里,我把我总结的原理截图再贴出来

这里说明一下,原理截图是我基于现象分析和总结出来的,而非基于源代码分析的结果,细节上可能有错误之处,如不正确请及时指出。

mysqld.sock.lock 为什么已经存在?

3307 没有启动,那为什么 mysqld.sock.lock 就已经存在呢?原因就是上次一次 3307 实例没有正常关闭,常见的是两种情况:

  • mysqld server 异常关闭 (crash 或者 被 kill -9 了)
  • Linux server 异常关机 (crash 或者 掉电了)

上述两种情况,均导致没有走正常 mysqld shutdown 流程,所以不能正常关闭和移除 mysql.sock、 mysql.sock.lock,那么下一次开机时 mysqld.sock.lock 就存在了。

对于 mysqld server 异常关闭这个场景,假设之前的 pid=893,那么这个 mysqld crash 后,pid=893 这个进程号就永远消失了,因为 Linux fork 新进程 pid 是递增的,也就是这时手动或通过mysqld_safe/systemd 等方式自动重启 mysqld,都不会有"原理图"提及的 pid 占用问题,所以这个场景并不会阻拦 mysqld 服务的启动。

所以,我们必须是遭遇了后一个场景,Linux server 异常关机的场景。

我们这个案例里,明显是遇到了“原理图”中红框的问题。

查看服务器进程,mysqld.sock.lock 文件记录的 pid=893,而 pid=893 确实已经被别的 mysqld 程序( 3310 实例)占用了,所以 3307 实例启动失败。

所以,在“原理图”中红框中,我分析如下:

  • "如果 mysql.sock.lock 文件已经存在,则先判断 mysql.sock.lock 文件里记录的进程号有没有别的程序占用了,有则启动 mysqld 失败" —— 对应的是 3307 实例启动失败。
  • "如果没有,则把这个进程号覆盖写入 mysql.sock.lock 文件。"—— 对应的是 3308、3309、3310 实例启动成功。

继续分析这次 case

我的 mysqld 服务是跑在 vmware 上的,而前几天我的小孩把我的物理机物理关机了,这相当于“掉电”故障,导致 vmware 异常关闭,所以数据库也异常关闭了,所以 4 个实例的 mysqld.sock.lock 全部没有移除 。

如图,第一个命令,我查看了 4 个实例的 mysqld.sock.lock 文件,获取里面的 pid 的值,可以发现在这次掉电后启动服务器后,3307 和 3310 的 pid 就是冲突的。

第二个命令,获取现在的所有在线的 mysqld 进程,能发现 3307 没有跑起来,其他 3 个实例都跑起了。

原因其实很简单,就是我的服务器几乎没有别的乱七八糟的服务,开启启动项我只设置了这四个实例,那么整台服务器启动的时候,各 mysqld 获取的 pid 很大概率就落在 881- 900 这个范围内,假设这个范围的 pid 获取是完全随机的,那 3307 上次启动成功获取到 pid=893 的概率是 1/20,我并不想精确的计算概率,我的意思是有概率,概率还不低。掉电后,这个 mysqld.sock.lock 没有被移除,内容是 893,意味着,如果有任何其他 mysqld 进程早于 3307 启动,并且刚好分配到 pid=893 的进程号,那么就会阻止 3307 的启动。

按照 pid 的大小顺序,我分析当时很大概率启动顺序是这样的: 

3309 实例启动成功 -> 3310 实例启动成功 -> 3307实例启动失败(根据mysqld.sock.lock记录的pid=893,发现有冲突)-> 3308 实例启动成功

如何解决

直接 rm -rf database/mysql/data/3307/mysqld.sock.lock
移除掉这个锁文件就可以了。因为删除后,它改为走下图的红框逻辑了,不再被“mysqld.sock.lock 已存在”的逻辑阻挡了。

如何规避

现在我们知道,导致多实例下 mysql 服务自启动有概率失败的原因就是因为上次 mysqld 异常关闭导致没有清理 mysqld.sock.lock,而 mysqld.sock.lock 的判断逻辑会阻止本应该可以正常启动的 mysqld 服务启动。所以我们只要有办法让 mysqld.sock.lock 在 mysqld 异常关闭的情况下会删除即可。

我的想法是——易失性设备 tmpfs

因为 tmpfs 是一种基于内存的文件系统,不像其他的文件系统需要格式化后才可以挂载,而是直接使用,由于是在内存中存储数据,那么在断电后会导致数据丢失。所以在 my.cnf 中设置

[mysqld]
socket = /tmp/mysql3307.sock

复制

并且把 /tmp 目录挂载到 tmpfs 文件系统,即可以让 sock 和 sock.lock 文件开机时自动删除,避免异常关机遗留的 sock.lock 文件影响数据库服务正常启动。

当然,本解决方案只是一个探索,不建议用于生产。生产环境要自动化处理可以用 HA 写一段逻辑来解决。


最后修改时间:2023-05-26 16:27:47
文章转载自芬达的数据库学习笔记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论