数据库连接问题:sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2006, "MySQL server has gone away")报错
1、情形:在系统首次进入时,会显示“系统处理异常”的弹框,但是刷新页面后又正常显示了。
2、查看日志部分如下:
in do_execute cursor.execute(statement, parameters) File "/home/jiafu/.local/lib/python3.6/site-packages/pymysql/cursors.py", line 148, in execute result = self._query(query) File "/home/jiafu/.local/lib/python3.6/site-packages/pymysql/cursors.py", line 310, in _query conn.query(q) File "/home/jiafu/.local/lib/python3.6/site-packages/pymysql/connections.py", line 547, in query self._execute_command(COMMAND.COM_QUERY, sql) File "/home/jiafu/.local/lib/python3.6/site-packages/pymysql/connections.py", line 814, in _execute_command self._write_bytes(packet) File "/home/jiafu/.local/lib/python3.6/site-packages/pymysql/connections.py", line 760, in _write_bytes CR.CR_SERVER_GONE_ERROR, "MySQL server has gone away (%r)" % (e,) sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2006, "MySQL server has gone away (BrokenPipeError(32, 'Broken pipe'))") [SQL: SELECT count(alarm_msg.id) AS count_1 FROM alarm_msg] (Background on this error at: https://sqlalche.me/e/14/e3q8) 2025-08-16 09:22:27,494 INFO get_alarm_all Session is active: True
报错情况: sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2006, "MySQL server has gone away") 是一个非常典型的数据库连接问题。
3、问题根源分析
这个错误的核心意思是:您的 Python 应用程序尝试使用一个已经失效的数据库连接。
发生这种情况最常见的原因是 连接超时。具体流程如下:
- 建立连接:您的应用程序启动时,SQLAlchemy 的连接池 (
engine) 会创建一些与 MySQL 服务器的连接,并把它们“存放”起来以备后用。 - 长时间空闲:如果应用程序在一段时间内没有数据库操作,这些“存放”的连接就会一直处于空闲状态。
- 服务器关闭连接:MySQL 服务器有一个
wait_timeout配置(默认通常是 8 小时,但可以被数据库管理员设置得更短,比如几分钟)。当一个连接空闲时间超过这个阈值后,MySQL 服务器会主动单方面断开这个连接,以释放资源。 - 应用再次使用:此时,MySQL 服务器认为连接已经关闭,但您的 Python 应用程序并不知道。当一个新的请求(比如您系统首次进入时的查询)到来时,SQLAlchemy 从连接池里取出了这个实际上已经被服务器关闭的“僵尸连接”,并尝试用它来执行 SQL 查询。
- 发生错误:查询请求发到一个已经不存在的连接上,于是就收到了
MySQL server has gone away的错误。 - 刷新后恢复:当错误发生后,SQLAlchemy 的连接池会识别到这个连接已损坏,将其丢弃并创建一个新的、有效的连接。所以当您刷新页面,发起第二次请求时,程序获取了新的连接,一切就正常了。
这完美地解释了为什么“首次进入时报错,刷新后就正常”的现象。
4、解决方案
解决这个问题的标准方法是在创建 SQLAlchemy 引擎时,配置 连接池的回收机制。这可以确保连接池不会持有已经超时的连接。
您需要在 create_engine 函数中添加 pool_recycle 参数。这个参数的值(单位为秒)应该小于 MySQL 服务器的 wait_timeout。一个安全且常用的值是 3600(1 小时)。
修改建议 :配置连接池回收
在 microservices/data_report/db_session.py 文件中,修改 create_engine 的调用:
# 原代码 # engine = create_engine(DB_URI) # 修改后的代码 # 增加 pool_recycle=3600 参数,让连接池每隔1小时回收一次空闲连接 engine = create_engine(DB_URI, pool_recycle=3600)
修改后完整代码:
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, scoped_session from contextlib import contextmanager # 假设 database_type 和 database_info 的获取逻辑在这里 # ... if database_type == "mysql": # Mysql数据库 HOSTNAME = database_info.get("host_name") PORT = database_info.get("port") DATABASE = database_info.get("database_name") USERNAME = database_info.get("username") PASSWORD = database_info.get("password") DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{db}".format( username=USERNAME, password=PASSWORD, host=HOSTNAME, port=PORT, db=DATABASE, ) else: DB_URI = "sqlite:///microservices/data_report/alarm_msg.db" # --- 主要修改点 --- # 添加 pool_recycle=3600 来防止连接超时 engine = create_engine(DB_URI, pool_recycle=3600, echo=False) Base = declarative_base() DBSession = sessionmaker(bind=engine) session = scoped_session(DBSession) @contextmanager def session_scope(): """ 提供一个事务性的作用域,并确保会话在使用后被正确移除。 这是使用 scoped_session 的标准模式。 """ s = session() # 从 scoped_session 代理获取当前线程的 Session 实例 try: yield s s.commit() except Exception as e: s.rollback() raise e finally: session.remove() # 在 scoped_session 代理上调用 remove()
这个改动会让 SQLAlchemy 自动处理空闲超时的连接,从根本上解决 gone away 的问题。
总结
- 核心原因:MySQL 服务器因连接空闲而主动断开了连接,但客户端(SQLAlchemy 连接池)不知道,导致使用了失效连接。
- 核心解决方案:在
create_engine中设置pool_recycle=3600,强制连接池定期回收并重建空闲连接。
回答来源于AI。

浙公网安备 33010602011771号