MySQL容器OOM排查记:一个infinity参数打穿LIMIT上限

MySQL容器OOMLIMIT
于 2026-10-02 07:30:21 修改
·本内容遵循CC 4.0 BY-SA版权协议

如果你在麒麟 V10 上用容器跑 MySQL,某天凌晨突然收到数据库连接数掉零的告警,上去一看容器已经 Exited,重启后以为没事,结果第二天同样时间点又崩一次——那大概率跟这次的问题一样:不是磁盘满,不是主从断裂,而是一行看起来很不起眼的 infinity 参数,最终被拼接成了 LIMIT 18446744073709551615,把 MySQL 的排序内存和临时表空间直接打穿,容器触发 OOM,mysqld 被内核干掉。下面把这起排查实录整理出来,给碰过类似问题或者准备在国产化环境部署 MySQL 容器的人一个参考。

1. 问题现场:容器重启与 MySQL 连接全断

1.1 现象描述

告警时间是在夜里 1 点 40 分左右,监控页面显示 mysql 容器状态从 running 变成了 exited,业务方反馈基础服务里所有需要查库的接口全部 5xx。我登录服务器后第一眼看到的并不是什么异常,宿主机负载不高,磁盘使用率也不到 60%,free -h 显示物理内存还剩 10 多个 G。这时候我下意识以为是不是有人误操作停容器,于是 docker ps -a 一看,容器退出码是 137,不是正常的 0。

退出码 137 是典型的被强制终止信号(SIGKILL),在容器环境里最常见的就是 OOM Killer。不过当时我还是先把容器拉起来恢复业务,docker start mysql 后等了大概两分钟,MySQL 起来,接口陆续恢复。这种做法只保住了眼前服务,根因并没有排除,所以第二天凌晨同一个时间点,告警又来了。

第二次我学乖了,先从宿主机把现场资料保留下来,再决定要不要重启。这里有个很关键的习惯:不要急着删容器,也不要急着 docker restart,先看一眼退出码、系统日志、容器内存统计,这些是排查 OOM 的第一手证据。如果你直接 restart,很多内核层的输出可能就被后续日志冲掉了。

1.2 第一反应:先恢复服务,再查根因

对于线上数据库这类核心组件,先把业务拉起来没有错,但必须边恢复边留证据。我当时按这个顺序操作:

BASH
# 1. 查看容器退出状态
docker inspect mysql --format '{{.State.ExitCode}} {{.State.OOMKilled}}'
 
# 2. 查看宿主机内核 OOM 日志
dmesg -T | grep -i -E "oom|killed" | tail -50
 
# 3. 查看系统日志里的内存和 OOM 相关记录
journalctl -k --since "1 hour ago" | grep -i -E "oom|killed"

docker inspect 输出里 OOMKilled 字段如果是 true,说明容器确实是被 cgroup 内存限制杀掉。当时这个字段返回的正是 true,同时 dmesg 日志里也能看到类似 Out of memory: Killed process 25314 (mysqld) 的信息。所以基本可以确定是容器内内存耗尽,接下来要回答的问题是:一个正常的 MySQL 实例,内存为什么会突然涨到容器上限?

这一阶段的时间线我也简单梳理了下,方便后续复盘:

时间 现象 初步判断
01:40 容器状态 exited,退出码 137 疑似 OOM 强杀
01:41 业务接口大量 5xx,数据库连接超时 依赖 MySQL 的服务全部受影响
01:50 手动启动容器,服务恢复 属于治标,未定位根因
次日 01:45 同样问题再次出现 说明有固定触发源,不是偶发

2. 排查路径:宿主机、容器、MySQL 三层逐步收网

2.1 宿主机 OOM 日志:关键字是 MySQL

拿到 OOMKilled=true 之后,第一件事是看宿主机内核日志。在麒麟 V10 这种基于 4.19 内核的系统里,OOM 事件通常会同时出现在 dmesg 和 /var/log/messages 中。我执行了:

BASH
dmesg -T | grep -i -E "out of memory|killed process"

日志里能看到很关键的一行:

TEXT
[Thu Mar xx 01:40:xx 2025] mysql invoked oom-killer: gfp_mask=0x...
[Thu Mar xx 01:40:xx 2025] Out of memory: Killed process 25314 (mysqld) total-vm:9234567kB, anon-rss:4023044kB, file-rss:0kB, shmem-rss:0kB

这里要注意,total-vm 是虚拟内存大小,不能直接当成实际物理内存占用;真正决定 OOM 的是 anon-rss,也就是匿名页的常驻内存,显示约 4GB。同时我看了宿主机 free -h,内存明明还有剩余,这就说明问题并不是宿主机物理内存不足,而是容器 cgroup 限制住了内存。

再往下翻日志,能看到 OOM Killer 遍历了 cgroup 里的进程,最后选择了 mysqld 下手。原因很简单:在同一个 memory cgroup 里,mysqld 的内存占用最大、增长最快,内核认为杀掉它能最快释放内存。

这一步也顺带确认了宿主机本身没有内存泄漏,也不存在其他进程抢占内存的问题。

2.2 容器内存统计:cgroup 限制被击穿

麒麟 V10 上的 Docker 默认使用 cgroup v1,容器的内存控制文件在 /sys/fs/cgroup/memory/docker/<容器ID>/ 下。我用以下命令查看:

BASH
# 容器完整 ID 可以通过 docker inspect 拿到
CONTAINER_ID=$(docker inspect mysql --format '{{.Id}}')
cat /sys/fs/cgroup/memory/docker/$CONTAINER_ID/memory.limit_in_bytes
cat /sys/fs/cgroup/memory/docker/$CONTAINER_ID/memory.usage_in_bytes
cat /sys/fs/cgroup/memory/docker/$CONTAINER_ID/memory.oom_control

结果如下:

  • memory.limit_in_bytes 是 4294967296,也就是 4GB;
  • memory.usage_in_bytes 在故障发生后回落到 1GB 左右,但日志里记录的峰值已经超过 4.3GB;
  • memory.oom_control 显示 oom_kill_disable 0,说明容器的 OOM Killer 没有被禁用。

这里顺带说一句,如果容器里跑的是 Java 或 Go 服务,大家往往更关注堆内存和 GC;但 MySQL 是 C++ 写的服务,内存模型更直接,大量内存来自 InnoDB Buffer Pool、排序缓冲区、临时表、线程栈和各类内部缓存。一旦某个查询需要处理的数据量远超预期,内存会以“肉眼可见”的速度涨上去。

我还打开了 Docker 的事件流,后面如果再复现,docker events 会输出容器被 kill 的事件记录,可以辅助确认精确时间点。

2.3 MySQL 日志与慢查询:定位到那一行 infinity

容器日志里其实已经露出了马脚:

BASH
docker logs mysql --since "2 hours ago" 2>&1 | grep -i -E "error|memory|killed" | tail -100

能看到类似这样的报错:

TEXT
[ERROR] [MY-000000] [Server] Out of memory (Needed 16777216 bytes)
[ERROR] [MY-010584] [Server] Error writing file '/tmp/xxx' (OS errno 28 - No space left on device)

第一眼看到 No space left on device 时我差点被带偏,以为是 /tmp 满了。后来查了磁盘和 tmpfs 挂载,发现磁盘剩余空间还有很多,其实是 MySQL 在创建临时文件时因为内存或文件系统限制触发了写入失败。这个现象在内存排序过大、需要大量落盘时经常出现。

真正定位到 SQL,靠的是慢查询日志和 PROCESSLIST 快照。我提前开启了慢查询日志:

SQL
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 5;
SET GLOBAL log_queries_not_using_indexes = ON;

然后在日志里看到一条语句:

SQL
SELECT id, user_id, amount, create_time
FROM trade_order
WHERE status = 1
ORDER BY create_time DESC
LIMIT 18446744073709551615;

18446744073709551615 是 MySQL 里 LIMIT 子句能接受的最大无符号整数。MySQL 文档里明确说,如果 LIMIT 写成这个值,等价于“不限制行数”。我当时就问了下业务同学,他们第一反应是“我们从来没传过这种数啊”,后来查应用代码才发现,某个内部管理后台的任务扫描逻辑里,有人写了一个“不限量”的配置项,代码里直接用 float("inf") 表示,ORM 在拼接 SQL 时把 inf 兜底转换成了这个最大值。

这条 SQL 本身的执行计划也很糟糕。通过 EXPLAIN 看到:

  • type 是 ALL,走的全表扫描;
  • key 为空,没有使用任何索引;
  • Extra 里有 Using filesort,意味着 ORDER BY create_time DESC 需要额外排序;
  • rows 估算到了千万级别。

于是问题脉络清晰了:一个要返回“所有行”的排序查询,加上一张快千万行的大表,直接把 MySQL 的内存吃穿。

3. 一行 infinity 为什么能把内存吃光

3.1 罪魁祸首:被解释成无限 LIMIT 的分页参数

很多人看到 LIMIT 18446744073709551615 会觉得很夸张,但其实不少 ORM 框架在“不限制返回条数”的场景下都会生成类似的 SQL。比如 Python 的 SQLAlchemy 里,如果你把 .limit(None) 传给查询,它一般不会拼 LIMIT 子句;但如果你给了一个特别大的数,比如 float("inf") 转换后变成字符串 "inf",某些驱动会报错,而另一些驱动或框架会帮你做一次“安全转换”,直接把非数字值替换成无符号整型最大值。

业内更常见的坑是前端分页组件传入 -1、0 或一个超大的 999999999999,后端没有对分页参数做边界校验,最终拼进 SQL。这次的问题本质是一样的:一个“无穷大”的业务语义,在数据库里就是一次全量扫描和全量排序。

ORDER BY 加上超大 LIMIT 的杀伤力在于:优化器知道你要返回所有排序后的行,它不会走“优先队列排序只保留前 N 条”的优化路径,而是老老实实把所有满足条件的行读出来,排序,再返回。如果表中数据量不大,这倒也能扛住;可一旦达到千万行级别,排序缓冲就会频繁溢出到临时表,而临时表如果一开始还能放在内存,后续数据量继续膨胀,就会在内存和磁盘之间反复折腾,内存峰值也随之而来。

3.2 MySQL 内存模型:排序缓冲、Join Buffer 和临时表

要理解这次 OOM,必须简单过一遍 MySQL 的内存结构。MySQL 的内存基本分两类:

一类是全局的,比如 innodb_buffer_pool_size,它负责缓存 InnoDB 的数据页和索引页。另一类是会话级或操作级的,包括 sort_buffer_size、join_buffer_size、read_buffer_size、tmp_table_size、`max_heap_table_size``,这些内存都是按连接或按查询临时分配的。

打个比方:InnoDB Buffer Pool 是仓库,平时把常用的货(数据页)放在仓库里;sort_buffer_size 是分拣台,每个需要排序的连接都会申请一块分拣台空间;临时表是临时货架,放中间结果。正常情况下,分拣台只处理一小批货,货架满了就往磁盘搬。可当查询要求返回“所有行”时,问题就变成了:所有货都要过一遍分拣台,而且临时货架要能容纳全部中间结果。

当时容器限制是 4GB,InnoDB Buffer Pool 虽然只占 256MB,但故障时刻活跃连接里可能有多个连接同时在执行类似的大排序查询。每个连接的 sort_buffer_size 默认 256KB 听起来不大,如果同时有几百个连接在跑,这个量就上来了;更关键的是内部临时表,MySQL 默认内存临时表超过 tmp_table_size 后会转成磁盘临时表,理论上不会一直涨内存,但大量连接同时创建大临时表时,内存的瞬时需求和内存碎片会叠加。

另外,这条 SQL 还带了 WHERE status = 1,如果 status 列上选择性差,MySQL 会将大量符合条件的行都读出来送入排序。也就是说,存储引擎层、Server 层的排序和临时表,再加上连接缓冲,几股流量汇在一起,很快就撞上 4GB 上限。

3.3 容器与系统内存分配:为什么 OOM 杀的是 mysqld

很多人会有疑问:明明 MySQL 有 tmp_table_size 和 sort_buffer_size 限制,临时表也可以落盘,为什么还会 OOM?

这是因为 MySQL 的内存参数很多是“单次分配”的上限,而不是“整个进程”的总预算。当一条 SQL 需要处理千万行时,排序缓冲会被反复填满、刷出,内部临时表也会根据数据量增长;更麻烦的是,如果排序过程中数据量估算出错,MySQL 会在内存里创建行数很多的临时表结构,直到达到 tmp_table_size 上限才转为磁盘表。这个过程中如果同时有多个查询并发,每个查询都在各自的线程里吃内存,进程总内存自然就会飙升。

而容器通过 cgroup 给进程组设定了一个硬上限,当 cgroup 内内存使用量超过 memory.limit_in_bytes,内核会调用 cgroup OOM Killer,从该 cgroup 内选择一个 oom_score 最高的进程杀掉。mysqld 因为是内存占用最大的进程,成了第一目标。这里有个反直觉的点:如果你把容器的 --oom-kill-disable 设为 true,进程可能不会被杀,但容器会一直卡在内存分配失败的状态,甚至把宿主机拖死。 所以正常情况下不要对数据库容器禁用 OOM Killer,除非你有非常完善的进程级守护方案。

在麒麟 V10 上,还有一个容易踩的坑:系统默认的 vm.overcommit_memory=0,也就是内核允许一定程度的 overcommit,MySQL 申请内存时可能不会立即失败,而是先拿到虚拟内存,真正写数据触发缺页时才分配物理内存,这会让 OOM 到来得更突然。排查时可以用 cat /proc/sys/vm/overcommit_memory 确认。

4. 修复方案与复盘:从参数校验到监控告警

4.1 应用侧:禁止 infinity 进入 SQL

根因虽然定位在 SQL,但问题的源头在应用层。第一道防线必须是参数校验,绝对不能把 float("inf")、"Infinity"、nan、负数或超过业务阈值的值传给数据库。

我建议在服务和 DAO 层各加一层限制。DAO 层的例子如下:

PYTHON
MAX_PAGE_SIZE = 1000
 
def safe_limit(raw_limit):
try:
limit = int(raw_limit)
except (TypeError, ValueError):
raise ValueError("invalid limit value")
if limit <= 0 or limit > MAX_PAGE_SIZE:
raise ValueError("limit out of range")
return limit

如果是 ORDER BY 类查询,必须要求调用方显式传入 limit,不允许默认 None 或 inf。对于真正需要全量导出的场景,建议改成游标分批拉取,或者直接用消息队列把任务拆成多个小批次,每次处理几千条。这里不是性能洁癖,而是为了避免“一个查询把所有数据捞出来”这种操作模式出现在生产环境。

另外,可以在网关或 SQL 审计层面对关键字做拦截。MySQL 里 LIMIT 18446744073709551615 这种特征非常明显,直接用字符串匹配就能识别;更完整的做法是把慢查询日志接入日志分析系统,出现类似 SQL 时实时告警。

4.2 数据库侧:优化 SQL 与内存参数

应用层修复后,数据库侧也不能放任不管。首先给这条 SQL 涉及的表补上索引:

SQL
ALTER TABLE trade_order ADD INDEX idx_status_create_time (status, create_time);

执行计划里 WHERE status = 1 ORDER BY create_time DESC 就能用上联合索引,既过滤 status,又通过索引的有序性避免 filesort。这条 SQL 之后即使 LIMIT 被错误放大,也不会因为排序吃掉大量内存,因为有索引后只需要按索引顺序扫描,读多少返多少。

内存参数方面,我做了如下调整,原则是“给单查询降额、给整体限流”:

INI
[mysqld]
# 全局缓冲,根据容器总内存 4G 设置
innodb_buffer_pool_size = 1G
 
# 会话级排序和临时表,限制单查询内存消耗
sort_buffer_size = 2M
join_buffer_size = 2M
read_buffer_size = 1M
read_rnd_buffer_size = 1M
 
# 临时表超过 64M 立即转磁盘
tmp_table_size = 64M
max_heap_table_size = 64M
 
# 最大连接数限制,防止内存被连接数拖垮
max_connections = 300

这里要说明一下,sort_buffer_size 等参数不是越小越好。调小后,如果业务里确实有大排序,MySQL 会更多地使用磁盘临时文件,单查询耗时变长,但至少不会把内存瞬间打爆。对于有 DBA 团队的环境,一定要基于压测结果调参,不能直接拷贝别人的配置。

4.3 容器侧:给 MySQL 设置合理的内存上限

在容器化部署 MySQL 时,容器的内存上限必须小于宿主机可用内存,同时要比 MySQL 预期的峰值留出余量。我这次的容器参数是:

BASH
docker run -d \
--name mysql \
-m 4g \
--memory-swap 4g \
-e MYSQL_ROOT_PASSWORD=xxx \
mysql:8.0

--memory-swap 4g 表示容器最多使用 4GB 物理内存,不允许多余部分落到 swap。如果只设 -m 4g 而不设 --memory-swap,Docker 默认会创建同等大小的 swap,也就是容器最多能吃 8GB,这会让 MySQL 在内存不足时“感觉还能撑”,实际上更危险。

后来又加上了 --pids-limit 和只读根文件系统等安全加固,但这些属于锦上添花,最关键的还是内存限制不能与数据库自身的 buffer pool 配置冲突。如果你给 InnoDB Buffer Pool 配了 3G,容器上限却只有 4G,那别的会话缓冲稍微一涨,容器必然 OOM。

另外,如果使用 Docker Compose,可以在 docker-compose.yml 里这样写:

YAML
services:
mysql:
image: mysql:8.0
mem_limit: 4g
memswap_limit: 4g
restart: unless-stopped

4.4 监控与压测:避免再次踩坑

这次故障之后,我把 MySQL 容器和实例的监控补全了。采集指标主要分三层:

  • 容器层:docker stats、cAdvisor 采集的内存使用量、内存 limit 的百分比;
  • 操作系统层:宿主机 dmesg 的 OOM 事件、/var/log/messages 中的内存记录;
  • MySQL 层:performance_schema 内存统计、SHOW GLOBAL STATUS 里的内存相关变量、慢查询数、活跃会话数。

具体到工具链,可以用 mysqld_exporter 配合 Prometheus 和 Grafana。mysqld_exporter 默认会采集 performance_schema 中的内存指标,比如 memory_summary_global_by_event_name。如果没有这套监控,至少要在故障复现时手动执行:

SQL
SELECT * FROM sys.memory_by_thread_by_current_bytes ORDER BY current_bytes_used DESC LIMIT 20;

这样能快速定位哪个连接或哪个线程占用了大量内存。

压测也不能省。上线前用 sysbench 准备一张千万行大表,模拟超大 LIMIT 和 ORDER BY 查询,观察容器内存曲线。只要复现出内存快速爬升,就说明问题没根除。

5. 常见问题与排查技巧速查

5.1 容器 OOM 和系统 OOM 怎么快速区分

很多人一看到 OOM 就认为是宿主机内存不够,其实是两回事。容器 OOM 是 cgroup 限制导致的,宿主机的 free -h 往往还有大量空闲;系统 OOM 是整机物理内存不足,影响范围更大,甚至 SSH 都会卡。判断方法很简单:

判断维度 容器 OOM 系统 OOM
容器退出码 137,OOMKilled=true 不一定,可能 137 也可能其他信号
宿主机 free -h 剩余内存还有很多 剩余内存很少或为 0
dmesg 场景 在 cgroup 内杀掉某个进程 整机触发 oom-killer,多个进程受影响
其他容器 通常不受影响 同一台宿主机上的所有容器都可能受影响

5.2 如何从 MySQL 侧定位内存占用大户

如果 MySQL 还没完全 OOM,只是内存吃紧,可以执行下面几条 SQL:

SQL
-- 查看当前所有线程占用内存情况
SELECT * FROM sys.memory_by_thread_by_current_bytes ORDER BY current_bytes_used DESC LIMIT 20;
 
-- 查看全局内存事件汇总
SELECT EVENT_NAME, CURRENT_NUMBER_OF_BYTES_USED
FROM performance_schema.memory_summary_global_by_event_name
ORDER BY CURRENT_NUMBER_OF_BYTES_USED DESC
LIMIT 20;

同时观察 SHOW FULL PROCESSLIST,重点看 State 字段。如果大量会话卡在 Sending data、Sorting result、Creating sort index,多半和排序、临时表有关。

5.3 麒麟 V10 上的几个注意点

麒麟 V10 是基于 4.19 内核的国产系统,整体对 Docker 和 cgroup v1 支持很成熟,但在排查时还是有几个差异需要留意:

  • Docker 日志不在传统路径,优先用 journalctl -u docker 和 docker logs;
  • /var/log/messages 里也可能有内核日志,但 /var/log/kern.log 不一定存在;
  • 查看 cgroup 文件路径时,如果容器使用的是 cgroup v2,路径会变成 /sys/fs/cgroup/docker/... 而不是 /sys/fs/cgroup/memory/docker/...,需要先用 mount | grep cgroup 确认;
  • 麒麟系统可能默认没有 sysstat 工具,sar 等命令需要额外安装;排查时用 top、free、ps 这些基础命令足够。

5.4 排查命令速查表

最后整理一份我当时用到的命令列表,方便以后直接抄:

目标 命令
查看容器退出码和是否 OOM docker inspect mysql --format '{{.State.ExitCode}} {{.State.OOMKilled}}'
查看内核 OOM 日志 dmesg -T | grep -i -E "oom|killed"
查看容器 cgroup 内存限制 cat /sys/fs/cgroup/memory/docker/$(docker inspect -f '{{.Id}}' mysql)/memory.limit_in_bytes
查看容器当前内存使用 cat /sys/fs/cgroup/memory/docker/$(docker inspect -f '{{.Id}}' mysql)/memory.usage_in_bytes
查看实时内存竞争 top -o %MEM
查看 MySQL 进程内存 ps -eo pid,rss,vsz,cmd | grep mysqld
查看慢查询日志 tail -f /var/log/mysql/slow.log
查看当前线程内存占用 SELECT * FROM sys.memory_by_thread_by_current_bytes;
定位超大 LIMIT SQL 在慢查询日志中搜索 18446744073709551615

这次故障让我最深的体会是:业务代码里的一个“不限量”,在数据库看来就是让它一次干完所有活。上线前真的要对所有查询做限量兜底,别让 infinity、-1、999999999999 这种值有机会拼进 SQL。而且 OOM 排查不要只盯着应用日志,容器层和内核层的日志才是第一现场。如果你也在麒麟 V10 上跑 MySQL 容器,建议提前把上述命令和监控配好,免得到时候手忙脚乱。

MySQL OOM(内存溢出)的排查思路及优化方法
本文深入探讨了OOM(内存溢出)现象,分析其产生的原因及操作系统处理机制,提供了详细的排查思路,包括如何检查和调整MySQL内存配置,以及如何监控和优化系统内存使用。
MYSQL轻松学
10926
MySQL 8.0 容器反复崩溃排查实录:从端口误用到内存OOM的踩坑与修复
本文记录MySQL 8.0在2GB内存云服务器上容器反复崩溃的完整排查过程,根因包括端口误用(混淆SSH 22端口与MySQL业务端口)、Docker未设内存限制、MySQL默认配置过高(wait_timeout、max_connections等)及后台重复进程导致OOM。通过添加memory limit、调优MySQL参数、清理冗余进程和硬件升级建议实现稳定运行。
钱栈up
235
inputstreamreader未关闭会导致oom_在MySQL 5.7下排查内存泄露和OOM问题全过程
本文介绍了一个MySQL 5.7版本因内存泄露导致的OOM问题排查过程,通过使用performance_schema定位到线程池配置不当为根本原因。
weixin_39628268
643
Docker容器exited(137)排查指南:从OOM到SIGKILL一次讲透
本文系统解析Docker容器退出码137的本质——即进程被SIGKILL(信号9)强制终止,对应128+9的Linux退出码规则。重点覆盖OOM Killer触发机制、dmesg日志取证、容器memory limit超限、JVM内存感知缺失、docker stop超时强杀等五大主因,并给出从cgroup限制、JVM参数调优、数据库内存配置到优雅停机的完整应对方案,强调系统级而非应用日志层面的排查路径。
王杰岸
87
在MySQL 5.7下排查内存泄露和OOM问题全过程
作者通过排查发现个人网站后台使用MySQL5.7版本时遭遇频繁oom-kill,最终定位到线程池与性能模式结合导致内存泄漏,关闭线程池后问题得以解决。
小知_知数堂
939
mysql5.7 OOM内存溢出问题排查
本文详细介绍了如何排查MySQL5.7的内存溢出问题,通过P_S performance_schema模块分析了内存使用情况,如table_handles、events_statements等。同时,讨论了开启P_S和thread pool可能导致的问题,并提供了查看内存消耗的多角度视图。最后,提到了可能的内存泄漏原因,如连接、table cache和临时表,并分享了一个特定版本的MySQL内存泄漏问题。
cdrcsy
5865
RocketMQ容器频繁Killed?默认JVM参数与OOM Killer是主因
本文深入分析RocketMQ 5在Docker中频繁被Killed的根本原因,指出其默认JVM参数(如-Xms8g)与小内存宿主机/容器限制冲突,触发Linux OOM Killer机制。内容涵盖退出码识别、dmesg日志取证、docker stats内存监控、JVM参数覆盖方案(通过JAVA_OPT环境变量)、cgroup内存感知失效原理,并提供适配2G/8G机器的docker-compose.yml配置范例及生产部署建议。
weixin_33922670
426
记录一个排查oom思路
本文记录了一次由于Linux内存管理机制导致MySQL进程被杀死的问题排查过程。通过检查Linux日志发现系统自动拉起了挂掉的MySQL,但并非由于MySQL自身内存占用过高。分析了MySQL 5.7以上版本的performance_schema、内存相关表、总内存消耗及线程内存占用,最终得出结论:MySQL被杀是因为其他容器内存占用过高,Linux按照评分规则而非直接内存占用进行进程结束。
奥兔man
576
inputstreamreader未关闭会导致oom_MySQL 5.7下排查内存泄漏和OOM问题全程记录
本文介绍了一个MySQL 5.7版本因内存泄露导致的OOM问题排查过程。通过对mysqld进程的监控、日志分析及利用P_S性能模式定位到问题源于线程池设置不当。
weixin_39607473
136
JVM高频知识点实战:内存模型、G1收集器与OOM排查
本文聚焦JVM高频实战要点,系统梳理内存模型五大运行时数据区(程序计数器、虚拟机栈、本地方法栈、Java堆、元空间),深入解析堆分代机制及G1收集器的Region模型、停顿时间控制与关键参数(如-XX:MaxGCPauseMillis、-XX:InitiatingHeapOccupancyPercent)。结合容器化场景,详解Docker中JVM内存配置陷阱、OOM Killer排查、GC日志持久化及堆外内存管理,提供可落地的调优策略与线上问题速查方法。
weixin_34356555
342
Java面试场景题实战:从并发扣库存到OOM排查
本文聚焦Java面试中日益重要的场景题,涵盖并发扣库存、订单超时关闭、接口幂等性、MQTT设备接入及OOM排查五大高频实战场景。强调从工程判断力出发,结合业务约束比较方案优劣,辅以可运行代码与兜底设计。内容包含刷题闭环方法、面试追问应对框架、典型环境坑排查(如Lombok兼容性、JDK版本配置、非堆内存OOM),并突出Redis Lua、ZSET、唯一索引、MQTT QoS等关键技术落地细节。
weixin_34364071
388
MySQL OOM问题诊断与pt-mysql-summary工具实战
本文围绕MySQL因内存超限触发OOM Killer导致进程被杀的问题,系统介绍使用Percona Toolkit中pt-mysql-summary工具进行诊断的完整流程:包括环境检查、内存配置分析(缓冲池、连接内存、临时表)、问题查询定位(长执行、大临时表、全表扫描),以及紧急处置与长期优化方案(索引优化、参数调优、监控建设)。特别涵盖容器化环境下的Cgroup限制和内存泄漏检测方法。
weixin_34195142
382
Windows虚拟内存设置与OOM排查实战:从原理到参数配置
本文深入解析Windows虚拟内存原理,阐明页面文件、提交限制(Commit Limit)与OOM的关系;提供按物理内存容量(8GB/16GB/32GB+)的虚拟内存参数配置方案;重点指出OOM根本原因常在于提交内存超限或应用层配置不当(如JVM堆设置、WSL2内存限制),而非单纯虚拟内存大小问题;强调排查应优先查看任务管理器提交量、资源监视器进程提交排名及事件查看器日志,再针对性调整系统或应用配置。
周恰恰
207
LIMIT vs 传统查询:MySQL数据检索效率对比
本文通过对比MySQL中LIMIT分页与传统全量查询的性能,揭示了在大数据量下LIMIT在执行时间、内存占用和网络IO方面的显著优势。测试显示,全量查询耗时约1200ms且内存占用高,而LIMIT查询仅需15ms,内存低于5MB。文章还探讨了深度分页优化、索引使用及实战建议,强调合理使用LIMIT配合索引可大幅提升数据库性能。
491
Windows虚拟内存配置与OOM排查:分页文件设置全解
本文深入解析Windows虚拟内存机制,重点阐述分页文件(pagefile.sys)对提交限制(commit limit)的影响,说明为何16GB物理内存仍可能触发OOM;提供Win10/Win11下虚拟内存的手动配置方法、不同内存容量(8GB/16GB/32GB)的推荐设置、SSD与多硬盘环境下的优化策略;并构建完整OOM排查链路,涵盖事件查看器分析、任务管理器提交监控、Docker/Elasticsearch/MySQL等开发工具的协同调优,以及内存泄漏与32位进程地址空间耗尽等易忽略诱因。
大厂男孩的粉丝
249
mysql docker oom_docker应用,后端服务出现OOM情况排查
这篇博客探讨了JVM内存设置问题,当系统内存为8GB时,默认最大堆大小约为2GB。然而,应用在高流量时段内存使用量激增至4.3GB,触发了`OutOfMemoryError: GC Overhead Limit Exceeded`。为解决此问题,建议设置`JAVA_OPTS`以增大堆内存至6GB。然而,检查发现设置未生效,可能是由于Docker容器未限制内存使用。通过`jmap`和`jhat`工具进行堆内存分析和对象统计,以进一步诊断和优化内存配置。
weixin_35725138
260
Linux 针对 MySQL 专用服务器的 OOM 预防策略配置
本文聚焦Linux环境下专用于MySQL的服务器OOM预防策略,核心包括禁用overcommit、合理配置InnoDB Buffer Pool与内存参数、使用cgroups硬限流、Swap优化及OOM Killer调度调优。强调通过内核级控制(如vm.overcommit_memory=2)和MySQL层配合(内存分配失败处理),避免数据库进程被意外终止,保障服务稳定性与数据一致性。
学亮编程手记
1070
videocapture.read()解决内存泄露_在MySQL 5.7下排查内存泄露和OOM问题全过程
本文介绍了一个MySQL 5.7版本因内存泄露导致的OOM问题排查过程。通过对mysqld进程的监控、日志分析及利用performance_schema进行深入调查,最终定位到线程池设置为问题根源。
weixin_39761422
504
大量delete mysql的数据时,为什么导致OOM
文章探讨了大量删除MySQL数据时可能导致的OOM问题,包括事务未提交、查询结果集过大、内存泄漏和未优化的删除操作。提出了解决方案,如分批处理、使用LIMIT子句、提交事务、优化SQL以及检查和修复内存泄漏,并建议在操作前备份数据。
程序员1970
896
mysql maxopenfiles_mysql数据库配置open_files_limit过大导致数据库被OOM
本文记录了一次MySQL数据库因配置不当导致的内存溢出问题。通过对open_files_limit参数的调整,解决了数据库在导入大量数据时被系统杀掉的问题。
weixin_39716510
267
oom如何排查
weixin_55086006
从OOM到MySQL锁表:一次线上Java服务内存泄漏的完整排查与修复实录
liu伟鹏
oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,task=vslam_ros_node,pid=3659,uid=0
本文分析了Linux系统中vslam_ros_node进程被OOM-Killer终止的原因,并提供了排查和解决该问题的方法。包括检查内存使用情况、分析OOM评分机制、优化内存使用、限制非关键进程内存、增加系统内存或交换空间等。
zhuhuanzu5148
[] - 2023-04-06 SpringBoot 实现 MySQL 百万级数据量导出并避免 OOM 的解决方案.pdf
资源摘要信息: 在现代企业级Java Web应用开发中,使用Spring Boot集成MySQL进行海量数据导出(如百万级甚至千万级记录)是一项常见但极具挑战性的任务。若处理不当,极易触发Java虚拟机的内存溢出(OutOfMemoryError, OOM),导致服务崩溃、响应超时、线程阻塞乃至整个应用不可用。本文档标题《SpringBoot 实现 MySQL 百万级数据量导出并避免 OOM 的解决方案》精准聚焦于该高频痛点,系统性地阐述了一套基于JDBC底层机制、Spring Data JPA高级特性与Java 8 Stream流式编程思想深度融合的高可靠性、低内存占用、可生产落地的数据导出范式。其核心并非简单调用findAll()后在内存中遍历List再逐行写入响应流——这种传统方式会将全部实体对象一次性加载至JVM堆内存,当数据量达百万级时,即使单个Entity仅占2KB,总内存消耗也将突破2GB,远超常规Web容器(如Tomcat默认堆配置-Xmx512m~1024m)承载能力,OOM风险极高。真正有效的解决方案必须打破“全量加载→内存遍历→批量写出”的反模式,转向“按需拉取→边读边写→即时释放”的流式处理模型。该模型依赖多个关键技术协同:第一,JDBC层面的fetchSize控制,通过设置Statement或PreparedStatement的fetchSize为Integer.MIN_VALUE(即-1),向MySQL JDBC驱动(mysql-connector-java)显式声明启用流式结果集(Streaming Result Set),驱动将不再缓存整张结果集,而是仅维持一个游标(cursor),每次仅从网络缓冲区读取少量记录(实际由MySQL服务器分批推送),极大降低客户端内存驻留压力;第二,Spring Data JPA的@QueryHints注解与HINT_FETCH_SIZE提示,将上述JDBC参数透传至底层EntityManager生成的查询语句,确保JPA抽象层不屏蔽底层流控能力;第三,Repository方法返回类型声明为java.util.Stream而非List或Iterable,这是Spring Data JPA 2.0+引入的关键增强,使框架能自动包装底层JDBC ResultSet为惰性求值的Stream,支持map、filter、forEach等函数式操作且不预加载全部数据;第四,严格限定@Transactional(readOnly = true)作用域,既保障数据库连接在流读取期间持续有效(防止Connection closed异常),又通过只读事务提示数据库优化执行计划(如跳过undo log写入、减少锁粒度),提升查询吞吐;第五,配合EntityManager.detach(entity)主动解除已处理实体与一级缓存(Persistence Context)的关联,强制GC回收已序列化完毕的对象引用,防止JPA一级缓存无限膨胀;第六,在Controller层采用try-with-resources语法确保Stream自动关闭,结合HttpServletResponse直接获取PrintWriter/OutputStream,以“零中间集合”方式将每条记录实时格式化(如CSV字段转义、UTF-8编码)并刷入HTTP响应体,实现内存常量级(O(1))占用。此外,方案还隐含多项工程实践要点:需禁用Hibernate二级缓存以防脏读;建议关闭Spring Boot Actuator中可能触发全量实体加载的端点;导出接口应增加异步化(如@Async)与进度回调机制以提升用户体验;对超大表需结合分页游标(如WHERE id > ? LIMIT N)或数据库原生导出命令(SELECT ... INTO OUTFILE)作为备选;同时必须进行严格的压测验证,监控JVM GC日志、堆内存直方图及MySQL slow query日志,确保fetchSize生效且网络传输带宽成为瓶颈而非内存。综上,该方案绝非单一技术点的堆砌,而是涵盖JDBC协议细节、JPA生命周期管理、Spring事务语义、Java内存模型、HTTP协议规范及数据库性能调优的综合性架构实践,代表了当前Spring生态下处理海量数据导出问题的工业级最佳实践标准。
毕业小助手
Memory cgroup out of memory: Killed process 3530055 (R) total-vm:11833668kB, anon-rss:8257236kB, file-rss:34280kB, shmem-rss:0kB, UID:1000 pgtables:18152kB oom_score_adj:0
本文详细介绍了Linux内存控制组(Memory cgroup)的OOM问题,并提供了一套分步解决方案。首先解释了Memory cgroup和OOM Killer的工作原理,然后通过调整进程的oom_score_adj优先级、合理配置Memory cgroup限制、监控内存使用指标、优化内核参数与进程行为、分析OOM日志并针对性优化等步骤,帮助用户解决OOM问题。
阿洋不会写代码
Linux进程管理与系统资源控制:深入解析cgroups和OOM杀手
![Linux进程管理与系统资源控制:深入解析cgroups和OOM杀手](https://img-blog.csdnimg.cn/d6cb61435d044c69b436068b7b869282.png)# 1. Linux进程管理概述Linux作为一种多用户、多任务的操作系统,通过进程管理,确保了系统的高效稳定运行。进程管理涉及进程的创建、执行、调度、同步、通信以及终止等多个方面。在Linux环境下,每一个运行的程序都被称为一个进程,这些进程共享有限的系统资源,如CPU、内存等。为了维持系统的稳定性和公平性,必须对进程资源进行有效的管理。理解Linux进程管理首先需要熟悉进程的生
SW_孙维
MySQL5.7内存泄露排查[项目代码]
MySQL 5.7 内存泄露排查是一项高度系统化、多维度、深度依赖内核机制与监控体系的运维与开发协同任务。其核心难点在于:MySQL 5.7 的内存管理模型已显著区别于早期版本(如5.6),引入了更精细的内存分配追踪能力(尤其依托 performance_schema 中的 memory_summary_* 系列表),但同时也因组件耦合度提升(如 Performance Schema 与 Thread Pool 插件共存)、内存池复用逻辑复杂(如 THD 对象生命周期管理、MEM_ROOT 多级嵌套释放)、以及部分内存统计未自动归还(如某些 prepared statement 缓存、table definition cache 中的 TABLE_SHARE 对象长期驻留)而极易诱发“伪内存泄露”——即内存持续增长却无显式泄漏点,实则为缓存膨胀、连接堆积、元数据累积或统计延迟所致。本文所指“OOM内存溢出问题”,并非传统意义上的 C/C++ 堆内存未 free 导致的野指针型泄露,而是 MySQL Server 层在高并发、长连接、复杂查询场景下,因内存分配策略与释放时机不匹配、监控盲区未覆盖关键路径、或配置参数与硬件拓扑(如 NUMA 架构)不协调,最终触发 Linux OOM Killer 强制 kill mysqld 进程的综合性稳定性危机。具体而言,performance_schema 在 MySQL 5.7 中默认启用(performance_schema=ON),并新增 memory_summary_global_by_event_name、memory_summary_by_thread_by_event_name、memory_summary_by_account_by_event_name、memory_summary_by_host_by_event_name 等核心视图,可逐层下钻至事件粒度(如 sql/SELECT、innodb/buf_buf_pool、myisam/MI_INFO)。通过执行类似 SELECT EVENT_NAME, CURRENT_NUMBER_OF_BYTES_USED, HIGH_NUMBER_OF_BYTES_USED FROM performance_schema.memory_summary_global_by_event_name ORDER BY CURRENT_NUMBER_OF_BYTES_USED DESC LIMIT 20;即可快速定位当前占用内存最高的内部模块。值得注意的是,“CURRENT_NUMBER_OF_BYTES_USED”反映实时持有量,而“HIGH_NUMBER_OF_BYTES_USED”记录历史峰值,二者差值过大往往暗示存在周期性内存抖动或未及时回收现象。进一步结合 sys schema(MySQL 官方提供的高性能诊断视图封装库),使用 sys.memory_by_host_by_current_bytes、sys.memory_by_thread_by_current_bytes、sys.memory_global_by_current_bytes 等视图,可将原始 performance_schema 数据转化为业务可读的主机/IP、线程ID、用户账号维度统计,极大降低分析门槛。例如,若发现某特定应用账号(如 app_user@192.168.10.%)的内存消耗远超均值,即可锁定其对应连接线程,再关联 threads 表获取 PROCESSLIST_ID,进而通过 SHOW FULL PROCESSLIST 或 performance_schema.events_statements_current 定位其正在执行的 SQL 及执行计划,判断是否存在未关闭游标、无限嵌套子查询、或 GROUP BY + ORDER BY 导致的内部临时表失控膨胀。尤为关键的是,MySQL 5.7 中 table cache 相关内存成为排查突破口,绝非偶然。table_definition_cache(默认400)与 table_open_cache(默认2000)共同控制着 TABLE_SHARE 和 TABLE 对象的缓存容量。当大量不同表被高频访问时,TABLE_SHARE(包含表结构、字段定义、索引元信息等只读内容)会被缓存在全局 hash 表中,而每个连接打开表时又会创建独立的 TABLE 实例(含锁、状态、扫描位置等运行时数据)。若应用存在“连接不复用、每查一表建一连接、且表名动态拼接导致无法命中 cache”的反模式,则 table_definition_cache 将持续增长直至耗尽(表现为 TDC 占用内存飙升),而由于 TABLE_SHARE 生命周期由引用计数管理,一旦某连接异常中断未正确 dec_ref,该对象便永久滞留于 cache 中,形成“缓存泄漏”。此外,P_S 与 Thread Pool 同时启用时存在已知兼容性缺陷:Thread Pool 的工作线程复用机制与 P_S 的 per-thread 内存统计钩子发生竞争,导致 memory_summary_by_thread_by_event_name 中部分线程内存统计始终为 0 或数值异常,掩盖真实泄漏源;此时需临时禁用 thread_pool(skip_thread_pool=ON)或升级至 5.7.20+ 修复版本。其他典型诱因还包括:tmp_table_size/max_heap_table_size 设置过小迫使磁盘临时表频繁创建(InnoDB 临时表仍需 buffer pool 分配页);query_cache_type=1 且缓存碎片严重(虽5.7默认关闭,但旧环境可能遗留);以及 NUMA 架构下未启用 innodb_numa_interleave 导致内存分配不均,局部节点内存率先耗尽触发 OOM。综上,MySQL 5.7 内存问题本质是“配置—代码—内核—硬件”四层耦合故障,必须依托 systemtap/eBPF 辅助跟踪 malloc/free 调用栈、结合 pstack/gdb 抓取 core dump 中 THD/MEM_ROOT 树状结构、并交叉验证 /proc/mysqld_pid/status 中 VmRSS/VmData 指标变化趋势,方能实现根因闭环。
MySQL导致oom
m0_59229493
OOM故障排查:定位内存泄漏大型变量的实战指南
文具一年
MySQL 内存分析
一个计算最大打开文件数的公式为:max(max(wanted_files, max_connections*5), open_files_limit),其中wanted_files表示需要的文件数,它基于最大连接数和表缓存大小计算得出
HoneyMoose
668