
凌晨 4 点,监控报警:MySQL 实例宕机。
查看 Linux 系统日志 (dmesg / /var/log/messages),赫然写着:
Out of memory: Kill process 10086 (mysqld) score 950 or sacrifice child。
你的困惑:
服务器物理内存 32G。
你明明把 innodb_buffer_pool_size 设置为了 20G。
理论上系统还有 12G 给 OS 和其他进程,怎么可能不够用?
到底是谁偷走了那 10G+ 的内存?
破案工具:
只要 Performance Schema 是开启的(默认就是),MySQL 早就把每一笔内存开销都记在账上了。
你只需要查询 sys.memory_global_by_current_bytes。
1. 核心原理:MySQL 的内存账本
MySQL 的内存分配主要分为两大部分:
sys.memory_global_by_current_bytes 视图基于底层的 performance_schema,将这些复杂的内存分配事件(Event)聚合、排序,并转换成人类能读懂的 KiB, MiB, GiB 格式。
2. 实战演练:谁是内存第一大户?
查询语句:
SELECT
event_name,
current_alloc,
current_avg_alloc,
high_alloc
FROM sys.memory_global_by_current_bytes
LIMIT 10;
典型输出解析:
| memory/innodb/buffer_pool_chunk | 20.00 GiB | 128.00 MiB | 20.00 GiB |
| memory/innodb/hash_index | 2.50 GiB | … | 3.00 GiB |
| memory/temptable/physical_ram | 1.20 GiB | … | 5.00 GiB |
| memory/sql/TABLE_SHARE | 800.00 MiB | … | 900.00 MiB |
| memory/performance_schema/events… | 500.00 MiB | … | 500.00 MiB |
破案线索:
- 这是预期的 20G,正常。
- 意外发现! AHI 竟然占了 2.5G。如果你有很多热点读查询,InnoDB 会自动建立 Hash 索引。
- 对策: 如果内存紧张,可以关闭 innodb_adaptive_hash_index。
- 关键嫌疑人! 当前占了 1.2G,历史最高(high_alloc)甚至达到过 5G!
- 这说明有大量的复杂查询(GROUP BY / DISTINCT)导致 MySQL 在内存中创建了巨大的临时表。这就是导致 OOM 的元凶。
3. 三大实战场景
场景一:临时表风暴 (Temp Table Explosion)
现象: 内存周期性飙升,甚至 OOM。
查询结果: memory/temptable/physical_ram 或 memory/innodb/row_log_buf 排名极高。
原因:
MySQL 8.0 默认使用 TempTable 引擎处理内存临时表。如果 SQL 烂(比如 SELECT * 配合巨大的 GROUP BY),内存会瞬间耗尽。
解决:
- 优化 SQL,减少临时表大小。
- 限制 temptable_max_ram(让它尽早转入磁盘,牺牲性能保命)。
场景二:表元数据缓存过大 (Table Cache Overflow)
现象: 这是一个 SaaS 系统,为每个租户建了 100 张表,总共有 10 万张表。内存居高不下。
查询结果: memory/sql/TABLE_SHARE 和 memory/innodb/table_struct 占用很高。
原因:
打开一张表,MySQL 就需要加载表的元数据(Metadata)到内存。10 万张表会吃掉几个 G 的内存。
解决:
- 调小 table_definition_cache。
- 清理不用的表。
场景三:Performance Schema 自身开销
现象: 这是一个小内存机器(2核4G),P_S 占了 500M+。
查询结果: memory/performance_schema/… 霸榜。
原因:
Performance Schema 是全内存的,开启的监控项越多,吃内存越狠。
解决:
- 在小内存机器上,考虑关闭部分 Performance Schema 监控项,或者完全关闭 P_S。
4. 避坑指南:它不是万能的
这个视图展示的是全局聚合数据。如果是某个特定的连接 Sort Buffer 设得太大(比如 100M),导致并发上来后 OOM,这个视图只能看到 memory/sql/sort_buffer 总量很高,但看不出是哪个线程干的。
- 进阶: 需要结合 sys.memory_by_thread_by_current_bytes 查看。
MySQL 只能监控自己申请的内存。如果是因为 Linux Page Cache 或者其他 Agent 进程导致的 OOM,这里看不出来。
5. 总结
sys.memory_global_by_current_bytes 是 MySQL 的“财务审计报表”。
不要再只盯着 Buffer Pool 看了。当内存不够用时,请第一时间打开这张表,看看是不是 临时表 (TempTable)、自适应哈希 (AHI) 或者 表元数据 (Table Share) 在偷偷掏空你的服务器。
网硕互联帮助中心






评论前必须登录!
注册