真实内存告警需以free命令的available值为准,若available>20%总内存则属缓存虚高;否则检查mysqld的VmRSS是否远超innodb_buffer_pool_size,并通过performance_schema定位内存消费者。

确认是不是真实内存告警,而不是 Linux 缓存虚高
MySQL 进程 RSS 高 ≠ 系统内存真不够。Linux 的 free 输出里 available 才是真实可用内存,buff/cache 是可回收的文件缓存。如果 available 还剩 >20% 总内存,基本不用管;如果 available Swap used 持续上涨,就得立刻处理。
快速验证是否为缓存:执行 sync; echo 3 > /proc/sys/vm/drop_caches(低峰期操作)。执行后 free -g 显示内存大幅回落 → 属于正常机制,不需调 MySQL;无变化 → 内存被进程真实占用,继续往下查。
有 dmesg | grep -i oom 输出 → 已发生 OOM,mysqld 可能随时被杀,必须立即干预。
定位是不是 mysqld 进程本身在吃内存
用 ps aux --sort=-%mem | head -10 或 top(按大写 M 排序)看内存占比最高的进程名。注意区分:
-
mysqld:MySQL 服务主进程,是排查目标 -
/usr/bin/mysql或mysqldump:客户端工具,属于外部调用,不是 MySQL 服务自身问题
确认是 mysqld 后,查它真实物理内存占用:cat /proc/$(pidof mysqld)/status | grep VmRSS,单位 KB。除以 1024*1024 得到 GB 数,这个值才代表 MySQL 实际占了多少物理内存。
如果该值远高于 innodb_buffer_pool_size 配置值(比如配置 32G,VmRSS 却达 58G),说明还有其他内存消费者,不能只盯着 buffer pool。
查 MySQL 内部谁在吃内存
优先跑这三条 SQL,顺序不能乱:
① 全局最大内存消费者:SELECT event_name, sys.format_bytes(CURRENT_NUMBER_OF_BYTES_USED) FROM performance_schema.memory_summary_global_by_event_name ORDER BY CURRENT_NUMBER_OF_BYTES_USED DESC LIMIT 10;
重点关注 memory/innodb/buf_buf_pool(应占大头)、memory/temptable/physical_ram(临时表爆了)、memory/sql/Query_cache(已弃用但若开启会浪费)。
② 用户级内存大户:SELECT user, host, SUM(current_number_of_bytes_used)/1024/1024 AS MB FROM performance_schema.memory_summary_by_account_by_event_name GROUP BY user, host ORDER BY MB DESC LIMIT 5;
某业务账号持续占 50MB+,大概率存在未关闭游标、长事务或低效批量插入。
③ 当前活跃线程的内存分配:
先 SHOW FULL PROCESSLIST 找出运行时间长、状态为 Sorting result 或 Copying to tmp table 的线程,记下 ID,再查:SELECT * FROM performance_schema.memory_summary_by_thread_by_event_name WHERE THREAD_ID = XXX ORDER BY CURRENT_NUMBER_OF_BYTES_USED DESC LIMIT 5;
⚠️ 注意:performance_schema 默认可能没开全内存 instrument,比如 memory/sql/TABLE,需提前确认:SELECT * FROM performance_schema.setup_instruments WHERE NAME LIKE 'memory%';,把对应项设为 ENABLED = YES。
重点盯死几个关键参数和行为
这些是生产环境最常踩坑的地方:
-
innodb_buffer_pool_size:专用服务器别超总内存 70%,混部机器别超 40%。64G 机器配 50G 就很危险,尤其加上连接内存和临时表后极易 OOM -
tmp_table_size和max_heap_table_size:必须设成一样,建议 256M–1G。16M 太小,复杂GROUP BY或JOIN会直接落盘,反而更耗资源 -
max_connections:设 5000 但实际峰值只有 30,每个连接仍预分配sort_buffer_size、join_buffer_size,隐性浪费严重。按峰值 ×1.5 设即可 - 多语句(multiple statements)或超长 bulk insert:一条网络包带几百个
INSERT或几万行数据,SQL 解析阶段就会吃掉大量内存,且不走performance_schema统计路径,容易漏掉 - THP(透明大页):
cat /sys/kernel/mm/transparent_hugepage/enabled显示[always]是高危信号,会加剧内存碎片,建议关掉
真正难搞的是那些不进 performance_schema 统计的内存,比如 glibc malloc arena 分配、JIT 解析缓存、Group Replication 的 XCOM cache —— 它们不会出现在 SQL 查询结果里,但加起来可能占十几个 GB,得结合 /proc/pid/status 和 pstack 进一步深挖。


















