如何避免 MySQL 因为内存不足被系统 Kill

这两天一台 VPS 上的 MySQL 总是自己宕机,查看了一下日志发现是因为 OOM ,内存不足被系统 Kill 了。

网上稍微查了一下,可以使用修改 syetemd service 的方式来阻止 Linux 因为内存不足而杀死 MySQL。

当然首当其冲原因就是cc攻击

做法

编辑

sudo systemctl edit mysqld.service
sudo systemctl edit mysqld.service
sudo systemctl edit mysqld.service

然后在配置文件中添加

[Service]
OOMScoreAdjust=-1000
[Service]
OOMScoreAdjust=-1000
[Service] OOMScoreAdjust=-1000

保存配置文件之后,重启 MySQL

sudo systemctl restart mysqld.service
sudo systemctl restart mysqld.service
sudo systemctl restart mysqld.service

可以检查配置是否生效,记得替换 MySQL 的 PID。

cat /proc/$(pidof mysqld)/oom_score_adj
cat /proc/$(pidof mysqld)/oom_score_adj
cat /proc/$(pidof mysqld)/oom_score_adj

原因分析

MySQL 宕机并出现 “Out of memory” 问题,通常是由于短时间内应用程序大量请求导致系统内存不足,从而触发了 Linux 内核中的 Out of Memory (OOM) killer 机制。OOM killer 会终止某个进程以释放内存给系统使用。

通过检查相关日志文件(/var/log/),可以看到类似的 Out of memory: Kill process 信息:

Linux 内核根据应用程序的需求分配内存,通常应用程序分配了内存但未全部实际使用。为提高性能,这部分未用的内存可以被其它进程利用。这种内存归属于每个进程,内核直接回收利用较为复杂,因此采用了内存过度分配(over-commit memory)的策略,以间接提高内存使用效率。一般情况下,这种策略是有效的,但当大多数应用程序同时消耗内存时,问题就出现了。此时,所有应用程序的内存需求加起来超出了物理内存(包括 swap)的容量,内核必须通过 OOM killer 终止一些进程来释放内存,保障系统正常运行。可以通过银行的例子来理解:当部分人取钱时银行能够应对,但如果全国人民同时取钱且都想取完自己的钱,银行实际上无法满足。

内核检测到内存不足并选择终止进程的过程。当系统内存不足时,会触发 out_of_memory(),然后调用 select_bad_process() 选择一个 “bad” 进程进行终止。选择 “bad” 进程的过程由 oom_badness() 决定,其算法主要根据进程占用的内存量来判断:

理解了这个算法,我们就理解了为什么 MySQL 总是被首当其冲地终止,因为它的内存占用最大。解决这个问题的最简单方法是增加内存,或优化 MySQL 使其占用更少的内存。此外,还可以优化系统以减少内存占用,使应用程序(如 MySQL)能够使用更多内存。一个临时解决方案是调整内核参数,使 MySQL 进程不易被 OOM killer 选中。

配置 OOM killer

可以通过调整内核参数来改变 OOM killer 的行为,避免频繁终止进程。例如,可以在触发 OOM 后立即触发 kernel panic,并在 10 秒后自动重启系统:

# sysctl -w vm.panic_on_oom=1
vm.panic_on_oom = 1
# sysctl -w kernel.panic=10
kernel.panic = 10
# echo "vm.panic_on_oom=1" >> /etc/sysctl.conf
# echo "kernel.panic=10" >> /etc/sysctl.conf
# sysctl -w vm.panic_on_oom=1
vm.panic_on_oom = 1

# sysctl -w kernel.panic=10
kernel.panic = 10

# echo "vm.panic_on_oom=1" >> /etc/sysctl.conf
# echo "kernel.panic=10" >> /etc/sysctl.conf
# sysctl -w vm.panic_on_oom=1 vm.panic_on_oom = 1 # sysctl -w kernel.panic=10 kernel.panic = 10 # echo "vm.panic_on_oom=1" >> /etc/sysctl.conf # echo "kernel.panic=10" >> /etc/sysctl.conf

从上面的 oom_kill.c 代码可以看到,oom_badness() 会为每个进程打分,根据分数决定终止哪个进程。可以通过调整进程的 oom_score_adj 参数来控制哪些进程不易被选中终止。例如,如果不希望 MySQL 进程被轻易终止,可以找到 MySQL 的进程号,并将其 oom_score_adj 设置为 -15:

# ps aux | grep mysqld
mysql 2196 1.6 2.1 623800 44876 ? Ssl 09:42 0:00 /usr/sbin/mysqld
# cat /proc/2196/oom_score_adj
0
# echo -15 > /proc/2196/oom_score_adj
# ps aux | grep mysqld
mysql    2196  1.6  2.1 623800 44876 ?        Ssl  09:42   0:00 /usr/sbin/mysqld

# cat /proc/2196/oom_score_adj
0
# echo -15 > /proc/2196/oom_score_adj
# ps aux | grep mysqld mysql 2196 1.6 2.1 623800 44876 ? Ssl 09:42 0:00 /usr/sbin/mysqld # cat /proc/2196/oom_score_adj 0 # echo -15 > /proc/2196/oom_score_adj

当然,如果需要的话,还可以完全禁用 OOM killer(不推荐在生产环境中使用):

# sysctl -w vm.overcommit_memory=2
# echo "vm.overcommit_memory=2" >> /etc/sysctl.conf
# sysctl -w vm.overcommit_memory=2
# echo "vm.overcommit_memory=2" >> /etc/sysctl.conf
# sysctl -w vm.overcommit_memory=2 # echo "vm.overcommit_memory=2" >> /etc/sysctl.conf
© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容