背景
五一期间,我用前端三件套写一个自用的浏览器插件,后端程序用 nodejs,它会连接 MySQL8.0.33 数据库,3306 端口。这个数据库是跑在我的 vmware 虚拟机里的,它同时是我的实验环境,上面还利用多实例部署方式,跑了另外一个 MySQL5.7.39 的实例,运行在 3307 端口。
近期我家中经历了一次断电事件,导致我正在运行的 VMware 虚拟机随着主机一同断电。尽管我的电脑在 BIOS 中设置了电源恢复后自动启动,并且 VMware 也配置了在主机启动时自动开启相应的虚拟机环境,但是在虚拟机重启后,我发现 mysqld 服务并未如预期般自动启动。这与我在之前的文章《多实例下,mysql 服务自启动有概率失败的原因》中所描述的问题相符。这很糟糕,为了解决这件事,有了这篇文章。
导读,希望详细了解本文编写的背景故事,可以按顺序阅读我之前写的两篇文章《MySQL5.7 为什么多了个 sock.lock 文件》《多实例下,mysql 服务自启动有概率失败的原因》
为了节省读者的流量费,我简单地概括一下这两篇文章的内容。
《MySQL5.7 为什么多了个 sock.lock 文件》一文解析了 MySQL5.7 版本为何新增了 mysql.sock.lock 文件。这一改变是为修复一个 Bug:当用户未正确配置 socket 参数,可能导致访问错误的数据库实例,或在尝试关闭数据库时误关其他实例。为此,mysql.sock.lock 文件应运而生。
在《多实例下,mysql 服务自启动有概率失败的原因》一文中,我讨论了 MySQL5.7 引入 mysql.sock.lock 所导致的新问题。这一改动可能导致在多实例混合部署的环境中,MySQL5.7 及以上版本在服务器意外断电重启后,部分 mysqld 实例有可能无法成功启动。针对此问题,我提出了一种解决方案:将 mysql.sock 路径设定在易失性设备 tmpfs 上,这样可以有效避免启动失败的情况。
在 CentOS7 默认安装下,/tmp 并没有独立分区,而是在 / 目录下的。
我最近在测试 openEuler22.03 LTS,我惊讶地发现,这个系统默认安装下,/tmp 是独立分区的,并且就是挂载在易失性设备 tmpfs上的。
(如图,两个系统的默认安装下对比)
这么说,其实 mysql.sock 可以放在 /tmp 目录下,以便利用 tmpfs 特性解决我现在的痛点。
mysql.sock 有三种配置路径
有了上述背景故事,我认为 mysql.sock 一般会有三种配置路径玩法。
- 第一种,@@datadir,也就是数据目录
- 第二种,/tmp,系统的临时目录
- 第三种,专门建了个放 sock 的目录
我佛性询问了 10 位管理 200 个到 几千个 MySQL 实例的老师,他们配置的 mysql.sock 路径如下
调研对象 | 选择 |
---|---|
芬老师(我) | @@datadir |
林老师 | @@datadir |
陈老师 | @@datadir |
李老师 | @@datadir |
杨老师 | @@datadir |
洪老师 | 专门建了个放 sock 的目录 |
威老师 | 专门建了个放 sock 的目录 |
杨老师 2 | 专门建了个放 sock 的目录 |
王老师 | 专门建了个放 sock 的目录 |
田老师 | @@tmpdir |
@@datadir 表示参数 datadir 定义的路径,即放在数据目录下
@@tmpdir 表示参数 tmpdir 定义的路径,即存放 MySQL 临时文件的目录
专门建了个放 sock 的目录,规范一些的话,一般建的目录名叫做 run
,例如 /database/mysql/run/
我们知道定义 MySQL 的 sock 路径是通过 socket
参数控制的。而官方默认值如下
意思是,如果采用源码和二进制方式部署,默认值是 socket=/tmp/mysql.sock
,采用 RPM 部署的话,默认值是 socket=/var/lib/mysql/mysql.sock
socket=/var/lib/mysql/mysql.sock 可以认为是属于第三种"专门建了个放 sock 的目录"的设置。
在生产环境中,大部分人倾向于使用二进制部署方式。我们现在发现一个有趣的现象,大佬们居然不按官方默认值规范来部署设置 socket=/tmp/mysql.sock
,这是为什么?
第一种,mysql.sock 放在/tmp 系统的临时目录
实际上,在以前我学习 MySQL5.6 的时候,老师示范时都是把 mysql.sock 放置在 /tmp 目录下的,再结合二进制部署情况下,默认值也是在 /tmp 下,导致大家都认为这是最佳实践,其实不然。他们这样做的主要原因,其实是为了方便,因为保持和官方默认值一致,那么使用 socket 方式登录数据库时可以省略输入 socket 路径。
mysql -u用户 -p密码 -hlocalhost -S /tmp/mysql.sock
# 默认设置下,和下面这个是等价的
mysql -u用户 -p密码
然而,如果采用多实例部署,一般会修改 socket 名称,例如 /tmp/mysql3306.sock,/tmp/mysql3307.sock。那么,节省输入的优势就不存在了。
从 5.7 开始使用 socket=/tmp/mysql.sock
不是一个好习惯。因为 5.7 新增了一个 mysql.sock.lock 用于解决一个历史 bug。而 mysql.sock 放在 /tmp 目录下,mysql.sock.lock 也会跟随着放在 /tmp 目录下。
/tmp 目录定位就是系统的临时目录,存放非生产应用的文件,文件是允许丢失的。而 mysql.sock 用于数据库的 socket 方式登录,明显不能随便删除。
在 CentOS7 等 Linux 发行版中,有一种叫做 systemd-tmpfiles 的进程,其默认设置是定期删除 /tmp 目录下的 10 天以上的旧文件。
通过 lsof
命令,我们可以看到 sock 文件有多个被打开并处于使用状态的文件句柄。值得注意的是,systemd-tmpfiles 在进行清理操作时,不会清理这些仍处于打开和使用状态的文件。
[root@CentOS7 tmp]# lsof |grep mysql.sock |wc -l
39
但,mysql.sock.lock 并没有处于打开或使用状态的文件句柄,它会被清理!
[root@CentOS7 tmp]# lsof |grep mysql.sock.lock |wc -l
0
为了验证这一点,我们可以通过修改 systemd-tmpfiles 的配置文件进行实验,设置其对超过一分钟的文件进行清理。
vi /usr/lib/tmpfiles.d/tmp.conf
# 修改清理配置文件
...
# Clear tmp directories separately, to make them easier to override
v /tmp 1777 root root 1m # 修改这里,原本是10d,现修改为1m,即创建超过1分钟就视为旧文件
v /var/tmp 1777 root root 30d
...
systemd-tmpfiles --clean # 马上执行清理
我们可以看到,mysql3307.sock 由于有文件句柄在占用,并没有被清理,这非常棒。但 mysql3307.sock.lock 被清理了!
那么 5.7 版本引入的 mysql.sock.lock 机制废了!
这也完全说明我前面调研的 10 位老师公司规范配置没有采用 /tmp 路径并非偶然。
第二种,mysql.sock 放在 @@datadir,也就是数据目录
10 位老师中有 5 位选择把 mysql.sock 放在数据目录里。其中,我就是这么干的。
我这么设置除了可以避免被 systemd-tmpfiles 机制清理外,核心原因是因为——"懒"。
设置到指定目录需要 socket=/database/mysql/run/mysql3307.sock
,而设定到数据目录里只需要 socket=mysql.sock
,少敲很多个字母,他和 socket=/database/mysql/data/3307/mysql.sock
是等价的。(我的 @@datadir 是/database/mysql/data/3307/ )
第三种,mysql.sock 放在专门建立的目录
其实后面 5 位老师,包括了田老师复用了 @@tmpdir 目录,都是这种思想,就是不放在 @@datadir 目录。
我认为有好几个原因:
- Linux 的哲学思想,像 sock、lock、pid 等文件都应该放在 run 目录。
- 放在独立的 run 目录,可以设置不同的进入目录的权限。
我要详细讲一下第 2 个原因,假设有一个用户叫 fander,我允许他登录主机,socket 设置采用了"第二种,mysql.sock 放在 @@datadir"的方法,并且因为我允许他使用 socket 方式连接数据库,那意味着我必须得给他 @@datadir 目录的 x 权限,让他能进入数据目录,虽然权限还不算大,他无法破坏我的数据库。但他至少能看通过 ls
命令看到我数据库里有什么 database,这可能会有隐私泄露风险。
那么我这个时候采用 mysql.sock 放在 run 目录就可以解决这个痛点了。
[root@CentOS7 data]# ll /database/mysql/data/ |grep 3307
drwxr-x--- 6 mysql mysql 4096 May 29 20:05 3307 #数据目录0750权限
[root@CentOS7 data]# ll /database/mysql |grep run
drwxr-xr-x 2 mysql mysql 106 May 29 18:53 run #run目录0755权限
最后,还有一个原因,就是 /database/mysql/run 目录独立出来,可以考虑通过挂载到 tmpfs,解决《多实例下,mysql 服务自启动有概率失败的原因》中提及问题。
哪个是最佳实践?
综上讨论,其实最佳实践,就是 mysql.sock 应该放在专门建立的目录,例如 /database/mysql/run/,并且设置/etc/fstab 开机自动挂载到 tmpfs 上,这适合像我这种强迫症用户。
其他情况下,设置到 @@datadir 也是非常不错的选择,并且有一些管理员就是故意控制 socket 登录方式只能管理员使用,专门放在 @@datadir 里,并且赋予目录 0750 权限。而官方默认值 /tmp/mysql.sock 是最差劲的选择,只适用于测试环境。
dbops 现已提供了上述三种配置路径
dbops 是一款提供生产级别 MySQL 部署的 playbook 工具,欢迎提 issue。
gitee 仓库地址:
https://gitee.com/fanderchan/dbops
Enjoy MySQL!