MySQL 远程访问需四步验证:先确认 bind-address=0.0.0.0 且无 skip-networking 覆盖;再用 mysql_native_password 插件创建用户;然后开放系统防火墙及云安全组 3306 端口;最后用公网 IP 测试并核对 mysql.user 表中 host 匹配。

MySQL 的 bind-address 默认只监听本地,改了也不生效?
直接改 /etc/mysql/mysql.conf.d/mysqld.cnf 里的 bind-address 为 0.0.0.0 不够——MySQL 8.0+ 默认启用了 skip-networking 的等效行为(比如 systemd 启动时加了 --skip-networking),或者配置被多个文件覆盖(mysqld.cnf、mysql.cnf、debian.cnf 都可能参与加载)。必须确认最终生效的配置:
- 运行
mysql -u root -p -e "SHOW VARIABLES LIKE 'bind_address';",看输出是不是0.0.0.0 - 检查
mysqld --help --verbose | grep "Default options"找到实际读取的配置路径 - 如果用 systemd,执行
systemctl cat mysql,留意ExecStart是否硬编码了--bind-address=127.0.0.1
授权语句写 GRANT ALL ON *.* TO 'user'@'%' 就完事了?
不行。MySQL 8.0 默认用 caching_sha2_password 插件,而旧客户端(比如某些 Python MySQLdb、老版本 PHP mysqli)不支持,连上去就报 Authentication plugin 'caching_sha2_password' cannot be loaded。授权不是光放行 IP,还得匹配认证方式:
- 创建用户时显式指定插件:
CREATE USER 'app'@'%' IDENTIFIED WITH mysql_native_password BY 'pwd123'; - 或修改已有用户:
ALTER USER 'app'@'%' IDENTIFIED WITH mysql_native_password BY 'pwd123'; -
FLUSH PRIVILEGES;必须执行,但注意:MySQL 8.0+ 大部分权限变更不需要它,唯独CREATE USER/ALTER USER后建议加上以防万一
防火墙和云服务商安全组才是真正的“第一道门”
即使 bind-address 改对了、用户也授权成功,外网还是连不上——90% 是卡在系统层或网络层。MySQL 默认端口 3306 在 Linux 上默认被 ufw 或 firewalld 拦住,云服务器(阿里云 / 腾讯云 /AWS)还有独立的安全组规则:
- Ubuntu 上检查:
sudo ufw status verbose,若状态是active,加规则:sudo ufw allow 3306 - CentOS 7+:
sudo firewall-cmd --permanent --add-port=3306/tcp && sudo firewall-cmd --reload - 云平台控制台里,找到对应实例的“安全组”,确保入方向规则放行
TCP:3306,源地址别写成0.0.0.0/0就完事,要确认是否限制了特定 IP 段(比如只允许公司出口 IP)
连接测试别只用 mysql -h 公网 IP -u user -p
这条命令看似能验证通不通,但它走的是 TCP 连接,掩盖了两个关键问题:DNS 解析失败、用户 host 匹配逻辑。MySQL 的 'user'@'host' 中的 host 是按字符串精确匹配的,% 虽然通配,但不匹配 localhost(因为 localhost 触发 socket 连接,绕过 TCP 栈):
- 从远程机器连,用
mysql -h <code> 你的公网 IP-u app -p,不是-h 127.0.0.1 - 如果应用报
Host 'x.x.x.x' is not allowed to connect,说明授权的host值不对,比如误写成'app'@'192.168.%'却想让手机热点 IP 连入 - 用
SELECT User, Host FROM mysql.user;确认记录里真有app对应的%行,且没有更具体的app行优先匹配了它
真正麻烦的永远不是改哪一行配置,而是你不知道哪一层在静默拦截——可能是 bind-address 被覆盖,可能是认证插件不兼容,也可能是安全组里一条规则没开。每改一处,都得回到对应层验证,不能假设上层 OK 了下层就自动 OK。