发布时间:2026-09-19 04:31 更新时间:2026-09-19 04:31 阅读量:0
连 MySQL 时看到 Can't connect to MySQL server ... (111) 或 Connection refused,和密码错、权限不足是两回事。密码错误的报错是 Access denied,而 Connection refused 说明 TCP 握手阶段就被拒绝了,根本没走到账号验证那一步。换句话说,客户端发出的包没有找到愿意接收它的服务。原因通常落在三个层面:套接字文件路径不对、MySQL 只监听了回环地址、或者中间有防火墙把端口拦掉。本文按从上到下的顺序拆一遍,帮你把排查范围一步步收窄。
MySQL 有两种连接通道,报错表现和排查方向完全不同。第一种是本地 socket 文件,Linux 上默认常在 /var/lib/mysql/mysql.sock,具体路径以 my.cnf 里 socket= 的配置和实际环境为准。用 mysql -u root -p 不带 -h 时,客户端默认走 socket;第二种是 TCP,用 -h 127.0.0.1 或指定 IP 时走网络协议,默认端口 3306。
很多人遇到的现象是:不带 -h 能进,加了 -h 127.0.0.1 就报错,或者反过来。这正好说明问题出在某一通道上,而不是账号密码。排查第一步就是确认客户端到底在连哪儿。可以用下面这条命令把 socket 路径和连接方式显式打印出来,避免靠猜:
# 查看客户端实际使用的 socket 路径与端口配置
mysql --print-defaults
显式指定 socket 连接
mysql -u root -p --socket=/var/lib/mysql/mysql.sock
显式走 TCP 回环
mysql -u root -p -h 127.0.0.1 -P 3306
如果 socket 路径对不上,客户端会报 Can't connect to local MySQL server through socket,这类报错字面上也带 refused,但根因是文件不存在或权限不对。可以先用 ls -l 看一眼 socket 文件是否真的存在,再用 mysqladmin --socket=... ping 验证服务端是否在正常响应。注意,socket 文件所在目录的权限要让 mysql 用户和连接用户都能访问,否则同样连不上。
TCP 报 refused 最常见的原因是 MySQL 只监听了 127.0.0.1,远程或容器外访问自然被拒。MySQL 的 bind-address 参数决定监听哪个地址,老版本默认可能是 127.0.0.1,新版本(如 8.0)默认多为 * 即全部地址,具体以你安装的版本和发行版配置为准。先用 ss 确认服务端到底在听哪个地址和端口:
# 查看 3306 端口的监听状态与进程
ss -lntp | grep 3306
输出示例:LISTEN 0 151 127.0.0.1:3306 ... 表示只监听回环
查看 mysqld 进程的启动参数
ps aux | grep mysqld
如果只看到 127.0.0.1:3306,说明外部连接必然 refused。这时要改配置文件,通常是 /etc/my.cnf 或 /etc/mysql/mysql.conf.d/mysqld.cnf,把 bind-address 改成 0.0.0.0 或注释掉(注释掉的行为随版本变化,建议显式写成 0.0.0.0 更可控)。改完必须重启 mysqld 才生效,注意重启会短暂中断现有连接,生产环境建议在低峰期操作。
[mysqld]
监听所有 IPv4 地址,允许外部连接
bind-address = 0.0.0.0
端口保持默认
port = 3306
改完监听地址后,还要确认用户授权范围。如果账号是 'user'@'localhost',即便端口通了,从远程连也会被拒(报错可能是 Access denied 或 host 不允许)。这一步用 SELECT user, host FROM mysql.user; 核对,把需要远程访问的账号改成对应网段,比如 'user'@'192.168.1.%'。别为了省事直接开 '%',安全边界会变薄。
监听地址没问题、账号也对,还是 refused,就要看包有没有被拦。注意一个细节:如果防火墙是 DROP 策略,客户端会超时卡住;如果是 REJECT,才会立刻返回 refused。所以“秒拒”往往指向 REJECT 规则或根本没有服务在听。先在服务端本机用 127.0.0.1 连一次,本机通、远程不通,基本就是网络层拦截。
这里要同时检查三层:本机 firewalld/iptables、云厂商安全组、以及中间的硬件或软件防火墙。以 firewalld 为例,放行 3306 并让配置立即生效:
# 查看当前放行的服务与端口
firewall-cmd --list-all
永久放行 3306/tcp 并重载
firewall-cmd --permanent --add-port=3306/tcp
firewall-cmd --reload
确认规则已生效
firewall-cmd --query-port=3306/tcp
如果用的是 iptables,规则写法与链名有关,建议先 iptables -L -n --line-numbers 看清顺序再插规则,别盲目 append 导致被前面的 DROP 覆盖。云服务器还要去控制台检查安全组入方向是否放行了 3306,安全组规则通常在实例外部生效,本机 firewalld 通了也未必能过。另外,SELinux 在某些发行版上也会影响非标准端口,若你把 MySQL 改到 3306 以外的端口,需要确认 SELinux 是否允许,具体策略以官方文档和实际环境为准。
排查顺序建议固定下来:先本机 socket 通不通,再本机 TCP 通不通,然后同网段其他机器通不通,最后外网通不通。每过一层就缩小一次范围,比反复重启服务有效得多。定位到具体层面后,再决定是改配置、调监听地址,还是补防火墙规则。改完记得用 mysqladmin -h 目标IP -u 用户 -p ping 验证一次,确认返回 mysqld is alive 才算收工。日常运维里,把监听地址、账号 host 范围和防火墙规则这三项写进部署检查清单,能省掉大量重复排障时间。
| 📑 | 📅 |
|---|---|
| systemd-journald 日志转发远程 syslog:rsyslog 对接与丢日志排查 | 2026-09-19 |
| Nginx 静态资源 404 与权限被拒排查:root、alias、try_files 的坑 | 2026-09-18 |
| Linux网卡丢包与TCP重传排查:ip -s link、ss -ti与ethtool实战 | 2026-09-18 |
| journald 日志占满 /var/log:持久化与容量限制配置 | 2026-09-18 |
| Docker容器时区不对?TZ变量与localtime挂载的正确用法 | 2026-09-17 |
| Docker容器DNS解析异常排查:resolv.conf与自定义网络 | 2026-09-19 |
| PHP-FPM 进程数怎么调:pm.max_children 与内存估算 | 2026-09-19 |
| Nginx gzip 与 Brotli 压缩配置实战:静态资源体积优化 | 2026-09-19 |
| Docker 容器内 CPU 被限流排查:cfs_quota、cpuset 与 top 显示异常 | 2026-09-19 |
| Nginx 与后端长连接调优:keepalive 与 upstream 复用 | 2026-09-20 |