利用 Linux Crontab,每天定时重启 Nginx / MySQL / PHP 服务,避免内存泄漏导致内存占用率过高

服务器有再大的内存都不嫌多,为了能够给业务提供最快的响应速度,我们恨不得所有数据都缓存在内存中。然而增加任何服务器规格都会导致成本上升,所以购买服务器时应根据业务需求来选购。

虽然如今的云服务器可以动态地调整资源,但调整配置应该针对特定时期的业务高峰,正常情况下我们要“压榨”服务器的所有潜力,利用每一分资源。优化好的情况下 1 核 1GB 服务器也能够支撑起简单的业务。

1核1GB服务器资源占用

内存泄漏是不可轻视的问题。正常情况下,程序会释放不再使用的内存给其他程序使用,而内存泄漏会导致大量内存变得不可利用。内存占用高,系统就会开始使用 SWAP,而 SWAP 受制于硬盘的 IO 速度,必将拖慢业务的响应时间。严重情况下程序挂起、服务器卡死,进而业务停止,这将带来巨大损失。

如何回收泄漏的内存?重启服务器耗时费力,不如重启程序来得方便,而且后者能实现和前者相似的效果。此操作有两个特点:定时和重复,特别适合用 Linux 的 Crontab 定时执行。

本文以流行的 LNMP 架构来举例。L 代表 Linux,是使用最广泛的服务器系统,也是最适合使用脚本来管理的系统;N 代表 Nginx,是当前最流行的服务器程序;M 代表 MySQL,是使用最多的数据库;P 代表 PHP,最常用的服务端脚本语言,用 WordPress 制作的网站占全部网站总数的 30% 以上,它便是用 PHP 写的。

在编辑 Crontab 规则之前先了解一下它的格式(引用自:菜鸟教程):

f1 f2 f3 f4 f5 program

  • 其中 f1 是表示分钟,f2 表示小时,f3 表示一个月份中的第几日,f4 表示月份,f5 表示一个星期中的第几天。program 表示要执行的程序。
  • 当 f1 为 * 时表示每分钟都要执行 program,f2 为 * 时表示每小时都要执行程序,其馀类推
  • 当 f1 为 a-b 时表示从第 a 分钟到第 b 分钟这段时间内要执行,f2 为 a-b 时表示从第 a 到第 b 小时都要执行,其馀类推
  • 当 f1 为 */n 时表示每 n 分钟个时间间隔执行一次,f2 为 */n 表示每 n 小时个时间间隔执行一次,其馀类推
  • 当 f1 为 a, b, c,… 时表示第 a, b, c,… 分钟要执行,f2 为 a, b, c,… 时表示第 a, b, c…个小时要执行,其馀类推
Crontab 时间参数解释
Crontab 时间参数解释,来源:菜鸟教程

有了上面的基础,接下来就可以开始添加 Crontab 规则了。

首先切换到 root 用户,Crontab 命令的执行与用户相关,重启网站服务需要 root 权限,因此需要以 root 身份编辑。

接着运行 crontab -e 进入编辑状态,在已有规则的末尾(每条规则一行)添加规则。这里以重启 Nginx MySQL 和 PHP 为例,我打算让它们在每天凌晨最少访客的时间重启。用 Google Analytics 可以直观地看到用户活跃的时间段,颜色越深说明该时段用户越多。

按时段划分的用户数
可以看到 2-7 点访客数量最少

有了访客活跃时间段的数据,只需要挑选一个访客少的时间来设置。我考虑到 4 点多用户少,于是将 3 个程序间隔一分钟重启。为什么不在同一个时间重启呢?其实也是可以的,但是如果担心同时重启会导致性能占用飙升,可以像我一样,每个任务留出一个很短的间隔。

31 4 * * * systemctl restart nginx > /dev/null 2>&1
32 4 * * * systemctl restart php-fpm > /dev/null 2>&1
33 4 * * * systemctl restart mysql > /dev/null 2>&1

参考上文提到的 Crontab 规则,以上 3 条命令规定在每天凌晨 4:31 / 4:32 / 4:33 分别重启 Nginx / PHP / MySQL。编辑完成记得保存。

如何查看 Crontab 计划是否执行成功呢?很简单,查看 Crontab 的日志就行。以 CentOS 7 为例,运行 cat /var/log/cron 即可查看日志。

Jul 10 04:31:01 louis-NB CROND[5061]: (root) CMD (systemctl restart nginx > /dev/null 2>&1)
Jul 10 04:32:01 louis-NB CROND[5080]: (root) CMD (systemctl restart php-fpm > /dev/null 2>&1)
Jul 10 04:33:01 louis-NB CROND[5096]: (root) CMD (systemctl restart mysql > /dev/null 2>&1)