解决Python连接MySQL报错:Access denied或Can't connect

Ruby
Ruby
发布时间:2026-11-03| 4人浏览过
原创

针对Python使用PyMySQL或mysql-connector连接MySQL数据库时常见的2003 Can't connect与1045 Access denied报错,提供系统性排查步骤、底层成因解释及可执行修复方案。

在 Python 中使用 PyMySQL、mysql-connector-python 或 SQLAlchemy 连接 MySQL 数据库时,最常见的阻断性错误通常分为两类:一是 Can't connect to MySQL server(错误码 2003 / 111 Connection refused),代表 TCP 连接在网络与服务端口层被拒;二是 Access denied for user(错误码 1045),代表网络通信成功建立,但在用户名、密码、来源主机或加密认证协议层被拦截。明确这两类报错的发生阶段与本质差异,是迅速定位根因、恢复业务连接的关键路径。

解决Python连接MySQL报错:Access denied或Can't connect
图示:Python 连接 MySQL 两大高频核心报错对比及受阻层级

第一部分:两类报错的判定与排查决策路径

当 Python 程序抛出 OperationalError 异常时,请不要立即盲目尝试修改密码或随意重启数据库。应当先观察异常的错误代码和伴随文本:

  • 若报错为 2003(Can't connect):问题完全发生在建立 TCP 连接的阶段,MySQL 进程可能未启动,或者请求因防火墙、IP 绑定规则未到达 MySQL 进程。此时修改数据库内的用户权限毫无意义。
  • 若报错为 1045(Access denied):客户端已经能够顺利与 MySQL 3306 端口建立双向通信,但服务端在鉴权时发现用户名不存在、密码不匹配、客户端来源 IP 不在授权主机白名单中,或者握手使用的加密插件不兼容。
解决Python连接MySQL报错:Access denied或Can't connect
图 1:Python 连接 MySQL 异常诊断与排查步骤流程

第二部分:排查并解决“Can't connect to MySQL server”

该错误在 PyMySQL 中通常呈现为类似 pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on '192.168.1.100' ([Errno 111] Connection refused)") 的提示。在 Windows 客户端下,对应的 WinError 通常为 10061 (由于目标计算机积极拒绝,无法连接)。请按照以下步骤排查:

解决Python连接MySQL报错:Access denied或Can't connect
图 2:Can't Connect 错误发生时的网络连接链路与阻断点

步骤 1:确认 MySQL 服务进程是否正在运行

如果数据库服务端进程根本没有启动,TCP 连接自然会被操作系统网络栈直接拒绝。登录 MySQL 所在服务器执行检查:

# Linux 系统下检查服务状态
sudo systemctl status mysql
# 部分发行版(如 CentOS/RHEL)服务名为 mysqld
sudo systemctl status mysqld

# 若状态为 inactive (dead) 或 failed,则启动服务
sudo systemctl start mysql
# 设置开机自启
sudo systemctl enable mysql

在 Windows 环境下,可在“服务”(services.msc)中查看 MySQL 或 MySQL80 服务是否处于“正在运行”状态。

步骤 2:检查监听地址 bind-address

这是本地能连但远程 Python 脚本连不上的最常见原因。为了安全,Ubuntu/Debian 等系统的 MySQL 默认只在回环地址 127.0.0.1 上监听,外部网络请求会被直接忽略。

首先在服务端使用命令查看 3306 端口的监听情况:

# 查看当前 3306 端口绑定的 IP 地址
sudo ss -tlpn | grep 3306
# 或者使用 netstat
sudo netstat -tlpn | grep 3306

如果输出显示的是 127.0.0.1:3306,说明只接受本机回环流量。此时需要修改 MySQL 配置文件:

  1. 打开配置文件(Ubuntu 常见于 /etc/mysql/mysql.conf.d/mysqld.cnf,CentOS 常见于 /etc/my.cnf)。
  2. 找到 bind-address 配置项,将其改为 0.0.0.0(允许所有网卡接入)或者注释掉该行:
    [mysqld]
    # 将原来的 bind-address = 127.0.0.1 修改为:
    bind-address = 0.0.0.0
  3. 保存文件后重启 MySQL 服务:sudo systemctl restart mysql。

步骤 3:排查系统防火墙与网络安全组

若 MySQL 所在主机部署在云服务器(如阿里云、腾讯云、AWS)或受本地防火墙保护,3306 端口必须被显式放行:

# Ubuntu UFW 防火墙放行 3306
sudo ufw allow 3306/tcp
sudo ufw reload

# CentOS firewalld 放行 3306
sudo firewall-cmd --zone=public --add-port=3306/tcp --permanent
sudo firewall-cmd --reload
云服务器提醒:操作系统内部放行端口后,还必须登录云平台控制台,在实例的“安全组”规则中添加允许入方向 TCP 3306 端口的策略,否则外部流量仍将在云宿主边界被直接丢弃。

步骤 4:Docker 容器化部署场景检查

如果是使用 Docker 运行的 MySQL 容器,请核实启动命令中是否包含了宿主机端口映射参数 -p 3306:3306。若未暴露端口,宿主机外部的 Python 脚本将无法触达容器内部服务;若 Python 代码与 MySQL 容器处于同一 Docker Compose 体系,应使用容器服务名称(如 db:3306)而非 127.0.0.1。

第三部分:排查并解决“Access denied for user”

当网络通路通畅后,若身份凭证或权限不匹配,MySQL 会返回类似 pymysql.err.OperationalError: (1045, "Access denied for user 'root'@'192.168.1.50' (using password: YES)") 的错误。解决此问题需要理解 MySQL 的身份鉴权体系。

解决Python连接MySQL报错:Access denied或Can't connect
图 3:Access Denied 鉴权机制:'user'@'host' 匹配与认证插件原理

核心概念:MySQL 中的账户是 'user'@'host' 组合

在 MySQL 中,用户标识是由用户名和来源主机范围(host)共同构成的。系统中的 'root'@'localhost' 与 'root'@'%' 是两个完全独立的账户定义。当 Python 脚本从外部 IP(例如 192.168.1.50)发起连接时,即使用户名和密码完全正确,MySQL 也只会寻找能匹配该来源 IP 的授权记录。如果只有 'root'@'localhost',访问就会被拒。

步骤 1:排查 using password 状态与特殊字符转义

注意观察报错括号内的详细提示:

  • (using password: NO):表示 Python 代码在建立连接时并没有传递密码参数,或者传递了空字符串。请检查连接字典或配置中是否遗漏了 password 参数。
  • (using password: YES):表示传递了密码,但密码计算出的哈希值与数据库存储不一致,或者该来源 Host 不允许以此密码登录。需要警惕特殊字符(如 $, %, #, @)在环境变量或连接字符串(如 SQLAlchemy URI)中是否被意外转义或截断。

步骤 2:在 MySQL 中检查并放行 Host 白名单

在数据库服务器本地以 root 登录,执行以下查询:

-- 查询现有用户的来源限制与加密插件
SELECT user, host, plugin FROM mysql.user;

若需要允许 Python 应用从任何外部机器访问数据库,建议为应用程序创建专用的非 root 账号,并指定 host 为 %:

-- 1. 创建允许任意远程主机连接的专用用户
CREATE USER 'appuser'@'%' IDENTIFIED BY 'SecurePassWord_2026!';

-- 2. 为该用户授予特定数据库的操作权限(请勿直接授予全局 *.* 权限)
GRANT ALL PRIVILEGES ON my_app_db.* TO 'appuser'@'%';

-- 3. 刷新权限表使其立即生效
FLUSH PRIVILEGES;

如果必须允许现有 root 账号远程访问(通常只建议在受控内网测试环境中使用),可以执行:

-- 修改现有用户的 host 为通配符 %
UPDATE mysql.user SET host = '%' WHERE user = 'root' AND host = 'localhost';
FLUSH PRIVILEGES;

步骤 3:处理 MySQL 8.0 认证插件兼容性问题

MySQL 8.0 默认将用户的身份验证插件从传统的 mysql_native_password 切换为了安全性更高的 caching_sha2_password。然而,在 Python 生态中:

  • 未安装 cryptography 依赖的 PyMySQL 在遇到 caching_sha2_password 时无法完成 RSA 密钥交换,会直接抛出 1045 错误或提示缺少加密依赖。
  • 部分老旧的连接池或环境驱动不原生支持 SHA-256 握手。

解决方案有两种:

  1. 在 Python 端补充安装加密依赖(推荐做法):
    # 安装 cryptography 库使 PyMySQL 原生支持 caching_sha2_password
    pip install cryptography
  2. 在 MySQL 服务端将该用户认证方式改为传统模式:
    -- 修改账号使用 mysql_native_password 认证插件
    ALTER USER 'appuser'@'%' IDENTIFIED WITH mysql_native_password BY 'SecurePassWord_2026!';
    FLUSH PRIVILEGES;

第四部分:编写具备自诊断能力的 Python 连接代码

在编写业务代码时,通过捕获 OperationalError 并对错误码进行模式识别,能够显著提升排查效率。以下是一份包含异常诊断的标准化连接示例:

import pymysql
from pymysql.constants import ER

db_config = {
    "host": "192.168.1.100",
    "port": 3306,
    "user": "appuser",
    "password": "SecurePassWord_2026!",
    "database": "my_app_db",
    "charset": "utf8mb4",
    "connect_timeout": 5
}

def test_mysql_connection():
    try:
        connection = pymysql.connect(**db_config)
        with connection.cursor() as cursor:
            cursor.execute("SELECT VERSION();")
            version = cursor.fetchone()
            print(f"[成功] 已成功连接到 MySQL 服务端,版本: {version[0]}")
        connection.close()
    except pymysql.err.OperationalError as err:
        err_code, err_msg = err.args[0], err.args[1]
        print(f"[连接失败] 错误代码: {err_code}")
        print(f"详细信息: {err_msg}")
        
        # 针对具体错误码输出精准诊断建议
        if err_code == 2003:
            print("--> 诊断建议:属于网络/服务层问题。")
            print("    1. 请检查目标服务器上 MySQL 服务是否处于运行状态;")
            print("    2. 检查 MySQL 配置文件中的 bind-address 是否设置为了 0.0.0.0;")
            print("    3. 检查服务器防火墙及云安全组是否放行了 3306 端口。")
        elif err_code == ER.ACCESS_DENIED_ERROR or err_code == 1045:
            print("--> 诊断建议:属于身份授权/鉴权层问题。")
            print("    1. 请核对用户名与密码拼写是否正确,检查有无特殊字符未转义;")
            print("    2. 检查 MySQL 中的用户 Host 白名单是否包含当前客户端 IP(或 '%');")
            print("    3. 若为 MySQL 8.0,尝试安装 cryptography 库或切换认证插件。")
        else:
            print(f"--> 其他操作异常,请检查配置或数据库日志。")

if __name__ == "__main__":
    test_mysql_connection()

第五部分:排查思路与修复速查全景总结

遇到报错时,按照“先通后辨”的原则推进——先保证网络端口连通,再核对用户账号与权限配置。下表与图 4 总结了高频场景的对应修复措施:

解决Python连接MySQL报错:Access denied或Can't connect
图 4:Python 连接 MySQL 故障排查与修复速查全景图
报错类别 典型报错特征 核心检查项与命令 确切修复方案
网络服务故障 (2003) [Errno 111] Connection refused
或 WinError 10061
systemctl status mysql
检查守护进程是否正在运行
执行 sudo systemctl start mysql 并设置 enable 开机启动。
绑定限制故障 (2003) 本机连接正常,外部远程连接直接被拒 ss -tlpn | grep 3306
查看监听 IP 是否为 127.0.0.1
在 my.cnf 中将 bind-address 改为 0.0.0.0 并重启服务。
防火墙拦截 (2003) 连接长时间挂起直至 Connection timed out nc -zv 目标IP 3306
检查端口物理通畅度
配置 UFW/firewalld 放行 3306 端口,并在云控制台放行安全组规则。
Host限制拦截 (1045) Access denied for user 'u'@'ip' (using password: YES) SELECT user, host FROM mysql.user; 创建或更新用户 host 为 '%':
CREATE USER 'u'@'%' IDENTIFIED BY '...';
认证加密冲突 (1045) MySQL 8.0 密码正确但提示插件或握手异常 SELECT user, plugin FROM mysql.user; 本地安装 pip install cryptography 或在服务端改为 mysql_native_password。
最终排错建议:当 Python 抛出异常时,可以在运行 Python 代码的同一台终端上,直接使用 mysql -h <host> -P 3306 -u <user> -p 命令手动发起连接。这样能够最直接地区分是 Python 驱动环境问题,还是底层的数据库网络与账户策略问题。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。