“localhost” 惹的祸:一次 500 错误排查,彻底搞懂 MySQL 的 socket 与 TCP 连接
一、事故现场
把 Flask 项目部署到云服务器,前端一登录就 500,日志只有一句话:
1 | sqlalchemy.exc.OperationalError: (2002, "Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)") |
很多新手看到 2002 就以为是密码错了,结果改了半天 root 密码依旧 500。
真相:MySQL 客户端根本连不到“本地 socket 文件”,和账号密码半毛钱关系都没有。
二、MySQL 的两种连接方式
| 方式 | 触发条件 | 传输通道 | 典型错误 |
|---|---|---|---|
| Unix Socket | host 被解析为 localhost(字面量) |
本地文件系统下的 *.sock |
(2002, 'No such file or directory') |
| TCP/IP | host 是 127.0.0.1 或真实 IP |
本机 3306 端口或网络端口 | (2003, 'Connection refused') |
划重点:
在 Linux 里localhost≠127.0.0.1,前者优先走 socket,后者强制走 TCP。
三、为什么会找不到 socket 文件?
- MySQL 没启动 → 根本没有 sock 文件。
- 启动了,但路径变了
- CentOS 默认
/var/lib/mysql/mysql.sock - Ubuntu 默认
/var/run/mysqld/mysqld.sock - 自己编译的可能放在
/tmp/mysql.sock
- CentOS 默认
- 套接字目录权限不足(AppArmor、SELinux)。
- 用了 Docker / 宝塔 / 云镜像,路径被魔改。
四、四步定位法(复制即可用)
1 | # 1. 看服务 |
五、三种修复姿势
① 懒人法(最推荐)
把 SQLAlchemy URI 里的 localhost 直接换成 127.0.0.1,强制走 TCP,99% 立即复活。
1 | SQLALCHEMY_DATABASE_URI = 'mysql://root:123456@127.0.0.1:3306/tableName' |
② 指定 socket 文件(想保持 localhost)
1 | SQLALCHEMY_DATABASE_URI = ( |
③ 建软链接(应急)
1 | sudo mkdir -p /var/lib/mysql |
重启 Flask,世界清净。
六、远程数据库额外 checklist
如果 MySQL 在别的机器,URI 写成真实 IP 后还 2003,请检查:
- my.cnf 里
bind-address = 0.0.0.0 - 授权
root@'%'1
2
3CREATE USER 'root'@'%' IDENTIFIED BY '123456';
GRANT ALL ON *.* TO 'root'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES; - 云安全组 / 防火墙放行 3306。
七、一张图总结(保存即可)
1 | localhost ──→ 找 /var/lib/mysql/mysql.sock |
八、彩蛋:如何永久避免
- 团队约定:配置模板统一用
127.0.0.1,减少环境差异。 - Docker 化:MySQL 与 Flask 都跑容器,用
--network内网 IP,彻底告别 socket。 - CI 检测:在 GitHub Action / Jenkins 里加一步“启动后能否连库”,提前暴露 2002/2003。
九、结语
500 错误不一定是代码 bug,有时是“连接方式”没对齐。
记住:localhost 走 socket,127.0.0.1 走 TCP,看到 2002 先找 sock 文件,改完重启服务,问题基本烟消云散。
把这篇转给正在踩坑的同事,节省他一下午时间 🙂
