必须先确认是否真被OOM killer杀:运行dmesg -T | grep -i "killed process",输出含"Out of memory: Kill process mysqld"且时间戳匹配;docker inspect返回OOMKilled=true且退出码137;三者缺一不可。

必须先确认是不是真被 OOM killer 杀的,不是所有 mysqld 崩溃都算内存泄漏。很多情况是配置错、SQL 写崩、磁盘满或信号中断,直接调 innodb_buffer_pool_size 只会掩盖真凶。
怎么一眼断定是 OOM killer 干的
别信日志里“MySQL crashed”这种模糊描述,只看三处硬证据:
- 运行
dmesg -T | grep -i "killed process",输出含Out of memory: Kill process mysqld才算坐实 - 检查
docker inspect <container_id> --format '{{.State.OOMKilled}}',返回true且退出码为137是容器场景铁证 - 比对时间戳:
systemctl status mysqld显示的宕机时间,和dmesg里那行 kill 时间差不能超 60 秒;否则大概率是其他原因(比如备份脚本把磁盘写满了)
为什么 performance_schema 是查内存泄漏的第一现场
配置参数只是静态上限,真正泄漏发生在运行时——某个线程反复 malloc 却不 free,performance_schema 能抓到这种“卡住”的内存块。
- 查全局内存分布:执行
SELECT SUBSTRING_INDEX(event_name,'/',2) AS code_area, SUM(CURRENT_NUMBER_OF_BYTES_USED) AS used FROM performance_schema.memory_summary_global_by_event_name GROUP BY code_area ORDER BY used DESC;,重点关注innodb、sql、memory/heap三块是否持续上涨 - 定位具体线程:用
SELECT thread_id, event_name, SUM_NUMBER_OF_BYTES_ALLOC FROM performance_schema.memory_summary_by_thread_by_event_name ORDER BY SUM_NUMBER_OF_BYTES_ALLOC DESC LIMIT 10;,如果看到非后台线程(thread_id > 100)长期霸占memory/innodb/mem0mem或sql/sp_head::main_mem_root,基本就是 SQL 或存储过程在 leak - 注意陷阱:
performance_schema自身也吃内存,若设了memory/% = COUNTED,单项就能占几 GB;生产环境建议只开memory/sql/% = COUNTED
哪些 SQL 行为会伪装成“内存泄漏”
MySQL 本身极少真 leak,多数是语义误用导致内存被长期占用不释放,比如:
- 字符串字面量触发隐式类型转换:
WHERE expire_time = 'infinity'这种值会让优化器放弃索引,全表扫描+聚合 → 临时表暴涨,tmp_table_size和sort_buffer_size全部叠加上去 - 大结果集未分页还带
ORDER BY+LIMIT:MySQL 5.7+ 会先排序全部数据再截断,sort_buffer_size不够就落盘,但落盘前仍要申请内存 - 大量短连接执行含子查询的视图:每个连接都会复制一份
sp_head::main_mem_root,max_connections=1000时可能多占 1–2GB
调参不是万能解,但必须守住底线
就算没找到 leak 点,也要立刻压住内存膨胀空间,防止反复被杀:
-
innodb_buffer_pool_size必须基于MemAvailable(不是MemTotal)来设:运行cat /proc/meminfo | grep MemAvailable,取值 ≤ 70% 的MemAvailable,并预留至少 2GB 给 OS 和监控 agent - 连接级缓冲必须收窄:
sort_buffer_size、join_buffer_size建议统一设为524288(512KB),tmp_table_size和max_heap_table_size设为67108864(64MB) - 高并发下禁用
table_open_cache_instances:设为1,避免每个 instance 都缓存一份表结构,尤其当有大量触发器时
最常被忽略的是:OOM killer 杀进程不看 RSS 是否超标,而看 oom_score —— 它优先选“内存占用大 + 子进程多 + 非系统进程”的组合。mysqld 天然符合这三条,所以哪怕你只漏了一个 GROUP BY 查询,它也可能成为那个被点名的倒霉蛋。


















