ERROR 2002本质是连接层失败,非账号密码问题;它发生在TCP握手或Unix socket打开阶段,需优先确认mysqld进程真实运行、端口监听、socket路径一致及localhost/127.0.0.1路径差异。

SQLSTATE[HY000] [2002] 本质是连接层失败,不是账号或密码问题
这个错误码明确指向「连接建立阶段失败」,和用户名、密码、数据库名、权限都无关。它发生在 TCP 握手或 Unix socket 打开环节,一旦报这个错,改密码、加权限、换库名全是白忙。关键判断依据是错误码开头的 2002——MySQL 官方定义里,所有 2002 错误都属于“Can't connect to local MySQL server”,即服务没通上。
先确认 MySQL 服务是否真在运行,别信 systemctl status 的“active (running)”
Linux 上 systemctl status mysql 显示 active,不代表 mysqld 进程真在监听。常见假象:服务启动了但崩溃退出、端口被其他进程占了、配置文件语法错误导致加载失败却没报错。
- 执行
sudo ss -tlnp | grep :3306,看有没有 mysqld 进程绑定到0.0.0.0:3306或127.0.0.1:3306 - 如果没输出,再试
sudo ps aux | grep mysqld,确认进程是否存在;若存在但没监听端口,大概率是 my.cnf 里写了skip-networking或bind-address = 127.0.0.1却没开 socket - Mac 用户用 MAMP/XAMPP,直接检查
/Applications/MAMP/tmp/mysql/下是否有mysql.sock文件,没有就说明 mysqld 没真正跑起来
localhost 和 127.0.0.1 在 PHP 里走的是完全不同的路径
PHP 的 PDO 和 mysqli 对 localhost 有特殊处理:默认强制走 Unix socket,不走 TCP。而 127.0.0.1 强制走 TCP。所以你把配置从 localhost 改成 127.0.0.1 后“突然能连”,不是网络通了,而是绕过了 socket 路径问题。
- 查当前 socket 路径最可靠方式:运行
php -i | grep 'mysql.default_socket\|pdo_mysql.default_socket\|mysqli.default_socket' - 对比 MySQL 实际 socket 路径:
mysql -e "SHOW VARIABLES LIKE 'socket';"(需能命令行登录) - 两者必须一致。不一致时,要么改 PHP 配置里的
pdo_mysql.default_socket,要么改 MySQL 的my.cnf中socket = /xxx/mysql.sock,不能只改一半 - ThinkPHP 的配置里如果显式写了
'unix_socket' => '/path/to/mysql.sock',它会覆盖 php.ini 设置,优先级更高
PHP 8.0+ 下这个错更容易“静默变致命”
PHP 8.0 把 PDO 默认错误模式改成 PDO::ERRMODE_EXCEPTION,以前 new PDO() 失败只返回 false,现在直接抛 PDOException。如果你代码里还留着 @new PDO(...),@ 在 PHP 8 里压不住异常,结果就是白屏 + 日志里只有那句 SQLSTATE[HY000] [2002],连堆栈都没有。
立即学习“PHP免费学习笔记(深入)”;
- 临时调试时,在入口加
ini_set('display_errors', '1'); error_reporting(E_ALL);,确保错误不被吞 - 不要依赖 @ 抑制连接错误,改用 try/catch 包住
new PDO(),并在 catch 里打印$e->getMessage()和$e->getCode() - 检查 php.ini 是否禁用了
pdo_mysql扩展:php -m | grep pdo_mysql必须有输出
最容易被忽略的一点:Docker 容器里跑 PHP 和 MySQL 分属不同网络命名空间时,localhost 对 PHP 来说指的是容器自己的 loopback,不是宿主机的 MySQL。此时必须用服务名(如 mysql)或宿主机网关 IP(如 172.17.0.1),而不是 localhost 或 127.0.0.1。



















