Python使用PyMySQL连接MySQL数据库完整代码示例

星野
星野
发布时间:2026-10-20| 5人浏览过
原创

在 Python 开发中,与 MySQL 数据库交互最轻量且通用的方式之一是使用 PyMySQL。它是一个基于纯 Python 实现的驱动程序,完全兼容 Python DB-API 2.0 规范,不需要在操作系统中预先安装底层的 C 语言客户端开发包。本文通过一套系统、规范且可直接运行的代码示例,带你全面掌握从环境准备、连接参数调优、CRUD(增删改查)操作、事务回滚到连接上下文管理的全流程。

一、PyMySQL 通信架构与运作原理

为了理解代码中各对象的协作方式,我们先梳理 Python 应用程序与 MySQL 数据库服务之间的交互链路。PyMySQL 作为客户端驱动,承担了将 Python 对象序列化为 MySQL 通信报文、通过 TCP Socket 发送给服务端,并将服务端返回的原始二进制数据解码为 Python 常用数据类型(如字典、元组、字符串、日期等)的任务。

Python使用PyMySQL连接MySQL数据库完整代码示例
图 1:PyMySQL 充当应用层与数据库服务层之间的通信桥梁

二、环境依赖安装与测试数据表准备

在开始编写代码前,首先需要在 Python 运行环境中安装 PyMySQL 依赖,并在 MySQL 数据库中准备测试用的数据库和数据表。

1. 安装 PyMySQL

打开终端或命令行,执行以下 pip 命令:

pip install pymysql
注意事项

如果在 Django 或某些依赖 mysqlclient 的框架中使用 PyMySQL,可通过在项目入口处加入 pymysql.install_as_MySQLdb() 实现无缝伪装替换。

2. 准备测试数据表

在 MySQL 中创建一个名为 test_db 的数据库以及一张用于演示的学生信息表 students:

CREATE DATABASE IF NOT EXISTS test_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

USE test_db;

CREATE TABLE IF NOT EXISTS students (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL COMMENT '姓名',
    age INT NOT NULL COMMENT '年龄',
    email VARCHAR(100) DEFAULT NULL COMMENT '邮箱',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

三、建立数据库连接与核心参数解析

连接 MySQL 数据库的入口函数是 pymysql.connect()。该函数接受一系列关键配置项:

参数名 类型 示例值 说明与最佳实践
host str '127.0.0.1' MySQL 服务器主机地址。推荐使用 IP 避免本机 localhost 域名解析开销。
port int 3306 MySQL 监听端口,必须为正整数。
user str 'root' 连接数据库所用的用户名。
password str 'your_password' 数据库用户密码。
database str 'test_db' 要操作的默认数据库名称(也可写作 db)。
charset str 'utf8mb4' 字符编码。强烈建议使用 utf8mb4,完整支持 Emoji 及 4 字节特殊符号。
cursorclass Cursor pymysql.cursors.DictCursor 游标类型。默认为元组游标;指定为 DictCursor 后,查询结果以字典形式返回。
autocommit bool False 是否自动提交。生产环境下建议保持 False,由程序显式控制事务。

四、操作步骤流程图

使用 PyMySQL 进行数据库操作的标准流程分为五个核心环节:从建立连接、获取游标,到执行 SQL、处理结果与事务确认,最后安全关闭资源。

Python使用PyMySQL连接MySQL数据库完整代码示例
图 2:PyMySQL 交互的标准执行阶段与方法对应关系

五、完整的 CRUD 数据库操作代码示例

下面是一份完整的 Python 脚本,涵盖了插入(Insert)、批量插入、单条查询、全量查询、数据更新(Update)及数据删除(Delete)。可以直接保存并执行。

import pymysql
from pymysql.cursors import DictCursor

def get_connection():
    """创建并返回数据库连接对象"""
    return pymysql.connect(
        host='127.0.0.1',
        port=3306,
        user='root',
        password='your_password',     # 替换为你的实际数据库密码
        database='test_db',
        charset='utf8mb4',
        cursorclass=DictCursor,       # 查询结果映射为 Python 字典
        autocommit=False              # 关闭自动提交,显式控制事务
    )

def main():
    conn = get_connection()
    try:
        with conn.cursor() as cursor:
            # 1. 单条插入数据 (INSERT)
            print("--- 1. 执行单条插入 ---")
            insert_sql = """
                INSERT INTO students (name, age, email)
                VALUES (%s, %s, %s);
            """
            cursor.execute(insert_sql, ("张三", 20, "zhangsan@example.com"))
            # 获取刚刚自增生成的主键 ID
            new_id = cursor.lastrowid
            print(f"成功插入数据,新增记录 ID: {new_id}")

            # 2. 批量插入数据 (executemany)
            print("\n--- 2. 执行批量插入 ---")
            batch_data = [
                ("李四", 22, "lisi@example.com"),
                ("王五", 21, "wangwu@example.com"),
                ("赵六", 23, "zhaoliu@example.com")
            ]
            affected_rows = cursor.executemany(insert_sql, batch_data)
            print(f"批量插入完成,受影响行数: {affected_rows}")

            # 写入操作完成后显式提交事务
            conn.commit()
            print("事务已提交!")

            # 3. 单条查询 (fetchone)
            print("\n--- 3. 执行单条查询 ---")
            select_one_sql = "SELECT id, name, age, email, created_at FROM students WHERE id = %s;"
            cursor.execute(select_one_sql, (new_id,))
            student = cursor.fetchone()
            print("单条查询结果:", student)

            # 4. 全量查询 (fetchall)
            print("\n--- 4. 执行全量查询 ---")
            select_all_sql = "SELECT id, name, age, email FROM students WHERE age >= %s ORDER BY id ASC;"
            cursor.execute(select_all_sql, (20,))
            all_students = cursor.fetchall()
            print(f"共查询到 {len(all_students)} 条记录:")
            for s in all_students:
                print(f"  ID: {s['id']} | 姓名: {s['name']} | 年龄: {s['age']} | 邮箱: {s['email']}")

            # 5. 更新数据 (UPDATE)
            print("\n--- 5. 执行更新操作 ---")
            update_sql = "UPDATE students SET age = %s, email = %s WHERE id = %s;"
            update_rows = cursor.execute(update_sql, (25, "zhangsan_new@example.com", new_id))
            print(f"更新完成,受影响行数: {update_rows}")
            conn.commit()

            # 6. 删除数据 (DELETE)
            print("\n--- 6. 执行删除操作 ---")
            delete_sql = "DELETE FROM students WHERE name = %s;"
            del_rows = cursor.execute(delete_sql, ("赵六",))
            print(f"删除完成,受影响行数: {del_rows}")
            conn.commit()

    except pymysql.MySQLError as err:
        # 捕获异常后必须回滚未提交的脏数据
        conn.rollback()
        print(f"执行数据库操作失败,已回滚事务!错误信息: {err}")
    finally:
        # 确保连接最终被安全关闭
        conn.close()
        print("\n数据库连接已安全关闭。")

if __name__ == '__main__':
    main()

六、防范 SQL 注入与参数化查询规范

在使用 PyMySQL 时,最常见且危险的安全漏洞是采用 Python 的字符串格式化(如 f-string 或 % 操作符)手动拼接 SQL。这种做法将用户输入当做 SQL 语法的一部分解析,极易导致 SQL 注入攻击。

危险示例(严禁在生产中使用)

# 漏洞代码:若 user_input 为 "' OR '1'='1",整张表将被误删或泄露
cursor.execute(f"SELECT * FROM students WHERE name = '{user_input}'")

正确的安全规范:必须在 SQL 语句中使用 %s 占位符,并将参数以元组或列表形式作为第二个参数传递给 execute()。注意:无论数据库字段是整数、浮点数还是字符串,在 PyMySQL 的 SQL 模板中一律统一使用 %s 占位,且占位符外绝对不能加引号:

# 安全示例:PyMySQL 会自动对参数进行严格转义与类型安全映射
safe_sql = "SELECT * FROM students WHERE name = %s AND age > %s;"
cursor.execute(safe_sql, (user_name, min_age))

七、事务控制与异常回滚机制

在数据库写操作(INSERT、UPDATE、DELETE)中,数据一致性与原子性至关重要。PyMySQL 默认不会在每次 execute 后自动提交,必须调用 conn.commit() 才能将修改真正持久化到磁盘中。如果中途发生异常未做提交,连接断开后所有修改将全部丢失。

为了保障多步写操作的原子性,必须采用 try...except...finally 控制结构,当任一环节出错时立即触发 conn.rollback() 回滚。

Python使用PyMySQL连接MySQL数据库完整代码示例
图 3:事务原子性保障机制与 commit/rollback 触发逻辑

八、生产环境健壮实践:上下文管理器封装

在大型应用中,手动调用 close() 容易因疏忽导致连接或游标泄漏。借助 Python 的 contextlib 模块,我们可以将数据库连接封装为上下文管理器(Context Manager),实现出作用域时自动提交事务、发生异常时自动回滚、退出代码块时自动关闭连接的高内聚模式:

import pymysql
from pymysql.cursors import DictCursor
from contextlib import contextmanager

DB_CONFIG = {
    'host': '127.0.0.1',
    'port': 3306,
    'user': 'root',
    'password': 'your_password',
    'database': 'test_db',
    'charset': 'utf8mb4',
    'cursorclass': DictCursor,
    'connect_timeout': 10
}

@contextmanager
def db_session():
    """生产级数据库连接上下文管理器:自动管理事务与连接生命周期"""
    conn = pymysql.connect(**DB_CONFIG)
    try:
        yield conn
        conn.commit()  # 代码块正常执行结束则自动提交
    except Exception as exc:
        conn.rollback()  # 捕获任何异常自动回滚
        print(f"事务执行失败,已自动回滚: {exc}")
        raise
    finally:
        conn.close()   # 无论成功失败均自动关闭连接

# 使用方式示例:
def query_user(user_id):
    with db_session() as conn:
        with conn.cursor() as cursor:
            cursor.execute("SELECT * FROM students WHERE id = %s;", (user_id,))
            return cursor.fetchone()

九、核心配置与避坑要点总结

为便于日常开发查阅,我们将 PyMySQL 最容易踩坑的四个维度整理如下:

Python使用PyMySQL连接MySQL数据库完整代码示例
图 4:PyMySQL 核心开发规范与四项关键避坑实践

常见报错与排查建议

  • OperationalError: (1045, "Access denied for user..."):账号或密码错误,或者该用户未授权从当前主机(如 'root'@'localhost' 与 'root'@'%')登录。
  • OperationalError: (2003, "Can't connect to MySQL server..."):MySQL 服务未启动、IP 地址错误、端口被防火墙拦截,或者 MySQL 配置中 bind-address 被限制。
  • InternalError: (1366, "Incorrect string value..."):插入包含生僻字或 Emoji 文本时抛出,通常是因为连接时未指定 charset='utf8mb4' 或目标表字段编码为 utf8/latin1。

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

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 算法库。