MySQL 内存占用高是云服务器常见的问题,通常由查询缓存、连接池、缓冲池配置不当或慢查询引起。以下是系统化的排查与优化步骤:
🔍 一、查看当前 MySQL 内存占用情况
1. 查看 MySQL 进程内存使用
# 查看 mysql 进程详细内存信息
ps aux | grep mysqld
top -p $(pgrep mysqld)
# 或使用更详细的工具
htop -C -p $(pgrep mysqld)
关注 RES(常驻内存)和 VIRT(虚拟内存)列。
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';
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Created_tmp_tables';
SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';
-- 查看当前连接数及每个连接的内存估算
SELECT * FROM information_schema.processlist;
-- 查看 InnoDB 缓冲池命中率
SELECT
(1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests) * 100 AS hit_rate
FROM information_schema.global_status;
3. 使用性能模式(Performance Schema)深入分析
-- 启用 performance_schema
SET GLOBAL performance_schema = ON;
-- 查看最耗内存的会话
SELECT
PROCESSLIST_ID,
PROCESSLIST_USER,
PROCESSLIST_HOST,
SUM(TIMER_WAIT/1000000000) AS total_time_sec,
COUNT_STAR AS total_statements
FROM performance_schema.events_statements_summary_by_thread_id
GROUP BY PROCESSLIST_ID
ORDER BY total_time_sec DESC
LIMIT 10;
🧹 二、释放内存的方法
✅ 方法 1:重启 MySQL 服务(临时释放)
sudo systemctl restart mysql
# 或
sudo service mysql restart
⚠️ 注意:会导致短暂服务中断,建议在低峰期操作。
✅ 方法 2:清理查询缓存(如果开启)
-- 查看查询缓存大小
SHOW VARIABLES LIKE 'query_cache_size';
SHOW VARIABLES LIKE 'query_cache_type';
-- 清空查询缓存
FLUSH QUERY CACHE;
-- 禁用查询缓存(推荐现代 MySQL 版本)
SET GLOBAL query_cache_type = OFF;
SET GLOBAL query_cache_size = 0;
💡 MySQL 8.0+ 已移除查询缓存功能,无需处理。
✅ 方法 3:调整关键内存参数(永久优化)
编辑 /etc/my.cnf 或 /etc/mysql/my.cnf:
[mysqld]
# InnoDB 缓冲池大小(建议设为物理内存的 50%-70%)
innodb_buffer_pool_size = 1G
# 键缓冲区(仅用于 MyISAM 表,若不用可设小)
key_buffer_size = 64M
# 排序/连接缓冲区(每个连接单独分配,需控制并发)
sort_buffer_size = 2M
join_buffer_size = 2M
read_buffer_size = 1M
read_rnd_buffer_size = 1M
# 线程缓存(减少新建线程开销)
thread_cache_size = 16
# 最大连接数(根据业务调整)
max_connections = 200
# 关闭不需要的功能
query_cache_type = OFF
query_cache_size = 0
然后重启 MySQL:
sudo systemctl restart mysql
✅ 方法 4:杀死异常/空闲连接
-- 查看所有连接
SHOW FULL PROCESSLIST;
-- 杀死特定连接(替换 ID)
KILL 12345;
-- 批量杀死空闲超过 10 分钟的连接
SELECT CONCAT('KILL ', id, ';')
FROM information_schema.processlist
WHERE COMMAND = 'Sleep' AND TIME > 600;
✅ 方法 5:优化慢查询,减少内存消耗
-- 启用慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2;
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
-- 分析慢查询
mysqlsla /var/log/mysql/slow.log
对高频慢查询添加索引或重写 SQL。
📊 三、长期监控与建议
1. 使用监控工具
- Prometheus + Grafana + MySQL Exporter
- Percona Monitoring and Management (PMM)
- MySQL Enterprise Monitor
2. 定期检查项
| 检查项 | 合理范围 |
|---|---|
Innodb_buffer_pool_usage |
80%~95% |
Threads_connected |
< max_connections 的 80% |
Created_tmp_disk_tables |
越低越好 |
Handler_read_rnd_next |
过高说明全表扫描多 |
3. 架构级优化建议
- 使用 读写分离 分散负载
- 引入 Redis/Memcached 缓存热点数据
- 定期 OPTIMIZE TABLE 整理碎片
- 升级硬件或扩容云实例
✅ 总结 Checklist
| 步骤 | 操作 |
|---|---|
| 1 | 用 ps/top 确认 MySQL 内存占用 |
| 2 | 用 SHOW GLOBAL STATUS 查看内部指标 |
| 3 | 清理查询缓存或禁用 |
| 4 | 调整 my.cnf 中的内存参数 |
| 5 | 杀死空闲/异常连接 |
| 6 | 优化慢查询,添加索引 |
| 7 | 设置监控告警,持续跟踪 |
如问题仍未解决,可提供以下信息以便进一步诊断:
- MySQL 版本
- 服务器总内存 & 配置
SHOW VARIABLES LIKE '%buffer%';输出SHOW GLOBAL STATUS;中关键字段值
云服务器