外观
MySQL 连接失败故障处理
连接失败是 MySQL 数据库运维中最常见的故障之一,直接影响应用的可用性。本文将详细介绍 MySQL 连接失败的常见原因、排查步骤、具体故障场景和解决方案,帮助 DBA 快速定位和解决连接问题,兼顾不同 MySQL 版本的特性差异。
连接失败的常见原因
MySQL 连接失败通常可以分为以下几类原因:
| 类别 | 常见原因 |
|---|---|
| 网络层面 | 网络不通、防火墙限制、DNS 解析失败、端口未开放 |
| 服务层面 | MySQL 服务未启动、服务崩溃、端口未监听、配置错误 |
| 认证授权 | 用户名/密码错误、权限不足、IP 限制、SSL 配置错误 |
| 连接参数 | max_connections 已满、wait_timeout 超时、connect_timeout 超时 |
| 资源限制 | 内存不足、文件句柄耗尽、磁盘空间不足、CPU 负载过高 |
| 其他原因 | 连接被 KILL、连接被中断、版本不兼容、连接池配置错误 |
连接失败的排查步骤
系统性排查流程
检查客户端网络连接
- 确认客户端与服务器之间的网络连通性
- 检查防火墙配置,确保 3306 端口开放
- 检查 DNS 解析是否正常
检查 MySQL 服务状态
- 确认 MySQL 服务是否正在运行
- 检查 MySQL 日志,查找错误信息
- 检查 MySQL 端口是否正常监听
检查认证和授权
- 确认用户名和密码是否正确
- 检查用户的主机权限配置
- 检查 SSL 配置是否正确(如果启用了 SSL)
检查连接参数和资源
- 检查 max_connections 是否达到上限
- 检查服务器资源使用情况(内存、CPU、磁盘)
- 检查连接超时参数配置
检查应用层配置
- 检查连接池配置是否正确
- 检查应用代码中的连接逻辑
- 检查应用服务器的资源使用情况
常用排查命令
bash
# 检查网络连通性
ping mysql-server-ip
telnet mysql-server-ip 3306
nc -zv mysql-server-ip 3306
# 检查防火墙状态
# CentOS/RHEL 7+
systemctl status firewalld
firewall-cmd --list-ports
# Ubuntu/Debian
systemctl status ufw
ufw status
# 检查 MySQL 服务状态
# 系统服务方式
systemctl status mysql
systemctl status mysqld
# 进程方式
ps aux | grep mysqld
# 检查端口监听
netstat -tlnp | grep 3306
ss -tlnp | grep 3306
# 检查 DNS 解析
nslookup mysql-server-hostname
dig mysql-server-hostnamesql
-- 检查 MySQL 连接状态
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL VARIABLES LIKE 'max_connections';
-- 检查连接错误
SHOW GLOBAL STATUS LIKE 'Connection_errors%';
SHOW GLOBAL STATUS LIKE 'Aborted_%';
-- 查看当前连接
SHOW PROCESSLIST;
-- 查看用户权限
SELECT user, host FROM mysql.user;
SHOW GRANTS FOR 'username'@'host';
-- 检查 SSL 配置
SHOW GLOBAL VARIABLES LIKE '%ssl%';
SHOW SESSION STATUS LIKE 'Ssl_cipher';
-- 检查资源使用情况
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages%';
SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool_size';连接故障自动诊断脚本
bash
#!/bin/bash
# MySQL 连接故障自动诊断脚本
MYSQL_HOST="localhost"
MYSQL_PORT="3306"
MYSQL_USER="root"
MYSQL_PASS="password"
LOG_FILE="/var/log/mysql/connection_diagnosis.log"
DATE=$(date +"%Y-%m-%d %H:%M:%S")
# 初始化日志
echo "[$DATE] MySQL 连接故障诊断开始" >> $LOG_FILE
echo "====================================" >> $LOG_FILE
# 1. 检查网络连通性
echo "[$DATE] 1. 检查网络连通性" >> $LOG_FILE
if ping -c 3 $MYSQL_HOST > /dev/null 2>&1; then
echo "[$DATE] 网络连通性: 正常" >> $LOG_FILE
else
echo "[$DATE] 网络连通性: 异常" >> $LOG_FILE
fi
# 2. 检查端口监听
echo "[$DATE] 2. 检查端口监听" >> $LOG_FILE
if nc -zv $MYSQL_HOST $MYSQL_PORT > /dev/null 2>&1; then
echo "[$DATE] 端口 $MYSQL_PORT 监听: 正常" >> $LOG_FILE
else
echo "[$DATE] 端口 $MYSQL_PORT 监听: 异常" >> $LOG_FILE
fi
# 3. 检查 MySQL 服务状态
echo "[$DATE] 3. 检查 MySQL 服务状态" >> $LOG_FILE
if systemctl status mysqld > /dev/null 2>&1 || systemctl status mysql > /dev/null 2>&1; then
echo "[$DATE] MySQL 服务状态: 运行中" >> $LOG_FILE
else
echo "[$DATE] MySQL 服务状态: 未运行" >> $LOG_FILE
fi
# 4. 检查连接情况
echo "[$DATE] 4. 检查连接情况" >> $LOG_FILE
MYSQL_CONN="mysql -u $MYSQL_USER -p$MYSQL_PASS -h $MYSQL_HOST -P $MYSQL_PORT -e"
if $MYSQL_CONN "SELECT 1" > /dev/null 2>&1; then
echo "[$DATE] 数据库连接: 正常" >> $LOG_FILE
# 检查连接数
CONNECTED=$($MYSQL_CONN "SHOW GLOBAL STATUS LIKE 'Threads_connected'" | grep Threads_connected | awk '{print $2}')
MAX_CONNECTIONS=$($MYSQL_CONN "SHOW GLOBAL VARIABLES LIKE 'max_connections'" | grep max_connections | awk '{print $2}')
echo "[$DATE] 当前连接数: $CONNECTED / $MAX_CONNECTIONS" >> $LOG_FILE
# 检查连接错误
ERROR_COUNT=$($MYSQL_CONN "SHOW GLOBAL STATUS LIKE 'Connection_errors_total'" | grep Connection_errors_total | awk '{print $2}')
echo "[$DATE] 连接错误总数: $ERROR_COUNT" >> $LOG_FILE
# 检查最大连接数错误
MAX_ERROR=$($MYSQL_CONN "SHOW GLOBAL STATUS LIKE 'Connection_errors_max_connections'" | grep Connection_errors_max_connections | awk '{print $2}')
if [ $MAX_ERROR -gt 0 ]; then
echo "[$DATE] 警告: 达到最大连接数错误次数: $MAX_ERROR" >> $LOG_FILE
fi
else
echo "[$DATE] 数据库连接: 失败" >> $LOG_FILE
echo "[$DATE] 错误信息: $($MYSQL_CONN "SELECT 1" 2>&1)" >> $LOG_FILE
fi
# 5. 检查系统资源
echo "[$DATE] 5. 检查系统资源" >> $LOG_FILE
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
MEMORY_USAGE=$(free -m | awk '/Mem:/ { printf("%.2f\n", $3/$2 * 100) }')
DISK_USAGE=$(df -h / | tail -1 | awk '{print $5}' | sed 's/%//')
echo "[$DATE] CPU 使用率: ${CPU_USAGE}%" >> $LOG_FILE
echo "[$DATE] 内存使用率: ${MEMORY_USAGE}%" >> $LOG_FILE
echo "[$DATE] 根目录磁盘使用率: ${DISK_USAGE}%" >> $LOG_FILE
# 6. 检查 MySQL 错误日志
echo "[$DATE] 6. 检查 MySQL 错误日志" >> $LOG_FILE
ERROR_LOG=$($MYSQL_CONN "SHOW GLOBAL VARIABLES LIKE 'log_error'" 2>/dev/null | grep log_error | awk '{print $2}')
if [ -n "$ERROR_LOG" ] && [ -f "$ERROR_LOG" ]; then
echo "[$DATE] 错误日志路径: $ERROR_LOG" >> $LOG_FILE
echo "[$DATE] 最近 10 条错误信息:" >> $LOG_FILE
tail -n 10 $ERROR_LOG >> $LOG_FILE
fi
echo "[$DATE] MySQL 连接故障诊断结束" >> $LOG_FILE
echo "====================================" >> $LOG_FILE
echo "诊断结果已保存到 $LOG_FILE"具体故障场景与解决方案
无法连接到 MySQL 服务器
现象:
ERROR 2003 (HY000): Can't connect to MySQL server on '192.168.1.100' (111)
ERROR 2003 (HY000): Can't connect to MySQL server on '192.168.1.100' (10060)排查步骤:
- 检查 MySQL 服务是否正在运行bash
systemctl status mysqld - 检查 MySQL 端口是否正常监听bash
ss -tlnp | grep 3306 - 检查防火墙是否开放 3306 端口bash
firewall-cmd --list-ports | grep 3306 - 检查 MySQL 配置文件中的 bind-address 设置bash
grep bind-address /etc/my.cnf /etc/mysql/my.cnf - 检查 MySQL 错误日志bash
tail -n 50 /var/log/mysqld.log tail -n 50 /var/log/mysql/error.log
解决方案:
- 如果服务未运行,启动 MySQL 服务:bash
systemctl start mysqld - 如果端口未监听,检查配置文件中的 port 设置和 bind-address 设置,确保 MySQL 监听在正确的 IP 和端口上:ini
[mysqld] bind-address = 0.0.0.0 port = 3306 - 如果防火墙未开放端口,添加防火墙规则:bash
firewall-cmd --add-port=3306/tcp --permanent firewall-cmd --reload
用户认证失败
现象:
ERROR 1045 (28000): Access denied for user 'root'@'192.168.1.10' (using password: YES)
ERROR 1045 (28000): Access denied for user 'appuser'@'localhost' (using password: NO)排查步骤:
- 确认用户名和密码是否正确
- 检查用户的主机权限配置sql
SELECT user, host FROM mysql.user WHERE user = 'root'; - 检查用户的密码哈希值sql
SELECT user, host, authentication_string FROM mysql.user WHERE user = 'root'; - 检查 MySQL 错误日志中的认证错误bash
grep -i "access denied" /var/log/mysqld.log
解决方案:
- 如果用户名或密码错误,重置密码:sql
ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password'; FLUSH PRIVILEGES; - 如果主机权限问题,添加或修改用户的主机权限:sql
CREATE USER 'root'@'192.168.1.%' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.168.1.%' WITH GRANT OPTION; FLUSH PRIVILEGES; - 如果是匿名用户问题,删除匿名用户:sql
DELETE FROM mysql.user WHERE user = ''; FLUSH PRIVILEGES;
连接数达到上限
现象:
ERROR 1040 (HY000): Too many connections排查步骤:
- 检查当前连接数和最大连接数sql
SHOW GLOBAL STATUS LIKE 'Threads_connected'; SHOW GLOBAL VARIABLES LIKE 'max_connections'; - 查看当前连接的详细信息sql
SHOW PROCESSLIST; - 检查连接状态统计sql
SHOW GLOBAL STATUS LIKE 'Connection_errors_max_connections';
解决方案:
- 临时增加最大连接数sql
SET GLOBAL max_connections = 2000; - 永久修改最大连接数(在配置文件中)ini
[mysqld] max_connections = 2000 - 检查并终止空闲连接sql
SELECT id, user, host, db, command, time FROM information_schema.processlist WHERE command = 'Sleep' AND time > 3600; KILL 123; -- 替换为实际的连接 ID - 优化应用连接池配置,减少并发连接数
- 分析应用代码,避免连接泄漏
连接超时
现象:
ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 110
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (115)排查步骤:
- 检查连接超时参数配置sql
SHOW GLOBAL VARIABLES LIKE '%timeout%'; - 检查网络延迟bash
ping -c 10 mysql-server-ip mtr mysql-server-ip - 检查服务器负载情况bash
top
iostat -x 1 vmstat 1
4. 检查 MySQL 错误日志中的超时信息
```bash
grep -i "timeout" /var/log/mysqld.log解决方案:
- 调整连接超时参数ini
[mysqld] connect_timeout = 30 net_read_timeout = 60 net_write_timeout = 60 - 优化服务器性能,降低负载
- 检查并修复网络问题
- 调整应用层的连接超时配置
- 考虑使用连接池,减少连接建立的开销
MySQL 服务崩溃
现象:
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)排查步骤:
- 检查 MySQL 服务状态bash
systemctl status mysqld - 查看 MySQL 错误日志,查找崩溃原因bash
tail -n 100 /var/log/mysqld.log - 检查服务器资源使用情况bash
free -h
df -h
4. 检查 MySQL 进程是否存在
```bash
ps aux | grep mysqld解决方案:
- 重启 MySQL 服务bash
systemctl restart mysqld - 根据错误日志中的信息,修复具体问题:
- 如果是内存不足,增加服务器内存或调整 MySQL 内存配置
- 如果是磁盘空间不足,清理磁盘空间
- 如果是配置错误,修改配置文件
- 如果是数据损坏,修复或恢复数据
- 配置 MySQL 服务的自动重启机制ini
# systemd 配置(/etc/systemd/system/mysqld.service.d/restart.conf) [Service] Restart=always RestartSec=30
SSL 连接失败
现象:
ERROR 2026 (HY000): SSL connection error: SSL certificate validation failure
ERROR 2026 (HY000): SSL connection error: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed排查步骤:
- 检查 MySQL SSL 配置sql
SHOW GLOBAL VARIABLES LIKE '%ssl%'; - 检查 SSL 证书和密钥文件bash
ls -l /path/to/ssl/certificates openssl verify -CAfile ca.pem server-cert.pem - 检查客户端 SSL 配置bash
mysql --ssl-ca=ca.pem --ssl-cert=client-cert.pem --ssl-key=client-key.pem -u root -p
解决方案:
- 检查 SSL 证书和密钥文件的权限和路径bash
chmod 600 server-key.pem client-key.pem - 确保证书链完整,CA 证书有效
- 调整客户端 SSL 配置,使用正确的证书文件
- 如果不需要 SSL,可以临时禁用 SSL 连接(仅用于排查)sql
SET GLOBAL require_secure_transport = OFF;
版本差异与特性
MySQL 5.6
- 基础连接错误处理
- 有限的连接监控指标
- 不支持 Performance Schema 连接错误监控
- 不支持 sys schema
- 不支持连接控制插件
MySQL 5.7
- 增强的 Performance Schema,支持连接错误监控
- 引入 sys schema,简化连接错误分析
- 支持连接控制插件,防止暴力破解
- 增强的错误日志,包含更多连接相关错误信息
- 支持 SSL 连接强制
MySQL 8.0
- 增强的 Performance Schema,提供更多连接错误监控指标
- 增强的 sys schema,提供更多连接诊断视图
- 支持角色管理,简化权限管理
- 增强的 SSL 支持,默认使用更安全的 TLS 版本
- 支持 SHA-256 密码认证
- 增强的错误日志,包含更详细的连接错误信息
预防措施与最佳实践
配置合理的连接参数
ini
[mysqld]
# 连接参数
max_connections = 1000
max_user_connections = 500
wait_timeout = 3600
interactive_timeout = 3600
connect_timeout = 30
# 资源参数
innodb_buffer_pool_size = 8G
key_buffer_size = 256M
table_open_cache = 2000
thread_cache_size = 128
# 安全参数
skip_name_resolve = ON
require_secure_transport = ON
validate_password.policy = STRONG实施连接池
- 应用层使用连接池管理连接
- 配置合理的连接池参数(最大连接数、最小空闲连接数、超时时间)
- 定期测试连接有效性
- 监控连接池使用情况
配置监控和告警
- 监控 MySQL 连接数、连接错误数、资源使用情况
- 设置连接数告警阈值(如 max_connections 的 80%)
- 配置 MySQL 错误日志监控
- 使用监控工具(如 Prometheus + Grafana、Zabbix)
加强网络和安全配置
- 配置防火墙,限制访问 MySQL 的 IP 范围
- 使用 SSL 加密连接
- 禁用不必要的用户和权限
- 定期更新 MySQL 版本,修复安全漏洞
定期维护和优化
- 定期清理长时间空闲连接
- 定期优化数据库,减少慢查询
- 定期检查和修复表
- 定期备份数据
制定应急预案
- 制定连接失败的应急预案
- 定期演练故障恢复流程
- 建立故障知识库,记录历史故障和解决方案
案例分析
连接数突增导致服务不可用
问题描述: 某电商平台在促销活动期间,MySQL 服务器突然无法接受新连接,应用报错 "Too many connections"。
排查过程:
- 检查当前连接数:sql
SHOW GLOBAL STATUS LIKE 'Threads_connected'; -- 结果:Threads_connected = 1000 SHOW GLOBAL VARIABLES LIKE 'max_connections'; -- 结果:max_connections = 1000 - 查看当前连接状态:sql
SHOW PROCESSLIST; -- 发现大量来自应用服务器的连接,状态为 "Sleep" - 检查应用服务器日志,发现应用代码中存在连接泄漏问题,未正确关闭连接。
解决方案:
- 临时增加最大连接数:sql
SET GLOBAL max_connections = 2000; - 重启应用服务器,释放泄漏的连接。
- 修复应用代码中的连接泄漏问题,确保连接正确关闭。
- 配置连接池,限制应用的最大连接数。
- 调整 MySQL 的 wait_timeout 参数,自动回收空闲连接:ini
[mysqld] wait_timeout = 300
经验教训:
- 应用代码中必须正确管理数据库连接,避免连接泄漏
- 配置合理的连接池参数,限制并发连接数
- 设置合适的 wait_timeout,自动回收空闲连接
- 监控连接数变化,及时发现异常
网络分区导致连接失败
问题描述: 某公司的 MySQL 主从复制集群,突然从库无法连接到主库,报错 "Can't connect to MySQL server on 'master-ip' (113)"。
排查过程:
- 检查网络连通性:bash
ping master-ip -- 无响应 telnet master-ip 3306 -- 连接超时 - 检查网络设备状态,发现主从服务器之间的交换机出现故障,导致网络分区。
- 检查 MySQL 主库状态,主库运行正常,但无法被从库访问。
解决方案:
- 联系网络运维团队,修复交换机故障。
- 故障修复后,检查主从复制状态:sql
SHOW SLAVE STATUS\G; - 重启从库复制进程:sql
STOP SLAVE; START SLAVE; - 验证主从复制恢复正常。
经验教训:
- 网络故障是导致连接失败的常见原因之一
- 定期检查网络设备状态,确保网络可靠性
- 考虑配置多路径网络,提高网络冗余
- 配置主从复制的监控和告警,及时发现复制异常
总结
MySQL 连接失败是 DBA 日常运维中经常遇到的问题,其原因复杂多样,涉及网络、服务、认证、资源等多个层面。解决连接失败问题需要系统性的排查方法和丰富的经验。
通过本文的介绍,DBA 可以掌握连接失败的常见原因、排查步骤、具体故障场景和解决方案,以及预防措施和最佳实践。在实际运维中,DBA 应该:
- 建立系统性的排查流程,从客户端到服务器端逐步排查
- 利用各种工具和命令,快速定位问题
- 采取有效的解决方案,恢复服务可用性
- 实施预防措施,避免类似问题再次发生
- 积累经验,建立故障知识库
通过不断学习和实践,DBA 可以提高处理 MySQL 连接失败问题的能力,确保数据库系统的高可用性和可靠性。
