MySQL被OOM Kill排查:swap、buffer pool与监控取舍

    发布时间:2026-09-27 05:01 更新时间:2026-09-27 05:01 阅读量:0

    网站跑得好好的,突然数据库连不上,宝塔面板里 MySQL 显示已停止,重启几分钟后又挂。翻系统日志看到一行 Out of memory: Killed process ... (mysqld),这就是典型的 OOM Kill:不是 MySQL 自己崩了,而是系统内存不够,内核挑中了占用内存最多的 mysqld 把它干掉。对站长来说,这不只是「重启一下」的事,反复发生会拖坏 InnoDB 数据页、伤到正在写入的表。下面把成因、参数取舍和监控方法讲清楚。

    先搞懂内核为什么挑中 MySQL

    Linux 在内存吃紧时会先尝试回收 page cache 和可回收的 slab,回收不动才触发 OOM Killer。它给每个进程算一个 oom_score,占用物理内存越多、分值越高,越容易被选中。mysqld 往往是单机内存占用最大的进程,自然首当其冲。要注意的是,容器环境(比如 Docker、部分云主机)里的 OOM 行为由 cgroup 控制,看的是容器内存上限而不是宿主机总量,排查时别只看 free -h。

    确认是不是 OOM,最直接的是看内核日志:

    dmesg -T | grep -i -E "oom|killed process"
    

    或

    journalctl -k --since "2 hours ago" | grep -i oom grep -i oom /var/log/messages 2>/dev/null

    输出里通常能看到被杀的进程名、PID 和当时的 total-vm、anon-rss 数值,anon-rss 就是它实际占的物理内存。如果被杀的是 mysqld,同时 MySQL 错误日志最后几行没有明显的 InnoDB 报错,那基本可以确认是内存不足而不是数据损坏。MySQL 错误日志位置一般在 /www/server/data/ 或 /var/log/mysql/ 下,具体以实际环境为准。

    innodb_buffer_pool_size 该给多大

    InnoDB 缓冲池是 MySQL 内存占用的绝对大头,它缓存数据页和索引页,设得合适能显著减少磁盘 IO。经验做法是:如果这台机器只跑 MySQL,缓冲池可以占到物理内存的 50%~70%;如果同时还跑着 Nginx、PHP-FPM、Redis,就得把这些进程的常驻内存先扣掉,剩下的再分给缓冲池,否则很容易把系统推到 OOM 边缘。

    估算时不要只看「内存总量」,要看「可稳定让出的内存」。一个简化的算法是:物理内存减去系统基础占用(几百 MB)减去 Web 与缓存进程的 RSS 峰值,再留 10%~20% 余量给突发流量。查看当前配置:

    mysql -uroot -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
    

    查看 mysqld 实际内存占用

    ps -o pid,rss,vsz,cmd -C mysqld

    RSS 是常驻物理内存,VSZ 是虚拟地址空间,判断是否吃紧看 RSS 更准。调小缓冲池会让命中率下降、查询变慢,所以不要一遇 OOM 就把这个值砍到很小;更合理的顺序是先确认是不是有慢查询、大表扫描或临时表把内存吃掉了。

    swap 要不要开,监控看什么

    关于 swap 常见两种极端:一是完全关闭,内存一满就直接 OOM;二是开得很大,MySQL 冷数据被换出,查询延迟飙升。对数据库服务器,比较稳妥的做法是留一小块 swap(比如物理内存的 1/4 到 1/2,视磁盘类型而定),并调低 vm.swappiness,让内核优先回收缓存而不是急着换出数据库页。具体取值以官方文档和实际负载测试为准,不要照搬某个固定数字。

    free -h
    swapon --show
    sysctl vm.swappiness
    

    临时调整(重启失效),需写入 /etc/sysctl.conf 才持久

    sysctl -w vm.swappiness=10

    监控层面,光看「内存使用率」不够,要盯三个信号:可用内存是否长期低于总量 10%、swap 使用量是否持续增长且不回落、以及 dmesg 里有没有 OOM 记录。宝塔面板自带的监控能看到趋势,但粒度较粗,建议再配合一个轻量采集(如 node_exporter + Prometheus,或 Uptime Kuma 看进程存活)。日志侧可以用 logrotate 控制 MySQL 错误日志大小,避免磁盘被写满引发另一类故障。

    另外提醒一点:不要用「定时重启 MySQL」来掩盖 OOM。重启只能短暂释放内存,如果根因是缓冲池过大或存在内存泄漏式的大查询,问题会周期性复发,还可能中断正在执行的事务。正确路径是:先用 dmesg 确认 OOM、用慢查询日志找出吃内存的 SQL、再决定是调小缓冲池、加内存还是优化查询。

    小结一下:OOM Kill 的本质是系统级内存分配失败,MySQL 只是那个「最显眼的受害者」。排查顺序建议固定为——看 dmesg 确认、看进程 RSS 定位、看慢查询找诱因、最后才动 innodb_buffer_pool_size 和 swap 参数。把这三个信号纳入日常监控,比事后重启更省心,也能避免下次流量高峰时数据库再次被系统「清理」掉。

    继续阅读

    📑 📅
    HTTPS混合内容批量修复:控制台与curl定位http资源 2026-09-27
    宝塔面板部署 WordPress 后固定链接 404:伪静态规则加载顺序与 try_files 排查 2026-09-27
    网站301与302混用导致权重分散:跳转类型选择与生效验证 2026-09-27
    WordPress后台被暴力撞库告警:登录限速与日志加固 2026-09-26
    域名下PC站与m站SEO冲突:canonical、自适应与UA跳转取舍 2026-09-26
    Nginx resolver 域名解析缓存:反代上游换 IP 后仍走旧地址的排查 2026-09-27
    宝塔面板日志被CDN节点IP填满:log_format与real_ip联动调整 2026-09-27
    WordPress 数据库表前缀修改实战:从 wp-config 到 SQL 批量改名 2026-09-27
    Nginx泛域名证书自动签发:acme.sh DNS验证与泛解析站点批量部署 2026-09-27
    服务器内存充足却频繁502:PHP-FPM进程回收与pm.max_requests取舍 2026-09-27