奋斗
努力

MySQL服务在CentOS/Ubuntu服务器上吃掉80%内存,怎样排查和调优?

云计算

MySQL 占用 80% 内存通常不是“故障”,而是配置不当或工作负载超出预期。以下是系统化的排查与调优步骤:


🔍 一、先确认:是 MySQL 真的占用了 80%,还是只是 RSS(常驻集)高?

1. 查看实际内存使用

# CentOS/Ubuntu 通用
top -p $(pgrep mysqld)
# 或
ps aux | grep mysql
  • VSZ(虚拟内存):可能很大,但不代表真实占用。
  • RSS(物理内存):才是关键指标。

✅ 如果 RSS 确实占系统总内存的 80%,继续下一步。


🧩 二、常见原因分析

原因 说明
innodb_buffer_pool_size 设置过大 InnoDB 缓冲池默认建议设为物理内存的 50–70%,若设为 90%+ 极易撑爆内存
大查询 / 临时表溢出到磁盘外 排序、分组等操作产生大量临时数据
连接数过多 每个连接分配独立内存(如 sort_buffer, join_buffer),并发高时累积巨大
未启用交换空间(swap) 无 swap 时 OOM killer 直接终止进程;有 swap 则表现为“慢”而非崩溃
监控工具误报 某些监控将共享库、缓存等计入 MySQL 进程,实际并非全部归属 mysqld

🛠️ 三、逐步排查与调优方案

Step 1:检查当前内存配置

登录 MySQL:

SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW VARIABLES LIKE 'max_connections';
SHOW VARIABLES LIKE 'sort_buffer_size';
SHOW VARIABLES LIKE 'join_buffer_size';
SHOW VARIABLES LIKE 'tmp_table_size';
SHOW VARIABLES LIKE 'max_heap_table_size';

关键公式估算最大理论内存占用:

最大内存 ≈ innodb_buffer_pool_size 
         + (max_connections × (sort_buffer_size + read_buffer_size + join_buffer_size + thread_stack))
         + tmp_table_size + max_heap_table_size

⚠️ 举例:
若 innodb_buffer_pool_size = 16G, max_connections=200, 其他 buffer 各 2MB →
额外连接内存 ≈ 200 × 8MB = 1.6GB → 总计约 17.6GB
若服务器只有 16GB RAM → 必然 OOM!


Step 2:优化核心参数(以 16GB 内存为例)

编辑 /etc/my.cnf 或 /etc/mysql/mysql.conf.d/mysqld.cnf:

[mysqld]
# InnoDB 缓冲池:设为物理内存的 50–70%
innodb_buffer_pool_size = 10G

# 连接数:根据实际需求调整,避免过高
max_connections = 100

# 每个连接的缓冲区:适当减小
sort_buffer_size = 2M
read_buffer_size = 1M
join_buffer_size = 2M
thread_stack = 256K

# 临时表限制
tmp_table_size = 64M
max_heap_table_size = 64M

# 启用 swap(作为安全网)
# 在操作系统层面添加 swap 分区或文件

💡 更精细的做法:使用 MySQL Tuning Primer 或 Percona Monitoring and Management 自动推荐值。


Step 3:检查是否有异常查询消耗内存

-- 查看当前正在执行的查询及内存使用情况
SELECT * FROM information_schema.processlist WHERE COMMAND != 'Sleep';

-- 查看最近的大查询(需开启 general log 或 performance schema)
SELECT DIGEST_TEXT, SUM(SUM_ROWS_EXAMINED) AS total_rows_examined
FROM performance_schema.events_statements_summary_by_digest
GROUP BY DIGEST_TEXT
ORDER BY total_rows_examined DESC
LIMIT 10;

或使用:

mysqlsla slow.log   # 分析慢查询日志

Step 4:监控实时内存分布

安装并运行 Performance Schema 或 sys schema 视图:

-- 查看 InnoDB 缓冲池命中率
SELECT VARIABLE_VALUE AS buffer_pool_hit_rate
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests';

-- 查看是否频繁使用临时表
SHOW STATUS LIKE 'Created_tmp%';

若 Created_tmp_tables 和 Created_tmp_disk_tables 比例高 → 说明临时表过多,需优化 SQL 或增大 tmp_table_size。


Step 5:操作系统层加固

添加 Swap(防止 OOM)

# 创建 4GB swap 文件
sudo dd if=/dev/zero of=/swapfile bs=1M count=4096
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# 永久生效
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

调整内核参数(减少过度提交)

# /etc/sysctl.conf
vm.overcommit_memory = 1
vm.swappiness = 10
sudo sysctl -p

✅ 四、验证效果

重启 MySQL 后观察:

watch -n 1 "free -h && echo '---' && ps aux | grep mysqld"

确保:

  • RSS 稳定在合理范围(如 < 70% 总内存)
  • 无频繁 swap 使用
  • 查询响应时间正常

📌 总结 checklist

项目 操作
✔️ 确认 RSS 而非 VSZ top / ps
✔️ 缩小 innodb_buffer_pool_size ≤ 70% 物理内存
✔️ 控制 max_connections 按需设定,勿盲目放大
✔️ 减小每连接 buffer sort/join/read buffer
✔️ 限制临时表大小 tmp_table_size, max_heap_table_size
✔️ 分析慢查询 优化索引、改写 SQL
✔️ 添加 swap 作为最后防线
✔️ 启用监控 Percona PMM / Prometheus + Grafana

如仍无法解决,请提供:

  • 服务器总内存
  • my.cnf 中 [mysqld] 段主要配置
  • SHOW ENGINE INNODB STATUSG 输出
  • top 截图或 free -h 结果

我可进一步帮你精准调优。

未经允许不得转载:云服务器 » MySQL服务在CentOS/Ubuntu服务器上吃掉80%内存,怎样排查和调优?