引言:一个令人惊醒的噩梦
有这么一句话是这么讲的,容器当中最为奇特的漏洞,通常来自你最为信赖的系统。8G的容器运行着MySQL,MySQL的RSS猛涨到了7.9G,重启有效果但是3小时后又再次出现——这就是那种能让人在半夜,吓得冷汗直冒惊醒的噩梦。
问题的扎心之处在于:一切看起来都很正常,却活生生地在吃你的内存。
现象:诡异的“幽灵”内存贼
容器分配8G,MySQL进程RSS占用7.9G,系统告急。
重启一下,内存瞬间掉下来,但仨小时后又开始蹦跶,周而复始。
这不是偶发,这是规律性的内存缓慢泄漏,最后的结局就是OOM Kill。好家伙,这不就是标准的“死亡漩涡”吗?一开始没人在意,等到容器崩溃,大家才开始挠头。
定位:Buffer Pool只有4G,5G去哪了?
当运用 SHOW ENGINE INNODB STATUS 来查看时,让人颇为惊讶,发现InnoDB Buffer Pool居然只有4G,可配置里明明写的是更大的,那余下的2-3G内存到底被谁占用了。
这时候就得祭出终极大招:查看memory_summary_global_by_event_name,看看各个模块到底占了多少。
一查不要紧,前三名全是error_log相关的条目。
这一刻,元凶浮出水面。
根因:性能观测表的“黑吃黑”
原本是MySQL 8.0所引入的新特性performance_schema.error_log表,它专门用来记录错误日志。这个物件配置得比较激进,记录级别是ALL再加上高频警告——换个说法,只要MySQL有任何动静,都会把记录内容存储到这张表格之中。
随着时光流逝,这个表就好像一个无底洞一样,不断地吞食内存。更叫人苦恼的是,这些数据并不会自己清理,就那样待在内存里变成僵尸数据。
容器场景下这个问题尤其致命,因为你根本看不到硬盘,所有东西都吃内存。
验证:杀人证据确凿
执行这条SQL:
SELECT * FROM performance_schema.memory_summary_global_by_event_name
ORDER BY CURRENT_NUMBER_OF_BYTES_USED DESC LIMIT 10;
结果已经出来,error_log相关的内存,占用排在前三位,证据都明明白白的,不用再去找寻——这就好像监控摄像头直接拍到了元凶作案的过程似的。
解决:一条命令,2G内存瞬间回落
SET GLOBAL log_error_verbosity=2;
这一条把日志记录级别从ALL降到WARNING以上,一瞬间收获医生“救命”般的快乐。
再跑一条:
TRUNCATE performance_schema.error_log;
把历史数据全清空。
效果?内存从7.9G直接跳水到5.9G,简直是见鬼了。
这就是为什么重启管用——重启后这张表也被清空了。
预防:一行配置省30%内存
根本不想再经历这噩梦?把这行写进my.cnf就完事了:
[mysqld]
performance_schema_consumer_events_errors_log = OFF
或者更直接的做法:
[mysqld]
performance_schema = OFF
对于容器场景,这能直接省掉30%左右的内存占用。
不需要性能观测?关掉它。
需要?那就想办法把error_log的记录级别调低,在需要排查问题的时候调高记录级别。
这个坑的启示很简单:监控工具本身也会变成性能杀手。新特性默认启用是为了方便,但在资源受限的容器里,这就是个定时炸弹。
定期对performance_schema的内存占用进行检查,降低log_error的清晰程度,关闭不需要的消费者,这三个办法就能够让你放心了,再也不要在半夜为MySQL OOM担心得冒汗了。
声明
声明,此文章中九成是我本人原创,仅有极少量素材借助AI来处理,并且所有内容我都已仔细检查,相关案例依照MySQL官方文档以及真实线上场景而来,该文章旨在传播正能量,不存在低俗不良引导,期望读者可以理解。