凌晨两点,站点挂了。宝塔面板能打开,但网站 502,数据库连不上。
SSH 进去一看,MySQL 进程没了。
查 dmesg:
Out of memory: Kill process 12453 (mysqld) score 892 or sacrifice child
Killed process 12453 (mysqld) total-vm:3821456kB, anon-rss:2891
MySQL 占了 2.8 G 内存,被系统杀了。
这台机器一共 4 G。
为什么会被杀
Linux 有个机制叫 OOM Killer。内存不够的时候,它会挑一个"占内存最多、又不太重要"的进程杀掉,腾出空间。
MySQL 因为占得多,经常是首选。
但 MySQL 本身不是故意吃这么多。正常的 MySQL 配置,内存占用应该是可控的。
问题是配置错了。
MySQL 的内存怎么算
主要看一个参数:innodb_buffer_pool_size。
这是 InnoDB 的缓存池,用来缓存数据和索引。越大越快,但不能超过物理内存的一定比例。
经验值:独立数据库服务器,可以设到物理内存的 70%。和网站同一台机器,最多 40%。
我这台 4 G,还跑着 nginx 和 PHP,MySQL 最多给 1.5 G。
结果一看配置:
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
-- 3221225472 = 3 G
3 G 全给了缓存池,加上连接缓冲、排序缓冲,实际占用能到 4 G 以上。系统一紧张就杀。
改配置
在 /etc/my.cnf 里改:
[mysqld]
innodb_buffer_pool_size = 1024M
innodb_buffer_pool_instances = 1
max_connections = 80
tmp_table_size = 32M
max_heap_table_size = 32M
sort_buffer_size = 2M
join_buffer_size = 2M
read_buffer_size = 1M
几个说明:
max_connections 默认 151,每个连接都要分配线程栈和缓冲。80 个连接乘以每个连接几 M 的缓冲,就是几百 M。降到 80 够用了。
sort_buffer_size 和 join_buffer_size 是每个连接单独分配的,不是全局。设太大,并发一高就爆。
改完重启:
systemctl restart mysqld
然后观察实际占用:
ps aux | grep mysqld | grep -v grep
PHP-FPM 也在吃内存
MySQL 稳住了,但 PHP 也在吃。
PHP-FPM 的进程模型是:预先起一批进程,每个进程占一份内存。进程数由 pm.max_children 控制。
宝塔默认给的是 pm = dynamic,max_children = 50。
50 个进程,每个跑 WordPress 占 60-80 M,全起来就是 3-4 G。
改成静态的、固定数量的:
pm = dynamic
pm.max_children = 12
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500
max_children 怎么定?用这个公式:
可用内存 ÷ 单进程内存 = 最大进程数
比如 MySQL 占了 1.2 G,系统和其他占 0.5 G,剩 2.3 G 给 PHP。单进程 80 M,那就是 28 个。
但实际不用给满,留点余量。我给的是 12。
站点日 IP 几百,12 个进程完全够。
要不要开 swap
这是个争论很多的话题。
开 swap 的好处是:内存紧张的时候不至于直接杀进程,用硬盘顶一下,不会宕机。
坏处是:swap 在硬盘上,速度比内存慢几百倍,一旦开始大量用 swap,整个机器会卡成幻灯片。
我的做法是:开,但设小一点,2 G。
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
然后把 vm.swappiness 调低,让它尽量不用 swap:
echo 'vm.swappiness=10' >> /etc/sysctl.conf
sysctl -p
swappiness 默认 60,意思是内存用了 40% 就开始往 swap 挪。改成 10,用到 90% 才挪。
现在的状态
改完之后:
- MySQL 稳定在 1.1 G 左右
- PHP-FPM 12 个进程,加起来 0.9 G
- 系统和其他 0.6 G
- 总共 2.6 G,4 G 的机器留了 1.4 G 余量
跑了半年,没再 OOM 过。
怎么提前发现
别等宕机了才知道。装个监控,或者最简单的,写个脚本每小时记一次:
free -m | awk 'NR==2{printf "内存使用: %s/%s MB (%.1f%%)\n", $3, $2, $3*100/$2}' >> /var/log/mem.log
也可以直接看 OOM 记录有没有新增:
dmesg | grep -i "out of memory"
OOM 不是"内存不够",是"配置超过了硬件"。加内存能解决,但改配置更省钱。
1、本站所有资源均收集于互联网,分享目的仅限学习与参考。如有侵权,请联系 1685698671@qq.com 及时删除!
2、本站资源不保证完整性和安全性,下载后请自行检测。使用过程中出现的任何问题均与本站无关,敬请自行处理。
3、本站资源仅限个人学习研究使用,严禁用于任何违法违纪用途。因使用者违规使用产生的一切后果,由使用者自行承担,本站不承担任何责任。


评论(0)