Hatanezumi - 技术
type: channel
link: https://hatanezumi.chunshengserver.cn/index.php/tag/%E6%8A%80%E6%9C%AF/
items: [技术]并发导致的数据库操作错误

[技术]并发导致的数据库操作错误
type: item
date: 2025-07-08T00:33:00+08:00
description: 我最近在编写一个fastapi的后端项目，前端在使用的时候经常会出现500的错误，在查看日志后发现是数据库的问题pymysql.err.InternalError: Packet sequence number wrong - got 1 expected 2这是一个并发导致的数据库处理错误，下面是数据库处理的代码class DataBaseProcess: '''自动数据库处理''' def __init__(self, connecter: str): self.engine = create_engine(connecter) if TEST: Base.metadata.create_all(self.engine) # type: ignore # 自动创建表结构 self.DBsession = sessionmaker(bind=self.engine) def __enter__(self): self.session = self.DBsession() return self.session def __exit__(self, exc_type, exc_value, traceback): if exc_type: self.session.rollback() else: self.session.commit() self.session.close()逻辑很简单，使用python的上下文管理来调用这个实例，在进入的时候实例化一个session并返回，在退出的时候关闭session。在业务逻辑中我是这么使用的class Processer: def __init__(self) -> None: self.database = DataBaseProcess(SQL_CONNECTER) ... def __get_editor_data(self, update_editor: str) -> EditorActivatily|None: ''' 获取活跃作者数据库数据 Parameters: update_editor: 作者名 Returns: - 若存在返回EditorActivatily - 若不存在返回None ''' with self.database as session: ...发现问题了吗，我将DataBaseProcess实例化后存储了起来，每次调用的都是同一个实例，然而在DataBaseProcess中每次进入上下文时都会重新实例化一个session，而为了在退出时释放session我将其保存在了self.session没错，如果是都是同步处理的情况下确实没有什么问题，但是fastapi会并发的处理所有的请求，这就导致了上一个session还未处理完新的session就被覆盖到了self.session中，而上一个在退出时就会错误的释放新的session。这其实是挺低级的错误，因为我在最初编写时保持着尽量减少实例化的操作来优化代码，然后因为疏忽就导致了这个问题，解决起来也非常简单只需要在每次调用时都重新实例化就行，当然也可以写一个队列来处理，但是这样会导致响应速度的下降。上面的业务逻辑就可以这么修改class Processer: def __init__(self) -> None: ... def __get_editor_data(self, update_editor: str) -> EditorActivatily|None: ''' 获取活跃作者数据库数据 Parameters: update_editor: 作者名 Returns: - 若存在返回EditorActivatily - 若不存在返回None ''' with DataBaseProcess(SQL_CONNECTER) as session: ...