云计算百科
云计算领域专业知识百科平台

明明还有 10G 内存,为什么 MySQL 还是被 Linux 杀掉了?

在这里插入图片描述
凌晨 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 的内存分配主要分为两大部分:

  • 全局共享内存 (Global Buffers): 所有线程共享的,如 InnoDB Buffer Pool、Log Buffer、Table Cache。
  • 线程私有内存 (Thread Buffers): 每个连接独享的,如 Sort Buffer、Join Buffer、Binlog Cache。
  • 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;

    典型输出解析:
    event_namecurrent_alloccurrent_avg_allochigh_alloc
    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

    破案线索:

  • memory/innodb/buffer_pool_chunk:
    • 这是预期的 20G,正常。
  • memory/innodb/hash_index (自适应哈希索引 AHI):
    • 意外发现! AHI 竟然占了 2.5G。如果你有很多热点读查询,InnoDB 会自动建立 Hash 索引。
    • 对策: 如果内存紧张,可以关闭 innodb_adaptive_hash_index。
  • memory/temptable/physical_ram (临时表):
    • 关键嫌疑人! 当前占了 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. 避坑指南:它不是万能的

  • Thread Memory 的盲区:
    这个视图展示的是全局聚合数据。如果是某个特定的连接 Sort Buffer 设得太大(比如 100M),导致并发上来后 OOM,这个视图只能看到 memory/sql/sort_buffer 总量很高,但看不出是哪个线程干的。
    • 进阶: 需要结合 sys.memory_by_thread_by_current_bytes 查看。
  • OS 层的开销:
    MySQL 只能监控自己申请的内存。如果是因为 Linux Page Cache 或者其他 Agent 进程导致的 OOM,这里看不出来。

  • 5. 总结

    sys.memory_global_by_current_bytes 是 MySQL 的“财务审计报表”。

    不要再只盯着 Buffer Pool 看了。当内存不够用时,请第一时间打开这张表,看看是不是 临时表 (TempTable)、自适应哈希 (AHI) 或者 表元数据 (Table Share) 在偷偷掏空你的服务器。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 明明还有 10G 内存,为什么 MySQL 还是被 Linux 杀掉了?
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!