背景
在 Kylin V10 Desktop(arm64) 上通过二进制包 mysql-8.0.44-linux-glibc2.28-aarch64.tar.xz 部署 MySQL,后端应用(如 SQLAlchemy)使用 TCP 方式连接 127.0.0.1:3306 时,反复抛出 Lost connection to MySQL server during query 错误,而手动使用 mysql 客户端连接 localhost 却一切正常。本文记录该问题的排查过程、根因分析与最终解决方案。
问题现象
应用日志中报错如下:
sqlalchemy/dialects/mysql/aiomysql.py", line 170, in connect
await_only(creator_fn(*arg, **kw)),
| | | -> {'host': '127.0.0.1', 'db': 'bisheng', 'user': 'root', 'password': '1234', 'port': 3306, 'charset': 'utf8mb4', 'client_flag': 2}
...
aiomysql/connection.py", line 661, in _read_bytes
raise OperationalError(CR.CR_SERVER_LOST, msg) from e
sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2013, 'Lost connection to MySQL server during query')
...
RuntimeError: Error creating DB and tables
ERROR: Application startup failed. Exiting.
核心错误码为 2013 ,即 Lost connection to MySQL server during query,表明应用无法通过 TCP 连接访问数据库。其使用的连接参数为:
python
{'host': '127.0.0.1', 'db': 'bisheng', 'user': 'root', 'password': '1234', 'port': 3306}
初步排查
1. MySQL 服务状态
bash
systemctl status mysql
服务运行正常。
2. 手动测试连接
bash
mysql -h 127.0.0.1 -P 3306 -u root -pxxx -e "SELECT 1;"
无法连接。
bash
mysql -h localhost -P 3306 -u root -pxxx -e "SELECT 1;"
连接成功。
这一差异强烈暗示:MySQL 当前仅允许 Unix socket 连接,而 TCP 连接存在异常。localhost 默认走 socket,127.0.0.1 则强制 TCP。
深入定位:MySQL 监听情况
bash
sudo netstat -nltp | grep 3306
输出:
tcp6 0 0 :::33060 :::* LISTEN 76452/mysqld
tcp6 0 0 :::3306 :::* LISTEN 76452/mysqld
结论:MySQL 只监听了 tcp6(IPv6 的 :: 通配地址),没有任何 IPv4 的 tcp 监听(如 0.0.0.0:3306)。
因此,当客户端使用 127.0.0.1(IPv4 地址)尝试连接时,系统找不到对应的 IPv4 监听端口,连接直接被拒绝或丢失。而 mysql -h localhost 能够成功是因为 MySQL 客户端默认使用 Unix socket,绕过了 TCP/IP 协议栈。
解决方案
强制 MySQL 监听 IPv4 地址。
-
编辑 MySQL 配置文件(不同系统的路径可能略有不同):
bashsudo vi /etc/mysql/mysql.conf.d/mysqld.cnf(部分环境也可能是
/etc/my.cnf或/etc/mysql/my.cnf,请根据实际情况选择。) -
在
[mysqld]段中添加或修改:ini[mysqld] bind-address = 0.0.0.0注意: 如果看到
skip-networking行,请将其注释或删除(该选项会禁用所有 TCP/IP 连接)。 -
保存并重启 MySQL:
bashsudo systemctl restart mysql -
再次检查监听端口:
bashsudo netstat -nltp | grep 3306
输出结果:
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 76452/mysqld
tcp6 0 0 :::3306 :::* LISTEN 76452/mysqld
-
验证 IPv4 连接:
bashmysql -h 127.0.0.1 -P 3306 -u root -p1234 -e "SELECT 1;"此时应能成功连接,应用启动异常也随之消失。
追问:为何 MySQL 安装后只监听了 IPv6?
从 MySQL 8.0.13 开始,当 bind_address 未显式设置时,其默认值为 *,表示 会分别创建 IPv4(0.0.0.0)和 IPv6(::)两个监听套接字 。也就是说,正常情况下使用 netstat 会同时看到 tcp 0.0.0.0:3306 和 tcp6 :::3306。
真凶:系统预置的配置文件
Kylin V10 Desktop 极有可能在以下路径之一 预置了 MySQL 或 MariaDB 的默认配置:
/etc/my.cnf/etc/mysql/my.cnf/etc/mysql/mysql.conf.d/mysqld.cnf/usr/etc/my.cnf- 编译时指定的
SYSCONFDIR下的my.cnf
这些预置文件中很可能包含了:
bind-address = ::1 或 bind-address = ::
当启动 mysqld 时,它会按照优先级顺序加载这些配置文件,从而覆盖了默认行为,最终 仅绑定了 IPv6 地址 。
用 netstat -tlnp 看到的便是仅有 tcp6 :::3306,而没有任何 IPv4 条目。
另一个小概率因素:net.ipv6.bindv6only
Linux 内核参数 net.ipv6.bindv6only 如果被设置为 1,即使 MySQL 监听了 IPv6 套接字,内核也会 禁止 IPv4 映射到 IPv6 。此时即使设置 bind-address = *,也可能无法通过 IPv4 连接,必须显式配置 0.0.0.0 创建独立的 IPv4 套接字。
不过从上述过程来看,添加 bind-address = 0.0.0.0 后问题立即解决,且没有报告其他内核异常,因此主因更大概率是配置文件覆盖,而非该内核参数。
显式设置 bind-address = 0.0.0.0 会让 MySQL 仅创建 IPv4 监听套接字 (AF_INET)。此时 netstat 会显示 tcp 0.0.0.0:3306,所有 IPv4 客户端(包括 127.0.0.1)就能顺利连接,问题解决。
当然,也可以设置为 0.0.0.0,:: 或 * 以同时支持 IPv4 和 IPv6,只是在本案例中快速恢复业务使用了最简单的方式。
愿你我都能在各自的领域里不断成长,勇敢追求梦想,同时也保持对世界的好奇与善意!