mysql被oom killer杀死本质是系统内存耗尽,需三步处理:先用dmesg确认oom事件;再调低innodb_buffer_pool_size(≤物理内存60%)、tmp_table_size(64m)、max_connections;最后优化查询,如straight_join控制驱动表、拆分join、禁用performance_schema等。

第一步:确认确实是OOM Killer下的手
别猜,直接查证据:
- 运行 dmesg -T | grep -i "killed process",如果输出里有 Out of memory: Kill process XXXX (mysqld),时间点和MySQL宕机时间吻合,就100%坐实
- 补充验证:cat /proc/$(pidof mysqld)/status | grep VmRSS,看实际驻留内存(RSS)是否远超你配置的
innodb_buffer_pool_size—— 超得越多,说明 sort/join/tmp 表等隐性开销越猛 - 注意:/var/log/messages 在部分新版系统(如CentOS 8+/AlmaLinux)里默认不记录OOM事件,dmesg 是唯一可靠来源
第二步:立刻压住MySQL总内存上限
全表JOIN往往触发大临时表、大排序、大哈希,这些都从 per-connection 缓冲区里现拿内存,乘上连接数就是灾难。重点调这三项:
- innodb_buffer_pool_size ≤ 物理内存 × 50%~60%,且必须预留 ≥2GB 给系统+其他服务;例如16GB机器,设为8G或9G,别硬塞12G
- tmp_table_size = max_heap_table_size = 64M(别超128M),强制大结果走磁盘临时表,宁慢勿崩
-
max_connections = 实际并发上限×1.2(比如网站日常最高80连接,设为100即可),再配合:
sort_buffer_size = 256K
join_buffer_size = 256K
read_rnd_buffer_size = 512K
第三步:让全表JOIN本身不爆内存
参数调完只是保命,真要跑通大JOIN,还得改查询逻辑和执行方式:
- 加 STRAIGHT_JOIN 或用 FORCE INDEX 控制驱动表顺序,避免优化器选错表导致中间结果爆炸
- 拆大JOIN:先用 CREATE TEMPORARY TABLE + INSERT ... SELECT 分步物化中间结果,每步控制数据量
- 禁用可能导致抖动的组件:performance_schema = OFF(尤其别开 memory/% instrument),query_cache_type = 0
- 检查是否启用了 innodb_buffer_pool_dump_at_shutdown —— 这个功能在重启初期会额外吃1~2GB内存,OOM高发期建议关掉
额外但关键的一环:别忘了systemd和cgroup
宝塔、Docker、systemd service 都可能自带内存限制,光改my.cnf没用:
- 查 systemd 限制:systemctl show mysqld | grep Memory,若看到 MemoryLimit=xxx,说明被硬限了
- Docker用户必须加:--memory=4g --memory-reservation=3g,不能只靠MySQL自己节制
- 给 mysqld 进程降 OOM 优先级:echo -900 > /proc/$(pidof mysqld)/oom_score_adj(临时),或在 systemd service 文件里加
OOMScoreAdjust=-900











