Skip to content

MySQL 连接失败故障处理

连接失败是 MySQL 数据库运维中最常见的故障之一,直接影响应用的可用性。本文将详细介绍 MySQL 连接失败的常见原因、排查步骤、具体故障场景和解决方案,帮助 DBA 快速定位和解决连接问题,兼顾不同 MySQL 版本的特性差异。

连接失败的常见原因

MySQL 连接失败通常可以分为以下几类原因:

类别常见原因
网络层面网络不通、防火墙限制、DNS 解析失败、端口未开放
服务层面MySQL 服务未启动、服务崩溃、端口未监听、配置错误
认证授权用户名/密码错误、权限不足、IP 限制、SSL 配置错误
连接参数max_connections 已满、wait_timeout 超时、connect_timeout 超时
资源限制内存不足、文件句柄耗尽、磁盘空间不足、CPU 负载过高
其他原因连接被 KILL、连接被中断、版本不兼容、连接池配置错误

连接失败的排查步骤

系统性排查流程

  1. 检查客户端网络连接

    • 确认客户端与服务器之间的网络连通性
    • 检查防火墙配置,确保 3306 端口开放
    • 检查 DNS 解析是否正常
  2. 检查 MySQL 服务状态

    • 确认 MySQL 服务是否正在运行
    • 检查 MySQL 日志,查找错误信息
    • 检查 MySQL 端口是否正常监听
  3. 检查认证和授权

    • 确认用户名和密码是否正确
    • 检查用户的主机权限配置
    • 检查 SSL 配置是否正确(如果启用了 SSL)
  4. 检查连接参数和资源

    • 检查 max_connections 是否达到上限
    • 检查服务器资源使用情况(内存、CPU、磁盘)
    • 检查连接超时参数配置
  5. 检查应用层配置

    • 检查连接池配置是否正确
    • 检查应用代码中的连接逻辑
    • 检查应用服务器的资源使用情况

常用排查命令

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-hostname
sql
-- 检查 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)

排查步骤

  1. 检查 MySQL 服务是否正在运行
    bash
    systemctl status mysqld
  2. 检查 MySQL 端口是否正常监听
    bash
    ss -tlnp | grep 3306
  3. 检查防火墙是否开放 3306 端口
    bash
    firewall-cmd --list-ports | grep 3306
  4. 检查 MySQL 配置文件中的 bind-address 设置
    bash
    grep bind-address /etc/my.cnf /etc/mysql/my.cnf
  5. 检查 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)

排查步骤

  1. 确认用户名和密码是否正确
  2. 检查用户的主机权限配置
    sql
    SELECT user, host FROM mysql.user WHERE user = 'root';
  3. 检查用户的密码哈希值
    sql
    SELECT user, host, authentication_string FROM mysql.user WHERE user = 'root';
  4. 检查 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

排查步骤

  1. 检查当前连接数和最大连接数
    sql
    SHOW GLOBAL STATUS LIKE 'Threads_connected';
    SHOW GLOBAL VARIABLES LIKE 'max_connections';
  2. 查看当前连接的详细信息
    sql
    SHOW PROCESSLIST;
  3. 检查连接状态统计
    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)

排查步骤

  1. 检查连接超时参数配置
    sql
    SHOW GLOBAL VARIABLES LIKE '%timeout%';
  2. 检查网络延迟
    bash
    ping -c 10 mysql-server-ip
    mtr mysql-server-ip
  3. 检查服务器负载情况
    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)

排查步骤

  1. 检查 MySQL 服务状态
    bash
    systemctl status mysqld
  2. 查看 MySQL 错误日志,查找崩溃原因
    bash
    tail -n 100 /var/log/mysqld.log
  3. 检查服务器资源使用情况
    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

排查步骤

  1. 检查 MySQL SSL 配置
    sql
    SHOW GLOBAL VARIABLES LIKE '%ssl%';
  2. 检查 SSL 证书和密钥文件
    bash
    ls -l /path/to/ssl/certificates
    openssl verify -CAfile ca.pem server-cert.pem
  3. 检查客户端 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"。

排查过程

  1. 检查当前连接数:
    sql
    SHOW GLOBAL STATUS LIKE 'Threads_connected';
    -- 结果:Threads_connected = 1000
    SHOW GLOBAL VARIABLES LIKE 'max_connections';
    -- 结果:max_connections = 1000
  2. 查看当前连接状态:
    sql
    SHOW PROCESSLIST;
    -- 发现大量来自应用服务器的连接,状态为 "Sleep"
  3. 检查应用服务器日志,发现应用代码中存在连接泄漏问题,未正确关闭连接。

解决方案

  1. 临时增加最大连接数:
    sql
    SET GLOBAL max_connections = 2000;
  2. 重启应用服务器,释放泄漏的连接。
  3. 修复应用代码中的连接泄漏问题,确保连接正确关闭。
  4. 配置连接池,限制应用的最大连接数。
  5. 调整 MySQL 的 wait_timeout 参数,自动回收空闲连接:
    ini
    [mysqld]
    wait_timeout = 300

经验教训

  • 应用代码中必须正确管理数据库连接,避免连接泄漏
  • 配置合理的连接池参数,限制并发连接数
  • 设置合适的 wait_timeout,自动回收空闲连接
  • 监控连接数变化,及时发现异常

网络分区导致连接失败

问题描述: 某公司的 MySQL 主从复制集群,突然从库无法连接到主库,报错 "Can't connect to MySQL server on 'master-ip' (113)"。

排查过程

  1. 检查网络连通性:
    bash
    ping master-ip
    -- 无响应
    telnet master-ip 3306
    -- 连接超时
  2. 检查网络设备状态,发现主从服务器之间的交换机出现故障,导致网络分区。
  3. 检查 MySQL 主库状态,主库运行正常,但无法被从库访问。

解决方案

  1. 联系网络运维团队,修复交换机故障。
  2. 故障修复后,检查主从复制状态:
    sql
    SHOW SLAVE STATUS\G;
  3. 重启从库复制进程:
    sql
    STOP SLAVE;
    START SLAVE;
  4. 验证主从复制恢复正常。

经验教训

  • 网络故障是导致连接失败的常见原因之一
  • 定期检查网络设备状态,确保网络可靠性
  • 考虑配置多路径网络,提高网络冗余
  • 配置主从复制的监控和告警,及时发现复制异常

总结

MySQL 连接失败是 DBA 日常运维中经常遇到的问题,其原因复杂多样,涉及网络、服务、认证、资源等多个层面。解决连接失败问题需要系统性的排查方法和丰富的经验。

通过本文的介绍,DBA 可以掌握连接失败的常见原因、排查步骤、具体故障场景和解决方案,以及预防措施和最佳实践。在实际运维中,DBA 应该:

  1. 建立系统性的排查流程,从客户端到服务器端逐步排查
  2. 利用各种工具和命令,快速定位问题
  3. 采取有效的解决方案,恢复服务可用性
  4. 实施预防措施,避免类似问题再次发生
  5. 积累经验,建立故障知识库

通过不断学习和实践,DBA 可以提高处理 MySQL 连接失败问题的能力,确保数据库系统的高可用性和可靠性。