内存只涨不落|拆到组件才发现占的是会话和临时表

简介: 从应用报 OOM、调小 Buffer Pool 没用切入,把 MySQL 内存分五层,用 performance_schema 逐层定位,讲清真不释放与高水位不回收两类根因,给出止血与根治。

大家好,我是数据库小学妹👋 我踩过的坑,你别再踩。

有一次线上,应用一直报 OOM,监控上数据库的内存曲线只涨不落。我第一反应是 Buffer Pool 出了问题,就去把它调小了。结果内存没降下来,查询反而更慢。折腾半天才搞明白,涨上去的那块,根本不在 Buffer Pool。

那次之后我记牢一件事。内存打满,先别急着调池子,得先看清是谁在占。数据库的内存不是一个数,是好几层叠起来的。你把它当一个总量去调,多半调错地方。

内存被谁占了

要看清是谁在占,先得知道内存分几层。MySQL 的内存,粗略分五层。Buffer Pool 是最大的一块,但常常不是涨的那块。

层 谁在占 会不会只涨不落
Buffer Pool 数据页、索引页、undo、change buffer、自适应哈希 会,但它是缓存,高是好事
每连接缓冲 read / sort / join buffer、net buffer 会话活着就一直占着
内存临时表 GROUP BY、子查询物化 并发一高就暴涨
元数据/其他 table_open_cache、log buffer、key_buffer 一般不主导
性能库 performance_schema 自身 固定开销,可观

先记住一句话。Buffer Pool 高占用,是缓存在命中,是数据库在好好干活。它不该是你排查的第一嫌疑,除非它被设得超过物理内存。

除了 Buffer Pool,还有一层容易被忽略,就是每连接缓冲。它按会话分配,随连接数线性涨,而且上限能算出来。

SHOW VARIABLES WHERE Variable_name IN (
  'read_buffer_size','read_rnd_buffer_size','sort_buffer_size',
  'join_buffer_size','binlog_cache_size','max_allowed_packet','thread_stack');

把这几项加一加,再乘上连接数,就是这一层的潜在上限。注意是「潜在」。这些缓冲按需分配,平时用不满。但要留个心眼,sort_buffer_size 和 join_buffer_size 是每次排序、每次连接操作各算一份。一个会话连跑几条大排序,累起来很可观。

逐层定位:先看组件,再看进程

分完层,接下来的问题是:每一层怎么查。以前我只会看 top,看到 mysqld 的 RES 高就慌。后来才知道,那里面混着 page cache 和 malloc 碎片,水分很大。现在我先看数据库自己报的账。

MySQL 5.7 起,performance_schema 会把内存按组件记下来。谁占得多,一眼可查。

-- 按组件看谁在占内存(换算成 MB)
SELECT EVENT_NAME,
       CURRENT_NUMBER_OF_BYTES_USED/1024/1024 AS mb
FROM performance_schema.memory_summary_global_by_event_name
ORDER BY CURRENT_NUMBER_OF_BYTES_USED DESC
LIMIT 15;

8.0 还有更顺手的 sys 库视图,省得自己换算。

-- sys 库:全局内存大户
SELECT * FROM sys.memory_global_by_current_bytes LIMIT 10;
-- 按线程看:哪类会话在占
SELECT * FROM sys.memory_by_thread_by_current_bytes
ORDER BY current_allocated DESC LIMIT 10;

Buffer Pool 单独确认一下。别光看它大不大,要看命中率。

SELECT @@innodb_buffer_pool_size/1024/1024 AS bp_mb,
       @@innodb_buffer_pool_instances      AS instances;
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_%';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';

命中率大概这么算:1 减去 Innodb_buffer_pool_reads 除以 Innodb_buffer_pool_read_requests。命中率高,说明池子够用,别再动它。命中率低还硬调小,只会更慢。

临时表这层最容易漏,也最容易爆。

SHOW GLOBAL STATUS LIKE 'Created_tmp%';   -- 看落盘比例
SHOW VARIABLES LIKE 'tmp_table_size';
SHOW VARIABLES LIKE 'max_heap_table_size';

内存临时表的实际上限,是 tmp_table_size 和 max_heap_table_size 里小的那个。它一旦被撑爆,临时表就落到磁盘,Created_tmp_disk_tables 开始涨。这既是一次内存事件,也是一次 IO 事件。

注意版本。上面这套是 5.7 及以前 MEMORY 引擎的逻辑。8.0 换成了 TempTable 引擎,旋钮也跟着变。

SHOW VARIABLES LIKE 'internal_tmp_mem_storage_engine';  -- TempTable 还是 MEMORY
SHOW VARIABLES LIKE 'temptable_max_ram';                -- 内存临时表上限
SHOW VARIABLES LIKE 'temptable_max_mmap';               -- 超过后 mmap 的上限

8.0 下,内存临时表涨过 temptable_max_ram,不会直接落盘,而是先尝试 mmap 文件,涨到 temptable_max_mmap 才真正走磁盘。所以 8.0 还照 5.7 的 tmp_table_size 去调,是调不准的。

数据库自己报完账,还得回到机器上对一遍。两边对得上,才算确认。

top -p $(pgrep -x mysqld)      # 只看 RES,别被 VIRT 吓到
cat /proc/$(pgrep -x mysqld)/status | grep VmRSS
pmap -x $(pgrep -x mysqld) | tail -1

两类根因,别混为一谈

定位到最后你会发现,涨上去的内存无非两类:真不释放,和高水位不回收。

真不释放,常见两个来源。一是长驻会话。连接池把连接一直留着,每条会话的 sort、read 缓冲用过高水位,就一直占着不放。max_connections 越大,这块上限越高。二是内存临时表并发暴涨。参数设得太大,几十个并发一起建大临时表,内存瞬间见底。

高水位不回收,更多是机制,不是 bug。Buffer Pool 一旦涨上去,不会主动缩;OS 的 page cache 也会把一份页缓在内存里,等于和 Buffer Pool 存了两份。这不是泄漏,是设计。

那份重复的 page cache,其实能省。把 innodb_flush_method 设成 O_DIRECT,InnoDB 读写就绕过 OS 的页缓存,只在 Buffer Pool 里存一份。省下的内存,反哺给 Buffer Pool 正合适。

还有一层不在数据库里,在进程自己身上。Linux 的 glibc 给每个线程一个内存 arena,最多开到 8 倍核数。线程用完的内存,退给 arena,却不退给操作系统。这就是为什么数据库里查不到它,top 的 RES 却下不来。压一压用 MALLOC_ARENA_MAX,或者干脆换 jemalloc。

分清这两类,处置完全不同。前者要抓出来释放,后者根本不用管。

先止血,再根治

分清了是哪一类,处置就有方向了。线上正在 OOM 时,先止痛,再治病。第一步,把危险的会话摘掉。

-- 找出跑太久、耗资源的会话
SELECT id, user, time, state, LEFT(info, 60) AS sql_head
FROM information_schema.processlist
WHERE command <> 'Sleep' AND time > 60
ORDER BY time DESC;
-- 确认后用 KILL 摘掉

第二步,临时表该落盘就落盘。把 tmp_table_size 调小,让大临时表走磁盘,用 IO 换内存。这是权宜,不是答案。第三步,限制并发和入参,max_allowed_packet 这类别设得过大。

止血之后,再看根治。参数上,Buffer Pool 按物理内存给,专用库一般占五到七成,别超过内存总量。每连接的缓冲参数,除非有实据,尽量用默认。把 sort_buffer_size 设成几个 G 是大忌,它是每个会话、每次排序都各算一份。

治理上,连接池卡上限、空闲连接及时回收。监控上,把进程 RSS、Buffer Pool 用量、连接数、临时表落盘率这几条线立起来,OOM 之前先报警。止损靠临时手段,根治靠参数、连接和监控一起收口。

判断力

内存这事,最忌讳只看一个总量就下手。很多人一看到使用率到九成就慌,其实该先问一句:涨上去的那块,创造价值了没有。

Buffer Pool 高,是缓存在干活,那是好内存,不该动。真正要盯的,是只涨不落、又不产出缓存收益的那部分。长驻会话攥着的连接缓冲,堆在高并发里的临时表,还有借了不还的内存碎片,它们才是真正被吃掉的。

所以顺序永远是先分层、再定位,最后才谈收缩还是释放。先看清是谁在占,再决定动谁。顺序反了,你动的往往是最不该动的那层。

避坑清单

连接缓冲是按连接数线性涨的,别只盯 Buffer Pool。max_connections 调大一时爽,内存上限跟着水涨船高。

临时表超限会落盘,它既是内存问题,也是 IO 问题。tmp_table_size 设太大,等于把雷埋进高并发里,平时看不见,一忙就响。

最后一条是我自己踩的。我一开始拿 top 的 RES 当数据库的真实占用,被 page cache 和 malloc 的碎片骗过。RES 高,不等于 InnoDB 真占了那么多。现在我都先用 performance_schema 按组件拆一遍,再去 OS 层对齐。两个数对不上,才知道该往下查哪。

写在最后

数据库内存涨,从来不是一个「调小参数」就能解的问题。它更像在查一笔账:先分层,再定位到具体那层,最后才决定收缩还是释放。

Buffer Pool 是缓存,占得多是好事,不该背锅。真正该抓的,是那些借了不还、只涨不落的部分。会话、临时表、碎片,这三样最像沉默的成本,平时不出声,结账时才知道贵。内存排查的功夫也不在调参,在看清。看清是谁在占,比急着释放更要紧。

你们线上数据库的内存,是涨到某条线就稳,还是一路不回头?用的又是哪套工具定位的?评论区聊聊。

我是数据库小学妹,帮你少走弯路少踩坑,咱们下篇见👋

相关文章
|
24天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
9478 31
|
1天前
|
云安全 人工智能 运维
当攻击全面Agent化,你的安全防御能否跟上AI Speed?
多起客户侧AK泄露事件应急的真实观察
1154 0
|
10天前
|
存储 人工智能 并行计算
大模型本地部署终端选型方法论:以 Qwen3.8-27B 为例的四档分层完整流程
本文提出一套大模型本地部署终端选型方法论:定约束、定档位、定框架、定参数四步决策法,配合入门、主力、质量、无损四档分层模型。以 Qwen3.8-27B 实测数据为例,逐环节解读显存、带宽、存储、散热、系统、预算等要素,给出面向不同预算的优选方案、决策自查清单与市场观察框架。文末前瞻 AI 笔记本的 CPU+GPU 与统一内存两条路线,论证四步决策法在新品类上的延续性。
|
9天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型深度拆解:原理解读、实战测评与保姆级落地教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为**System One Model(系统一模型)**,对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
716 1
|
5天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型全解析:核心原理拆解,性能实战测评与落地部署教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为System One Model(系统一模型),对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
467 0
|
22天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
2545 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
4天前
|
存储 缓存 测试技术
QWT 3D 模块重构记:旧渲染管线改进介绍及实时瀑布图示例
QWT 3D模块完成重大重构:解耦窗口与图形(Plot+Item),摒弃OpenGL 1.x固定管线,迁至Core Profile着色器渲染;支持多图元同场景叠加,并以实时3D瀑布图验证——滑动窗口、固定色域、64帧×256点达61fps。兼容Qt 5.12+/6,LGPL协议。
176 120
|
16天前
|
Windows
【保姆级】.NET Framework 3.5离线安装教程(2026最新实测)
.NET Framework 3.5是微软经典运行库,专为兼容老软件、老游戏设计。Win7自带,Win10/11需手动启用(在线或离线安装)。它与4.x系列并存不冲突,缺它会导致“找不到组件”报错。本文详解两种安装方式及常见问题解决,小白也能轻松上手。(239字)
2076 0