Python连接MySQL时报错,很多人第一反应是“密码是不是输错了”,但真正的原因可能出在驱动没装对、账号主机不匹配,或者MySQL 8的认证插件发生了变化。与其反复试错,不如先看清完整异常信息,再按连接层、认证层、网络层、会话层的顺序排查。这篇文章以PyMySQL为主要示例,同时兼顾mysqlclient和官方mysql-connector-python,把常见的连接报错和处理方式一次说清。

一、驱动安装与连接方式:先解决ModuleNotFoundError
如果运行脚本时直接抛出 ModuleNotFoundError: No module named 'pymysql',说明当前Python环境中还没有安装对应驱动。先执行 pip install PyMySQL。需要注意的是,包名是 PyMySQL,导入时写小写的 pymysql,这两者不是同一个值。还有不少旧项目导入的是 MySQLdb,这是 MySQL-python 的接口名称,PyMySQL 安装后并不会自动提供 MySQLdb 这个模块名,所以会继续报 No module named 'MySQLdb'。这时有两种选择:一是把 import MySQLdb 改成 import pymysql;二是安装 mysqlclient,它才是 MySQLdb 的正统编译版本。
mysqlclient 依赖系统编译工具,在 Windows 上经常出现 Microsoft Visual C++ 14.0 is required 这样的报错。如果只是做常规查询和事务操作,不建议为了装 mysqlclient 去折腾编译环境,直接使用 PyMySQL 或官方 mysql-connector-python 更省事。前者是纯 Python 实现,部署简单;后者由 Oracle 提供,支持更多 MySQL 特性。下面这段代码可以在安装 PyMySQL 后做最小连接验证。
import pymysql
conn = pymysql.connect(
host="127.0.0.1",
port=3306,
user="root",
password="your_password",
database="test_db",
charset="utf8mb4",
connect_timeout=5,
)
cursor = conn.cursor()
cursor.execute("SELECT VERSION()")
print(cursor.fetchone())
cursor.close()
conn.close()
如果这段代码能输出类似 (8.0.36,) 这样的版本号,说明驱动、端口和账号密码都通。如果这里就报错,不要把密码拼到命令行里继续试,应当保留这段脚本,使用 print 输出连接参数但不含密码,或直接阅读异常类型。
二、Access denied与认证插件:账号权限问题的两个重灾区
Access denied for user 'root'@'localhost' (using password: YES) 是最典型的认证失败提示。很多人第一反应是密码错误,但这条日志里还包含两个关键信息:用户是 root,来源主机是 localhost。MySQL 的账号体系并不只是一个用户名,而是由 user 和 host 共同组成。也就是说,root@localhost 和 root@127.0.0.1 在权限表中是两个不同的账号。当代码使用 host='127.0.0.1' 连接时,如果数据库里只有 root@localhost,就可能匹配不到账号或权限被拒绝。
处理这一问题时,可以先在 MySQL 命令行执行 SELECT user, host FROM mysql.user; 查看当前有哪些账号。如果应用从本机走 TCP 连接,建议为 127.0.0.1 或 % 创建独立账号,而不是直接开放 root。下面示例创建一个专门给 Python 应用使用的账号,并授予目标库权限。
CREATE USER 'app_user'@'%' IDENTIFIED BY 'App@123'; GRANT SELECT, INSERT, UPDATE, DELETE ON order_db.* TO 'app_user'@'%'; FLUSH PRIVILEGES;
这里使用 % 表示允许任意主机连接。如果数据库在公网环境,更安全的做法是把 % 换成应用服务器所在的内网 IP 段,例如 'app_user'@'192.168.1.%'。同时密码里如果包含 @、#、% 等字符,在 Python 的 connect 参数中直接写成字符串通常没有问题,但如果把账号密码拼进 SQLAlchemy 的数据库 URL,就需要做 URL 编码,否则会出现解析错误。
另一个高频认证报错是 Authentication plugin 'caching_sha2_password' cannot be loaded。MySQL 8 把默认认证插件从 mysql_native_password 改成了 caching_sha2_password,而旧版本的 mysqlclient、部分 Linux 发行版自带的连接库并没有实现该插件。解决办法有三种:升级驱动到新版本;给 Python 环境安装 cryptography 库,让 PyMySQL 支持该插件;或者在数据库中临时把用户改回 mysql_native_password。
ALTER USER 'app_user'@'%' IDENTIFIED WITH mysql_native_password BY 'App@123'; FLUSH PRIVILEGES;
修改认证插件会影响安全性,只建议在兼容旧客户端的过渡阶段使用。新项目尽量保持 caching_sha2_password,并使用新版驱动连接。
三、网络类报错:连接被拒、超时和库名错误的定位方法
Can't connect to MySQL server on '127.0.0.1' (111) 中的 111 表示端口被拒绝。这个问题通常不是 Python 代码引起的,而是 MySQL 服务没有启动、监听的端口不是 3306,或者 bind-address 限制只允许本地 socket。先确认服务状态,再检查端口监听。Linux 下可以用 ss -lntp | grep 3306,Windows 下用 netstat -ano | findstr 3306 查看。
如果 MySQL 只监听 127.0.0.1,那么从另一台机器连接就会被拒绝。需要修改 my.cnf 或 my.ini 中的 bind-address 为 0.0.0.0,并重启服务。云服务器还要检查安全组是否放行 3306 端口,否则服务正常但公网仍然连不上。报错码 10060 一般对应连接超时,常见于防火墙丢弃数据包;10061 则是端口拒绝,常见于服务未启动或端口不对。
除了 TCP 连接,超时参数也需要合理设置。connect_timeout 控制建立连接阶段的等待时间,read_timeout 和 write_timeout 控制后续读写的最长等待时间。如果数据库查询本身比较慢,read_timeout 设得太小会出现 Lost connection to MySQL server during query。可以在连接参数里显式指定这类超时值。
conn = pymysql.connect(
host="db.internal.ipipp.com",
port=3306,
user="app_user",
password="App@123",
database="order_db",
charset="utf8mb4",
connect_timeout=10,
read_timeout=30,
write_timeout=30,
)
Unknown database 'order_db' 报错则说明账号是通的,但目标库名不存在或拼写错误。此时可以先不传 database 参数连接,再执行 SHOW DATABASES; 查看实际库名。也可以在建表、查询前用 cursor.execute("USE order_db") 切换库。还有一类与字符集相关的异常,例如 Illegal mix of collations,通常是连接字符集与表字段字符集不一致。建议连接时统一使用 charset='utf8mb4',数据库和表也尽量使用 utf8mb4,避免 emoji 或特殊中文写入失败。
四、用异常分类和重试机制提高连接稳定性
连接失败时如果只捕获 Exception,虽然不会让程序崩溃,但会把编程错误、权限错误和网络抖动混在一起。PyMySQL 的异常体系以 MySQLError 为基类,下面细分 OperationalError、ProgrammingError、InterfaceError 等。连接超时、服务断开通常抛出 OperationalError;SQL 语法或权限问题多抛出 ProgrammingError。分开捕获可以更准确地记录日志和决定是否重试。
下面是一个带重试的连接函数。网络抖动导致的 OperationalError 可以重试,而账号密码错误这类 ProgrammingError 不应盲目重试,否则会浪费时间和锁资源。示例中只对 OperationalError 进行有限次重试,其他异常直接抛出。
import time
import logging
import pymysql
def get_connection(retries=3, delay=2):
last_error = None
for attempt in range(1, retries + 1):
try:
conn = pymysql.connect(
host="127.0.0.1",
port=3306,
user="app_user",
password="App@123",
database="order_db",
charset="utf8mb4",
connect_timeout=5,
)
logging.info("MySQL连接成功,第 %s 次", attempt)
return conn
except pymysql.err.OperationalError as exc:
last_error = exc
logging.warning("连接失败第 %s 次:%s", attempt, exc)
if attempt < retries:
time.sleep(delay * attempt)
except pymysql.err.ProgrammingError as exc:
raise RuntimeError("请检查账号、密码或SQL权限") from exc
raise last_error
实际生产环境中,频繁创建连接会带来额外开销,可以使用 DBUtils 或 SQLAlchemy 的连接池管理连接。连接池会维护一定数量的空闲连接,并自动检测连接是否被服务端断开。不过问题排查阶段仍然建议先用单次连接定位错误,确认账号和网络都正常后再切换到连接池。
最后可以按照一个固定清单检查:驱动是否安装且导入名称正确;MySQL 服务是否监听目标端口;账号的 user 和 host 是否与连接来源匹配;密码是否直接以参数传入;MySQL 8 认证插件是否需要升级驱动;目标库是否存在;charset 是否统一为 utf8mb4;云主机安全组是否放行端口。把完整报错信息和这份清单对照,绝大多数 Python 连接 MySQL 的问题都能快速解决。
Python连接MySQLMySQL报错数据库连接修改时间:2026-09-17 12:38:55