MySQL 进程内存占用高是云服务器常见的性能问题,通常由配置不当、查询效率低或连接数过多引起。以下是系统化的排查与优化步骤:
一、查看 MySQL 内存占用情况
1. 查看 MySQL 进程实际内存使用
# 查看 mysqld 进程的 RSS(常驻内存)和 VSZ(虚拟内存)
ps aux | grep mysqld
top -p $(pgrep mysqld)
- RSS:实际物理内存占用(重点关注)
- VSZ:虚拟内存映射大小(包含未使用的部分,参考意义较小)
2. 查看 MySQL 内部内存分配
登录 MySQL 后执行:
-- 查看全局内存使用情况
SHOW GLOBAL STATUS LIKE 'InnoDB_buffer_pool_size';
SHOW GLOBAL STATUS LIKE 'Key_buffer_size';
SHOW GLOBAL STATUS LIKE 'query_cache_size';
-- 查看当前连接数及每个连接的内存估算
SELECT * FROM information_schema.processlist;
-- 查看 InnoDB 缓冲池命中率
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';
3. 使用 Percona Toolkit 深入分析(推荐)
pt-memory-advisor # 建议合理内存配置
pt-query-digest # 分析慢查询
pt-mysql-summary # 生成 MySQL 状态摘要
二、常见原因及解决方案
✅ 1. InnoDB Buffer Pool 过大
现象:innodb_buffer_pool_size 设置过大(如超过服务器内存的 70%)
检查:
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
解决:
- 一般建议设置为服务器总内存的 50%~70%
- 修改
/etc/my.cnf或/etc/mysql/my.cnf:[mysqld] innodb_buffer_pool_size = 2G # 根据实际内存调整 innodb_buffer_pool_instances = 8 # 多实例提升并发性能 - 重启 MySQL 生效:
systemctl restart mysql
💡 注意:MySQL 5.6+ 支持动态调整
innodb_buffer_pool_size,无需重启:SET GLOBAL innodb_buffer_pool_size = 2147483648; -- 2GB
✅ 2. Key Buffer(MyISAM)占用过高
如果大量使用 MyISAM 表,key_buffer_size 可能占用过多内存。
检查:
SHOW VARIABLES LIKE 'key_buffer_size';
SHOW GLOBAL STATUS LIKE 'Key_read_requests%';
解决:
- 迁移 MyISAM 表到 InnoDB(更推荐)
- 若必须保留,适当减小:
key_buffer_size = 64M
✅ 3. 临时表占用内存
大查询或排序操作会产生临时表,可能驻留内存。
检查:
SHOW GLOBAL STATUS LIKE 'Created_tmp%';
Created_tmp_disk_tables/Created_tmp_tables比例过高 → 临时表溢出到磁盘,但前期仍占内存
解决:
[mysqld]
tmp_table_size = 64M
max_heap_table_size = 64M
✅ 4. 连接数过多导致内存累积
每个连接都会分配固定内存(如 read_buffer_size, sort_buffer_size 等)。
检查:
SHOW VARIABLES LIKE '%buffer_size%';
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW VARIABLES LIKE 'max_connections';
| 典型默认值(可能导致内存爆炸): | 变量 | 默认值 | 说明 |
|---|---|---|---|
read_buffer_size |
128K | 每连接读取缓冲区 | |
sort_buffer_size |
256K | 每连接排序缓冲区 | |
join_buffer_size |
256K | 每连接连接缓冲区 | |
thread_stack |
192K | 每线程栈空间 |
计算示例:
假设 max_connections=500,每个连接平均占用 ~1MB 额外内存
则额外内存 ≈ 500 × 1MB = 500MB
解决:
-
降低不必要的 buffer 大小:
[mysqld] read_buffer_size = 128K sort_buffer_size = 256K join_buffer_size = 256K thread_stack = 192K -
限制最大连接数:
max_connections = 200 # 根据实际需求调整 -
使用连接池(应用层):如 HikariCP、Druid 等,避免频繁创建/销毁连接。
✅ 5. 慢查询或未优化查询导致内存泄漏式增长
某些复杂查询可能在执行过程中积累大量中间结果。
解决:
- 启用慢查询日志:
slow_query_log = 1 long_query_time = 2 slow_query_log_file = /var/log/mysql/slow.log - 使用
EXPLAIN分析慢查询,添加合适索引 - 定期清理无用数据
✅ 6. 其他内存相关变量检查
SHOW VARIABLES LIKE '%cache%';
SHOW VARIABLES LIKE '%size%';
重点关注:
query_cache_size(MySQL 5.7.20+ 已废弃,建议设为 0)table_open_cacheopen_files_limit
三、快速释放内存技巧
⚠️ 注意:MySQL 没有直接的“释放内存”命令,以下方法需谨慎
| 方法 | 说明 | 风险 |
|---|---|---|
FLUSH STATUS |
重置统计信息,不释放内存 | 无 |
RESET QUERY CACHE |
清除查询缓存(旧版本) | 仅影响缓存命中 |
| 重启 MySQL | 彻底释放所有内存 | 服务中断 |
| 调整参数并热加载 | 如动态调小 buffer pool | 需 MySQL 5.6+ |
-- 动态调整 InnoDB Buffer Pool(MySQL 5.6+)
SET GLOBAL innodb_buffer_pool_size = 1073741824; -- 1GB
-- 动态调整 Key Buffer(需重启或 MySQL 8.0.3+ 支持)
-- 大多数 buffer 参数需重启生效
📌 最佳实践:通过
my.cnf调整参数后,重启 MySQL 是最稳妥的方式。
四、自动化监控建议
1. 使用 Prometheus + Grafana 监控
- 导出器:
mysqld_exporter - 关键指标:
mysql_global_variables_innodb_buffer_pool_pages_data、processlist_threads
2. 简单脚本监控内存趋势
#!/bin/bash
while true; do
echo "$(date) RSS: $(ps -o rss= -C mysqld)"
sleep 60
done > /var/log/mysql_memory.log
五、总结 checklist
| 步骤 | 操作 |
|---|---|
| 🔍 1 | 确认 RSS 内存是否真的异常(排除监控误差) |
| 🔍 2 | 检查 innodb_buffer_pool_size 是否过大 |
| 🔍 3 | 检查 max_connections 及各 buffer 参数 |
| 🔍 4 | 分析慢查询,优化 SQL 和索引 |
| 🔧 5 | 调整 my.cnf 参数,重启 MySQL |
| 📊 6 | 部署监控,持续观察内存趋势 |
附:推荐的基础 my.cnf 配置(适用于 4GB 内存服务器)
[mysqld]
# 基础配置
user=mysql
pid-file=/var/run/mysqld/mysqld.pid
socket=/var/run/mysqld/mysqld.sock
port=3306
# 内存优化
innodb_buffer_pool_size = 2G
innodb_buffer_pool_instances = 4
key_buffer_size = 64M
tmp_table_size = 64M
max_heap_table_size = 64M
# 连接优化
max_connections = 200
thread_stack = 192K
read_buffer_size = 128K
sort_buffer_size = 256K
join_buffer_size = 256K
# 日志
slow_query_log = 1
long_query_time = 2
log-error=/var/log/mysql/error.log
# 字符集
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
根据实际服务器内存和应用负载,按比例调整上述参数即可有效降低 MySQL 内存占用。
云知识CLOUD