MySQL 8.0 容器反复崩溃排查实录:从端口误用到内存OOM的踩坑与修复
MySQL 8.0 容器反复崩溃排查实录:从端口误用到内存OOM的踩坑与修复
上周在daylay项目的云服务器部署中,我遇到了一个非常典型的“薛定谔的MySQL故障”:用Navicat连接数据库一直提示超时,排查完端口映射以为解决了,结果隔几个小时MySQL容器还是会自动崩溃,反复折腾了两天才定位到真正的根因。整个过程踩了好几个低级坑,也积累了不少小内存服务器跑MySQL的避坑经验,这里完整记录下来。
第一阶段:端口连通性故障的排查
一开始用户提供的连接信息是火山云服务器、端口22、root用户,我下意识就把这个当成MySQL的连接信息用了,结果连了半天一直提示连接失败。第一反应是Docker端口映射没生效,赶紧登录服务器执行docker ps查看容器状态,发现MySQL 8.0容器的端口映射明明是0.0.0.0:16034->3306/tcp,完全正常。
这时候才反应过来:22端口是服务器的SSH端口,根本不是MySQL的业务端口,属于典型的“把SSH连接信息和业务连接信息搞混”的低级失误。调整连接端口为16034之后,连通性测试立刻通过了,本以为问题解决了,结果没过几个小时,用户就反馈MySQL又连不上了。
第二阶段:MySQL反复崩溃的根因定位
连通性恢复后,我加了监控观察MySQL的运行状态,发现它几乎是每2-3小时就会崩溃一次,重启后过几个小时又挂。登录服务器查看MySQL容器的日志,发现报错信息全是内存不足,再执行dmesg | grep oom查看系统日志,果然看到多条OOM Killer杀死mysqld进程的记录:
Out of memory: Kill process 3420 (mysqld) score 999 or sacrifice child
Killed process 3420 (mysqld) total-vm:1515520kB, anon-rss:512000kB, file-rss:0kB
再执行free -h查看服务器内存现状:这台火山云服务器只有2G内存,除了MySQL容器占了近1G,还有十几个同名的update_news.py进程在后台运行,每个占用30-50M内存,加起来就吃掉了近600M内存,加上系统本身占用的内存,可用内存经常只剩几十M,MySQL自然会被系统优先杀死。
除了内存不足的问题,我还顺手看了MySQL的配置,发现默认配置也存在隐患:wait_timeout和interactive_timeout默认是28800秒(8小时),table_open_cache默认是4000,这些配置都是给大内存服务器准备的,在2G内存的机器上会占用大量不必要的内存。
第三阶段:修复方案与落地
确认根因后,我立刻做了四件事:
- 给MySQL 8.0容器加了内存限制,启动参数加上
--memory=768M --memory-swap=768M,避免容器吃光所有内存; - 优化MySQL配置,把
wait_timeout和interactive_timeout降到300秒(5分钟),table_open_cache降到300,减少空闲连接和缓存占用的内存; - 清理冗余的
update_news.py进程,只保留1个运行实例,避免重复进程占用内存; - 给用户提了硬件升级建议:如果业务有增长,最好把服务器内存加到4G,避免后续再出现资源不足的问题。
改完之后我观察了3天,MySQL再也没有出现过崩溃的情况,用Navicat连接公网IP:16034也一直稳定可用。
可带走的避坑经验
这次排查踩了好几个典型的云服务器部署坑,总结几个可以直接用的经验:
- 先确认端口再排查连通性:云服务器默认开22 SSH端口,业务端口(比如MySQL的3306、Redis的6379)都是自定义映射的,连不上先执行
docker ps看端口映射,不要一上来就改配置,避免走错方向。 - 小内存服务器跑MySQL必须做资源限制:Docker启动MySQL容器一定要加
--memory参数限制最大内存,MySQL 8.0最低建议给512M,生产环境至少1G,避免被OOM Killer杀死。 - 小内存场景要调整MySQL默认配置:默认的
wait_timeout=28800、table_open_cache=4000都是给8G以上内存的机器准备的,2G以下内存的服务器建议把wait_timeout降到300-600,table_open_cache降到200-400,能省出不少内存。 - 定时任务一定要加进程锁:很多Python脚本如果没有做进程锁,会跑出大量重复实例,积少成多就会吃光内存,要么用
systemd管理定时任务,要么在脚本里加文件锁避免重复运行。

浙公网安备 33010602011771号