为远程访问单独创建数据库账户后,客户端却始终无法建立连接,这个问题的表现多种多样:有的直接提示 Access denied for user,有的提示 Can't connect to MySQL server on 主机地址,还有的是连接建立后长时间无响应。出现这些情况时,不要只把注意力放在密码是否输入正确上,因为远程连接涉及监听地址、账户主机范围、认证插件和网络策略等多个环节,任何一环配置不当都会导致失败。

本文以 MySQL 为例梳理完整的排查顺序。MariaDB、Percona Server 以及部分兼容 MySQL 协议的数据库,在账户体系和网络行为上大致相同,因此排查方法也可以复用。
一、先确认数据库服务是否监听了可远程访问的地址
MySQL 默认安装后,出于安全考虑通常只监听 127.0.0.1,也就是只有本机上的程序可以连接。远程客户端发起请求时,数据包到达服务器的网卡后,如果服务没有在对应网卡上监听,连接会被直接拒绝或显示无法连接。检查这个配置最简单的方式是查看 MySQL 配置文件中的 bind-address 参数。
在 Ubuntu/Debian 系统中,配置文件通常位于 /etc/mysql/mysql.conf.d/mysqld.cnf;在 CentOS/RHEL 中可能是 /etc/my.cnf 或 /etc/my.cnf.d/ 下的文件。打开配置后,如果看到 bind-address = 127.0.0.1,就需要把它改成 0.0.0.0 表示监听所有 IPv4 地址,或者改成服务器的内网网卡 IP。改完后需要重启 MySQL 服务才会生效。
除了 bind-address,MySQL 8.0 还提供了 mysqlx-bind-address 参数,它控制 X Plugin 使用的监听地址。如果只是普通的 3306 端口连接,只需要关注 bind-address。但如果你使用了 MySQL Shell 或 X Protocol,也要一并进行调整。修改配置后,可以用 ss 或 netstat 命令确认监听地址是否已经变成 0.0.0.0:3306。
# 查看 3306 端口监听情况 sudo netstat -tlnp | grep 3306 # 如果 netstat 未安装,可使用 ss sudo ss -tlnp | grep 3306
二、账户的 host 范围必须覆盖远程来源 IP
MySQL 中的用户身份由用户名和允许登录的主机共同确定,也就是说 appuser@localhost 和 appuser@192.168.1.5 是两个不同的账户。创建远程访问账户时,如果只创建了 appuser@localhost,或者 host 写成了与客户端实际来源不一致的 IP,就会出现密码正确但依然 Access denied 的情况。
例如,应用服务器通过公网连接云数据库,出口 IP 是 203.0.113.10,但管理员创建账户时写成 CREATE USER 'appuser'@'10.0.0.8' IDENTIFIED BY '密码'; 那么来源 IP 不匹配,MySQL 会直接拒绝。此时应该先确认客户端的真实出口 IP,再针对该 IP 创建账户,或者在测试阶段使用 % 作为 host,允许所有主机连接。正式环境不建议长期使用 %,因为这会扩大攻击面。
查看已有账户的 host 范围,可以查询 mysql.user 表。需要注意,如果同一个用户名存在多个 host 条目,MySQL 会优先匹配更精确的 host。比如同时存在 appuser@'%' 和 appuser@'192.168.1.20',来自 192.168.1.20 的连接会使用后者的权限。创建完成后还要确认授权语句中的数据库名称拼写正确,避免把权限授给了 testdb,而连接时使用的是 appdb。
-- 查看指定用户允许从哪些主机登录 SELECT user, host FROM mysql.user WHERE user = 'appuser'; -- 创建仅允许从指定 IP 访问的用户 CREATE USER 'appuser'@'203.0.113.10' IDENTIFIED BY 'StrongPass!2024'; GRANT SELECT, INSERT, UPDATE ON appdb.* TO 'appuser'@'203.0.113.10'; FLUSH PRIVILEGES;
三、认证插件不兼容是远程连接失败的常见原因
MySQL 8.0 默认使用 caching_sha2_password 作为认证插件,这个插件安全性更高,但部分旧版客户端驱动、PHP 扩展或可视化工具并不支持。此时连接错误信息里往往会出现 caching_sha2_password 字样,例如 Authentication plugin caching_sha2_password cannot be loaded。解决办法有两种:升级客户端到支持该插件的版本,或者将远程账户改为 mysql_native_password。
如果团队中使用的客户端版本较旧,短时间内无法统一升级,比较快速的做法是只对远程账户修改认证插件。执行 ALTER USER 时指定 IDENTIFIED WITH mysql_native_password BY '新密码'; 就可以让该账户兼容旧客户端。需要注意的是,MySQL 8.4 和部分云数据库已经逐步移除或默认关闭 mysql_native_password,这种情况下只能升级客户端驱动,否则无法连接。
此外,像 PHP 的 mysqli 扩展如果使用系统自带的 libmysqlclient,官方建议安装 mysqlnd 驱动。对于 Python 开发者,PyMySQL 和 mysql-connector-python 较新版本均已支持 caching_sha2_password。对于 Java 项目,应使用 mysql-connector-j 8.0 以上版本。驱动问题在排查时很容易被忽略,但它和账户权限问题一样都会表现为认证失败。
-- 查看用户当前使用的认证插件 SELECT user, host, plugin FROM mysql.user WHERE user = 'appuser'; -- 将远程用户修改为传统认证插件 ALTER USER 'appuser'@'203.0.113.10' IDENTIFIED WITH mysql_native_password BY 'StrongPass!2024'; FLUSH PRIVILEGES;
四、防火墙和安全组需要同时放行数据库端口
云服务器上的数据库无法远程访问,还有一个非常典型的原因是网络策略拦截。云环境通常存在两层防护:第一层是云平台提供的安全组,第二层是操作系统内部的防火墙,如 firewalld、ufw 或 iptables。管理员有时只在安全组中放行了 3306 端口,却忘记本机 firewalld 仍然处于开启状态,导致远程连接仍然被丢弃。
排查这类问题时,可以先从客户端测试端口是否可达。使用 nc 或 telnet 连接数据库主机的 3306 端口,如果连接失败或超时,说明请求没有到达 MySQL 服务。此时应依次检查云安全组入方向规则、服务器本机防火墙状态以及数据库监听地址。若使用云数据库产品,还需要确认是否开启了公网访问,很多云数据库默认只提供内网地址。
如果确认是本机防火墙原因,以 firewalld 为例,可以执行 firewall-cmd --permanent --add-port=3306/tcp 放行端口,再执行 firewall-cmd --reload 生效。对于 ufw,可以使用 ufw allow 3306/tcp。放行时尽量将来源限制为应用服务器所在的 IP 或网段,不建议对 0.0.0.0/0 完全开放数据库端口。
# 查看防火墙已放行的端口 sudo firewall-cmd --list-ports # 永久放行 3306 端口 sudo firewall-cmd --permanent --add-port=3306/tcp sudo firewall-cmd --reload # 从客户端测试端口是否可达 nc -zv 203.0.113.10 3306
五、根据错误信息快速定位问题层次
远程数据库连接失败时,错误信息大致可以分为两类:认证失败和网络不可达。如果客户端返回 Access denied for user,说明 TCP 连接已经建立,MySQL 服务也响应了请求,问题一般出在账户的 host 范围、密码或认证插件上。如果返回 Can't connect to MySQL server on 主机地址,或者提示连接超时,问题更可能出在监听地址、防火墙、安全组或云数据库公网访问上。
因此,建议在出现远程连接问题时,先不要盲目修改密码,而是按照监听地址、账户 host、认证插件、网络策略这个顺序逐层排查。每一步都可以用简单的命令快速验证:ss 查看监听,SELECT user,host 查看账户范围,SELECT user,host,plugin 查看认证方式,nc 测试端口可达性。定位到具体层面后再动手修改,效率会高很多。
如果以上步骤全部检查完毕仍然无法连接,可以查看数据库服务端的错误日志。MySQL 的错误日志通常位于 /var/log/mysql/error.log 或 /var/lib/mysql/主机名.err,里面会记录认证失败、连接被拒绝以及插件相关的详细原因。云数据库则可以在控制台查看错误日志和审计日志,结合客户端返回的错误码进一步分析。