Python操作MySQL数据库时常见的三个陷阱及解决方法

 更新时间:2026年05月31日 08:29:45   作者:detayun  
这段文章详细剖析了pymysql、mysql-connector-python及SQLAlchemy在数据库操作中可能遇到的三个关键问题:异常处理不当导致数据不一致、连接池管理不当引发资源耗尽、以及批量更新操作时事件监听失效,需要的朋友可以参考下

坑一:pymysql —— 异常吞掉,钱扣了没到账

案发现场

import pymysql

conn = pymysql.connect(host='localhost', user='root', password='pwd', database='test')
cursor = conn.cursor()

# 转账:扣款
cursor.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
# 假设这里程序崩溃 / 网络超时 / 抛出异常...
# 加款永远没执行
cursor.execute("UPDATE accounts SET balance = balance + 100 WHERE id = 2")

conn.commit()  # 第一条已经生效,无法回滚

结果:用户A被扣了100块,用户B没收到钱。数据不一致,且无法挽回。

根因

pymysql 默认 autocommit=False,但每条 execute 本身不会自动提交——问题出在异常发生后,第一条 SQL 已经被 MySQL 隐式提交(因为 pymysql 在某些异常场景下会触发自动提交),或者更常见的情况是:开发者以为异常会自动回滚,但实际上没有显式调用 rollback()。

解决方法

try:
    cursor.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
    cursor.execute("UPDATE accounts SET balance = balance + 100 WHERE id = 2")
    conn.commit()
except Exception as e:
    conn.rollback()  # ← 必须显式回滚
    raise  # 或记录日志后重新抛出
finally:
    cursor.close()
    conn.close()

关键原则:try 里写业务 SQL,except 里必须有 rollback(),finally 里关闭连接。三件套缺一不可。

坑二:mysql-connector-python —— 连接池泄漏,服务跑着跑着就挂了

案发现场

import mysql.connector
from mysql.connector import pooling

dbconfig = {
    "host": "localhost", "user": "root", "password": "pwd", "database": "test"
}
connection_pool = pooling.MySQLConnectionPool(pool_name="mypool", pool_size=5, **dbconfig)

def get_user(user_id):
    conn = connection_pool.get_connection()  # 从池中取连接
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
    result = cursor.fetchall()
    # 忘记关闭连接,直接返回
    return result

结果:前5次请求正常,第6次开始报错 Too many connections。因为连接取走后没归还,池子里的5个连接被永久占用。

根因

mysql-connector-python 的连接池行为是连接常驻——取走后不会自动回收。与 SQLAlchemy 的 QueuePool(空闲超时后销毁连接)不同,这里的连接一旦泄漏,池子容量就永久减少,直到耗尽。

解决方法

def get_user(user_id):
    conn = connection_pool.get_connection()
    try:
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
        return cursor.fetchall()
    finally:
        cursor.close()
        conn.close()  # ← 必须归还连接到池中

或者用上下文管理器:

from contextlib import closing

def get_user(user_id):
    with closing(connection_pool.get_connection()) as conn:
        with conn.cursor() as cursor:
            cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
            return cursor.fetchall()
    # 退出 with 自动关闭,连接归还池中

监控指标:定期执行 SHOW PROCESSLIST,观察 Sleep 状态的连接数是否持续增长。

坑三:SQLAlchemy —— 批量更新时,事件监听器"沉默"了

案发现场

from sqlalchemy import event, update
from sqlalchemy.orm import Session

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    updated_at = Column(DateTime)

# 定义事件:每次更新自动维护 updated_at
@event.listens_for(User, 'before_update')
def set_updated_at(mapper, connection, target):
    target.updated_at = datetime.now()

# 业务代码:批量更新用户状态
session = Session()
stmt = update(User).where(User.status == 'pending').values(status='processed')
session.execute(stmt)  # ← 监听器根本没触发
session.commit()

结果:1000条记录的 status 改了,但 updated_at 全是 NULL。因为 session.execute(stmt) 走的是 Core 层,绕过了 ORM 的对象生命周期,事件监听器根本不知道发生了更新。

根因

SQLAlchemy 的事件系统绑定的是 ORM 对象的状态变化。session.execute(update(...)) 是直接执行 SQL,不经过 ORM 对象加载,所以 before_update / after_update 全部静默。

解决方法(三选一)

方案适用场景代码
回归 ORM 遍历更新数据量 < 1万for u in session.query(User).filter_by(status='pending'): u.status='processed'
SQL 里直接写追求性能values(status='processed', updated_at=func.now())
数据库触发器强一致性要求交给 MySQL 层,ON UPDATE CURRENT_TIMESTAMP

推荐大多数场景用方案二,性能和一致性兼顾:

from sqlalchemy import func

stmt = (
    update(User)
    .where(User.status == 'pending')
    .values(status='processed', updated_at=func.now())
)
session.execute(stmt)
session.commit()

三个坑的本质对比

坑一 pymysql坑二 mysql-connector坑三 SQLAlchemy
表面问题异常没回滚连接耗尽事件没触发
真实原因异常处理不完整连接池机制不同Core vs ORM 路径差异
一句话解法except 里必写 rollback()finally 里必关连接批量操作别用 Core 绕过 ORM

这三个坑覆盖了 异常流、资源管理、抽象层边界 三个维度,踩过一次就不会再踩第二次。

以上就是Python操作MySQL数据库时常见的三个陷阱及解决方法的详细内容,更多关于Python操作MySQL数据库常见陷阱的资料请关注脚本之家其它相关文章!

相关文章

  • Python PIL图片添加字体的例子

    Python PIL图片添加字体的例子

    今天小编就为大家分享一篇Python PIL图片添加字体的例子,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-08-08
  • Python web实战教程之Django文件上传和处理详解

    Python web实战教程之Django文件上传和处理详解

    Django和Flask都是Python的Web框架,用于开发Web应用程序,这篇文章主要给大家介绍了关于Python web实战教程之Django文件上传和处理的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-12-12
  • 使用tensorflow实现VGG网络,训练mnist数据集方式

    使用tensorflow实现VGG网络,训练mnist数据集方式

    这篇文章主要介绍了使用tensorflow实现VGG网络,训练mnist数据集方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-05-05
  • Python核心内置函数len()、list()、locals()实用指南解析

    Python核心内置函数len()、list()、locals()实用指南解析

    Python作为一门以简洁、易读和强大功能著称的高级编程语言,其内置函数是语言核心能力的重要体现,也是初学者快速上手、中高级开发者提升编码效率的关键基石,这篇文章主要介绍了Python核心内置函数len()、list()、locals()实用指南的相关资料,需要的朋友可以参考下
    2026-03-03
  • 解决csv.writer写入文件有多余的空行问题

    解决csv.writer写入文件有多余的空行问题

    今天小编就为大家分享一篇解决csv.writer写入文件有多余的空行问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • python制作填词游戏步骤详解

    python制作填词游戏步骤详解

    在本文里我们给大家整理了关于python制作填词游戏的具体步骤以及实例代码,需要的朋友们跟着学习下。
    2019-05-05
  • python实现dbscan算法

    python实现dbscan算法

    DBSCAN 算法是一种基于密度的空间聚类算法,本文主要介绍了python实现dbscan算法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-05-05
  • Python中实现JWT认证的完整指南(从生成到验证)

    Python中实现JWT认证的完整指南(从生成到验证)

    JSON Web Tokens (JWT) 是现代 Web 开发中广泛使用的身份验证机制,本文将用生动的方式带你全面了解 JWT 在 Python 中的实现,包括生成、验证和各种相关方法,需要的朋友可以参考下
    2025-06-06
  • 总结Python函数参数的六种类型

    总结Python函数参数的六种类型

    这篇文章主要总结了Python函数参数的六种类型,传递参数实现不同场景的灵活使用,下面总结的六种函数参数类型,需要的小伙伴可以参考一下
    2022-03-03
  • python二分法查找实例代码

    python二分法查找实例代码

    二分算法是一种效率比较高的查找算法,其输入的是一个有序的元素列表,如果查找元素包含在列表中,二分查找返回其位置,否则返回NONE,下面这篇文章主要给大家介绍了关于python二分法查找的相关资料,需要的朋友可以参考下
    2021-11-11

最新评论