“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 里 localhost127.0.0.1,前者优先走 socket,后者强制走 TCP。


三、为什么会找不到 socket 文件?

  1. MySQL 没启动 → 根本没有 sock 文件。
  2. 启动了,但路径变了
    • CentOS 默认 /var/lib/mysql/mysql.sock
    • Ubuntu 默认 /var/run/mysqld/mysqld.sock
    • 自己编译的可能放在 /tmp/mysql.sock
  3. 套接字目录权限不足(AppArmor、SELinux)。
  4. 用了 Docker / 宝塔 / 云镜像,路径被魔改。

四、四步定位法(复制即可用)

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 看服务
sudo systemctl status mysqld || systemctl status mysql

# 2. 找真实 sock
sudo netstat -ln | grep -E 'mysql.*sock'
# 或
mysql -uroot -p -e "show variables like 'socket';"

# 3. 能否 TCP 登录
mysql -uroot -p -h127.0.0.1 -P3306

# 4. 能否 socket 登录
mysql -uroot -p -hlocalhost # 如果这条挂,说明 socket 路径不对

五、三种修复姿势

① 懒人法(最推荐)

把 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
2
3
4
SQLALCHEMY_DATABASE_URI = (
'mysql://root:123456@localhost/tableName'
'?unix_socket=/tmp/mysql.sock' # 换成第 2 步查到的路径
)

③ 建软链接(应急)

1
2
sudo mkdir -p /var/lib/mysql
sudo ln -s /tmp/mysql.sock /var/lib/mysql/mysql.sock

重启 Flask,世界清净。


六、远程数据库额外 checklist

如果 MySQL 在别的机器,URI 写成真实 IP 后还 2003,请检查:

  1. my.cnf 里 bind-address = 0.0.0.0
  2. 授权 root@'%'
    1
    2
    3
    CREATE USER 'root'@'%' IDENTIFIED BY '123456';
    GRANT ALL ON *.* TO 'root'@'%' WITH GRANT OPTION;
    FLUSH PRIVILEGES;
  3. 云安全组 / 防火墙放行 3306。

七、一张图总结(保存即可)

1
2
3
4
5
6
7
localhost  ──→ 找 /var/lib/mysql/mysql.sock
├─ 文件存在 → 连接成功
└─ 文件不存在 → 2002 错误

127.0.0.1 ──→ 走 TCP 3306
├─ 端口通 → 连接成功
└─ 端口拒 → 2003 错误

八、彩蛋:如何永久避免

  • 团队约定:配置模板统一用 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 文件,改完重启服务,问题基本烟消云散。

把这篇转给正在踩坑的同事,节省他一下午时间 🙂