记一次 Docker 环境下 MySQL 与 MongoDB 内存抢占导致 CPU 爆表的深度排查
·
记一次 Docker 环境下 MySQL 与 MongoDB 内存抢占导致 CPU 爆表的深度排查
现象描述
在日常运维中发现,服务器 CPU 负载突然飙升,通过 top 指令发现:

- mysqld 进程 CPU 占用持续在 220% - 360% 波动。
- mongod 进程 CPU 占用约 110% - 360%。
- 内存占用惊人:两个进程的常驻内存(RES)分别达到了 18.6GB 和 21.7GB,几乎榨干了宿主机 46GB 的物理内存。
- 即使手动执行
STOP SLAVE停止 MySQL 主从同步,CPU 和内存负载依然没有明显下降。
排查过程与根因分析
1. 误区:主从同步是元凶?
初始怀疑是主从同步压力大。但通过 SHOW SLAVE STATUS 发现 SQL 线程已停止,且 Created_tmp_disk_tables 数量并不多。这说明高负载并非单纯由同步 SQL 引起,而是系统级资源耗尽引发的连锁反应。
2. 核心矛盾:配置与实际占用的巨大偏差
通过数据库内部指令发现一个诡异现象:
- MySQL 的
innodb_buffer_pool_size仅配置了 2GB。 - 但在 Docker 中,
mysqld实际占用了 19GB 内存。
根因点 1: 数据库在 Docker 容器中默认没有内存限制(Limit),它会识别宿主机的总内存(46GB)来申请资源。
根因点 2: 由于物理内存被 MySQL 和 MongoDB 强行占满了 90% 以上,系统触发了频繁的内存分页交换(Swapping)。CPU 爆表实际上是内核在处理极高频的 I/O 等待和内存回收。
3. Docker 层的资源失控
通过 docker stats 发现:
- MySQL 容器 CPU 占用率显示为 784%(意味着跑满了 8 个核心)。
- 两个容器都没有设置
MEM LIMIT,处于完全竞争状态。
解决方案:实施“资源配额制”
针对 Docker 部署的数据库,单纯修改 my.cnf 或 mongod.conf 是不够的,必须在 容器层 和 应用层 同时进行约束。
优化后的 docker-compose 配置建议:
services:
mysql:
image: mysql:8.0
deploy:
resources:
limits:
cpus: '4.0' # 限制 CPU,防止单容器锁死宿主机
memory: 16G # 强制物理内存上限
command:
- --innodb_buffer_pool_size=12G # 核心:给内部缓存留足空间,但不超过容器限额
mongo:
image: mongo:latest
deploy:
resources:
limits:
cpus: '4.0'
memory: 16G
command:
- --wiredTigerCacheSizeGB=10 # 显式限制 Mongo 缓存
总结与反思
- 不要让数据库“裸奔”:在 Docker 中运行数据库,必须显式设置
deploy.resources.limits,否则在高负载下,容器会无节制地吞噬宿主机资源。 - 内外配合:容器的内存 Limit 应略大于数据库内部缓存(如 Buffer Pool),预留 25% 左右的空间给线程连接、排序缓冲区等。
- 重启是最后的防线:当内存发生严重溢出且停止同步无效时,重启容器是清理僵死事务、释放泄露内存、重置系统 I/O 状态最有效的手段。
运维建议命令
- 查看容器实时资源:
docker stats - 释放宿主机 Page Cache:
sync; echo 3 > /proc/sys/vm/drop_caches - 进入容器抓取实时 SQL:
docker exec -it <id> mysql -e "show full processlist;"
更多推荐
所有评论(0)