根本原因是localhost强制走unix socket而非tcp,需确认并统一mysql服务端、客户端及php配置中的socket路径。

localhost 连不上但 127.0.0.1 可以,不是网络不通,也不是密码错了——根本原因是 MySQL 客户端库(包括 PHP 的 mysqli 和 PDO_MySQL)对字符串 "localhost" 有硬编码逻辑:只要 host 是字面量 "localhost"(不含端口),就强制走 Unix socket,完全跳过 TCP/IP 协议栈。
这意味着你连的不是 3306 端口,而是某个 .sock 文件;如果路径不对、权限不对、或 MySQL 根本没监听那个 socket,就会直接报错,比如:
Can't connect to local MySQL server through socket '/tmp/mysql.sock'-
Connection refused(但其实是 socket 打不开,不是端口拒绝)
下面分场景说清楚怎么查、怎么改。
查 MySQL 实际监听的 socket 路径
别猜配置文件里写的路径,MySQL 启动后可能用的是编译默认值或运行时覆盖值。登录 MySQL 执行:
mysql -u root -p -e"SHOW VARIABLES LIKE 'socket';"
输出类似:
socket /usr/local/mysql/var/mysql.sock
这个路径才是真实有效的。常见错误包括:
-
php.ini里mysqli.default_socket或pdo_mysql.default_socket没配,或配成/tmp/mysql.sock(旧系统常用,但新版 MySQL 默认不用) - 路径写错斜杠方向(Windows 下反斜杠不被识别)
- 路径末尾漏了
.sock扩展名 - 多版本 MySQL 共存时,phpEnv 当前启用的 MySQL 版本和 socket 路径不匹配
验证是否 socket 路径问题最简单的方法
绕过 php.ini 配置,显式传 socket 路径测试连接:
$conn = mysqli_connect('localhost', 'root', 'pass', '', 0, '/usr/local/mysql/var/mysql.sock');
如果这样能连上,说明问题 100% 出在 php.ini 的 socket 配置没对齐。注意:
- 第 5 个参数是
port,必须传0(表示不走 TCP 端口) - 第 6 个参数才是
socket路径,不能为空字符串 - 改完
php.ini后必须重启php-fpm或 Apache/Nginx,reload不生效
为什么改了 my.cnf 的 bind-address 没用?
因为 bind-address 控制的是 TCP 监听范围,跟 localhost 走 socket 完全无关。它只影响 127.0.0.1、服务器IP 这类 TCP 连接。如果你的问题是 localhost 连不上,改 bind-address 是白忙活。
真正要检查的是:
-
skip_networking是否为ON(SHOW VARIABLES LIKE 'skip_networking';)——如果是,TCP 端口压根不监听,但 socket 不受影响 -
socket和datadir在my.cnf中是否指向同一级目录,否则 MySQL 启动时可能自己都找不到 sock 文件 - socket 文件权限:MySQL 进程用户(如
mysql)必须对该文件有读写权,PHP 进程用户(如www-data或nginx)也得有读权
不想折腾 socket?直接用 127.0.0.1 就行
这不是“退而求其次”,而是更可控的做法。尤其在开发环境:
- 避免 Unix socket 权限、路径、多版本冲突问题
- 连接行为可预测:一定是走 TCP,端口、防火墙、
bind-address全部可查可调 - MySQL 用户权限也清晰:
'root'@'localhost'和'root'@'127.0.0.1'是两个独立账号,不会因 host 解析模糊导致权限错配
真正容易被忽略的是:很多人反复修改 bind-address 或用户权限,却从不确认 PHP 实际用的是哪个 socket 路径——而那条路径,往往藏在 MySQL 启动后的运行时变量里,不是配置文件里写的那一行。











