
我管理着数十台部署于香港的数据中心的Linux服务器。最近,我们的几台主打跨境业务的服务器,频繁出现 MySQL 服务宕机的问题。几乎每天凌晨 2 点到 4 点之间,应用团队就会在群里“狂轰滥炸”:连接超时、服务无响应、订单处理失败。
起初我以为是内存不足、恶意攻击或MySQL配置错误,但排查后发现事情并不简单。本文将用实战方式带你走一遍我排查并彻底解决问题的全过程,涉及系统配置、MySQL日志分析、磁盘IO监控、硬件瓶颈等关键细节。希望你在面对类似问题时不再一头雾水。
一、初始环境与服务器配置
我们的问题服务器配置如下:
- 服务器位置:香港沙田数据中心
- 操作系统:CentOS 7.9 (Kernel 3.10.0-1160)
- MySQL版本:5.7.44
硬件配置:
- CPU:Intel Xeon E5-2690 v4 @ 2.60GHz(共14核28线程)
- 内存:64GB DDR4 ECC
- 硬盘:2 × 1TB SSD(RAID 1)
- 网络带宽:1Gbps 专线
服务器部署了以下主要服务:
- Nginx(反向代理)
- PHP-FPM(Laravel应用后端)
- MySQL(单机主库)
二、现象分析与初步排查
1. 问题现象
/var/lib/mysql 中频繁生成 .err 文件;
mysqld 在凌晨时段意外退出;
查看日志:Out of memory: Kill process xxxx (mysqld);
错误代码:Got signal 11 (Segmentation fault)。
2. 系统日志关键内容
dmesg | grep -i kill
[123456.789123] Out of memory: Kill process 1532 (mysqld) score 984 or sacrifice child
说明内核 OOM Killer 介入,直接干掉了 MySQL。我们首先锁定内存问题。
三、精准定位:内存、配置还是硬盘?
1. 分析 MySQL 内存使用
查看 MySQL 启动参数(位于 /etc/my.cnf):
innodb_buffer_pool_size = 48G
query_cache_size = 0
tmp_table_size = 512M
max_connections = 1000
问题出在 innodb_buffer_pool_size:设置了 48G,加上连接数一多,MySQL高峰期总共占用内存接近 60G,严重超出了实际可用空间(系统自身 + 其他进程也要内存)。
我们用如下 SQL 分析内存消耗:
SHOW ENGINE INNODB STATUS;
SHOW STATUS LIKE 'Max_used_connections';
结果:
- Max_used_connections = 856
- 单个连接平均消耗约 4~6MB,856 × 6MB ≈ 5GB
- 加上 buffer_pool,MySQL 实际内存消耗已经超出64GB物理内存。
✅ 解决方法:
- 将 innodb_buffer_pool_size 从 48G 降为 32G
- 配置连接池控制:使用 ProxySQL 或 PHP-FPM 连接复用
- 设置 max_connections = 300
四、隐藏杀手:IO 瓶颈与 SSD 性能退化
在优化内存后,崩溃频率降低了,但凌晨仍偶有 MySQL 停止响应现象。
1. 使用 iostat 监控磁盘IO
iostat -x 1 10
结果显示:
Device: %util await
sda 98.5 305.12 ms
- %util 高达 98.5%
- await 超过 300ms
SSD明显已经过载。我们怀疑是表的临时文件和 binlog 导致磁盘IO飙升。
2. 分析 binlog 和临时表写入量
SHOW VARIABLES LIKE 'log_bin';
SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';
结果显示:
- Binlog 每日写入量超过 30GB
- 每小时创建 5000+ 个磁盘临时表
- 这说明磁盘写入压力很大。
✅ 解决方法:
- 增加 tmp_table_size 和 max_heap_table_size 至 256M,减缓磁盘临时表生成
- 清理长时间未清理的 binlog:设置 expire_logs_days = 3
- 升级硬盘:将原 RAID 1 升级为 RAID 10,换用企业级 Samsung PM983 NVMe SSD
五、进阶优化:线程池与慢查询管理
1. 启用 MySQL 原生线程池(企业版功能)
如果你使用的是 Percona Server 或 MySQL 企业版:
thread_handling = pool-of-threads
我们改用了 Percona Server 5.7,支持线程池,有效防止连接风暴导致系统资源耗尽。
2. 开启慢查询日志 + pt-query-digest 分析
slow_query_log = 1
long_query_time = 1
log_queries_not_using_indexes = 1
每天用 Percona Toolkit 工具分析:
pt-query-digest /var/log/mysql/mysql-slow.log > slowreport.txt
找出最耗资源的 SQL,例如:
SELECT * FROM orders WHERE status = 'pending' ORDER BY created_at DESC;
优化方式:
- 添加合适索引:CREATE INDEX idx_status_created_at ON orders(status, created_at);
- 使用分页时避免 OFFSET:改为使用 WHERE id < ? LIMIT 20
六、网络层问题:TCP连接泄漏
在分析过程中,我们还发现部分 MySQL 连接并未关闭,导致连接堆积。
1. 使用 netstat 分析连接状态
netstat -anp | grep 3306 | grep ESTABLISHED | wc -l
发现即使无请求时仍有大量 ESTABLISHED 连接。
✅ 解决方法:
在 PHP-FPM 层使用 PDO 持久连接连接池
调整 MySQL 配置:
wait_timeout = 120
interactive_timeout = 120
配合应用层连接池中间件如 ProxySQL,实现连接复用
七、稳定运行,源于系统级理解
经过这轮深入排查与优化,MySQL 服务已经连续运行 30 天无宕机,系统平均负载从原先的 6~8 降至 1.5 左右。我们的经验教训是:
问题往往不是单点,而是多个瓶颈叠加;
不要“拍脑袋”优化,要用数据说话;
学会阅读系统日志和使用性能工具是运维的核心竞争力。











