news 2026/5/1 5:42:40

mysql.connector.errors.OperationalError: 1040 (08004): Too many connections

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
mysql.connector.errors.OperationalError: 1040 (08004): Too many connections

从报错截图来看,核心错误信息是:
mysql.connector.errors.OperationalError: 1040 (08004): Too many connections

这意味着你的 Python 程序(具体是在 Streamlit 框架下运行)向 MySQL 数据库发起了过多的连接请求,超出了数据库允许的最大并发连接数(默认为 151),并且之前的连接没有被及时关闭

为什么会发生这种情况?

  1. 连接未关闭(Connection Leak):代码中打开了数据库连接(connect()),但在使用完后没有调用conn.close()
  2. Streamlit 的运行机制:我注意到报错路径中有streamlit。Streamlit 的特点是每当用户与页面交互(点击按钮、刷新等)时,整个 Python 脚本会从头到尾重新运行一次
    • 看你的代码位置:utils\scm_db.py第 9 行直接执行了conn = mysql.connector.connect(...)
    • 每次页面刷新,这行代码就运行一次,创建一个新连接。
    • 旧连接还在占用资源,新连接不断增加,很快就耗尽了 MySQL 的连接池。

解决方案

方案一:使用 Streamlit 的缓存机制(推荐,最适合你的场景)

在 Streamlit 中,数据库连接应该被缓存,确保整个应用只创建一次连接,而不是每次刷新都创建。

修改utils/scm_db.py

importmysql.connectorimportstreamlitasst# 你的 Settings 导入# 使用 @st.cache_resource 装饰器,确保连接只被创建一次@st.cache_resourcedefinit_connection():returnmysql.connector.connect(host=Settings.DB_HOST,user=Settings.DB_USER,password=Settings.DB_PASSWORD,database=Settings.DB_NAME)# 在其他文件中调用时:# conn = init_connection()

注意:@st.cache_resource是 Streamlit 新版的方法,旧版可能叫@st.experimental_singleton

方案二:确保连接即用即关(标准做法)

如果你不需要长连接,应当在查完数据后立刻关闭连接。最好使用try...finally或者上下文管理器。

# 修改你的调用逻辑defget_data():conn=Nonecursor=Nonetry:conn=mysql.connector.connect(host=...,...)# 建立连接cursor=conn.cursor()# 执行查询cursor.execute("SELECT * FROM ...")result=cursor.fetchall()returnresultexceptExceptionase:print(e)finally:# 无论是否报错,这里都会执行,确保关闭连接ifcursor:cursor.close()ifconn:conn.close()
方案三:临时解决(重启服务)

为了立刻让程序能跑起来,你需要清除当前占满的“僵尸”连接:

  1. 重启 MySQL 服务:这会强制断开所有现有连接。
    • Windows: 在“服务”中找到 MySQL 并重启。
    • Linux:sudo service mysql restart
  2. 重启 Streamlit 应用:关闭终端里的 Streamlit 进程并重新运行。
方案四:增加 MySQL 最大连接数(治标不治本)

如果你的应用确实需要很高并发(通常开发环境不需要),可以在 MySQL 中修改配置。

  1. 登录 MySQL 命令行。
  2. 执行命令查看当前限制:SHOW VARIABLES LIKE 'max_connections';
  3. 临时修改限制(例如改为 1000):SET GLOBAL max_connections = 1000;

总结

问题出在Streamlit 每次刷新页面都重新执行了连接数据库的代码,且没有关闭旧连接。请优先采用方案一进行代码优化。

这是一个非常好的问题,触及了 SQLAlchemy(通常配合 pandas 使用)的核心机制——连接池(Connection Pooling)

简单直接的回答是:

  1. 算一次连接吗?是的,它会占用或者建立一个与数据库的会话。
  2. 如何关闭?你不需要(也不能)手动关闭它。只要你传给read_sql的是engine,pandas 会自动处理“借出连接”和“归还连接”的操作。

但结合你之前遇到的Too many connections报错,这里有很深的“坑”,请仔细看下面的分析:


一、pd.read_sql的内部机制

当你执行如下代码时:

importpandasaspdfromsqlalchemyimportcreate_engine# 假设 engine 已经创建好df=pd.read_sql("SELECT * FROM table",engine)

Pandas 内部其实做了以下事情:

  1. 向 Engine 申请连接:它调用engine.connect()
  2. 连接池机制
    • 如果连接池里有空闲连接,Engine 直接把旧连接拿来用(速度快,MySQL 看来连接数没变)。
    • 如果连接池是空的,Engine 会创建一个新的物理连接(MySQL 的Threads_connected+1)。
  3. 执行 SQL:利用这个连接读取数据。
  4. 自动关闭(归还):数据读取完毕后,Pandas 会自动调用连接的.close()方法。

关键点来了:
对于 SQLAlchemy 的 Engine 来说,.close()并不代表断开 TCP 连接,而是代表**“把连接还给连接池”
所以在 MySQL 服务端看来,这个连接依然是
连着(Sleep)**的状态,等待下一次被复用。

二、 既然自动关闭,为什么还会报错?

既然read_sql会自动归还连接,为什么你还会遇到连接数过多的报错?

罪魁祸首还是 Streamlit 的刷新机制。

如果你的代码是这样写的(在 Streamlit 中):

# 错误写法:每次刷新页面都会运行这几行engine=create_engine("mysql+mysqlconnector://user:pass@host/db")df=pd.read_sql("SELECT * FROM table",engine)

发生了什么?

  1. 第 1 次刷新:创建了Engine对象_A(自带一个连接池)。read_sql建立了一个连接(Conn_1)。用完后,Conn_1 回到了Engine对象_A的池子里。
  2. 第 2 次刷新:脚本重新运行,创建了Engine对象_B(自带的连接池)。read_sql建立了一个连接(Conn_2)。用完后,Conn_2 回到了Engine对象_B的池子里。
    • 问题Engine对象_A虽然被 Python 变量抛弃了,但它建立的 Conn_1 还在 MySQL 那边挂着!直到 Python 进行垃圾回收(GC)或者 MySQL 超时,这个连接才会断。
  3. 第 1000 次刷新:你创建了 1000 个 Engine 对象,MySQL 里挂了 1000 个 Sleep 的连接 ——BOOM! Too many connections.

三、 正确的解决方案

你要做的不是“手动关闭连接”,而是**“防止重复创建 Engine”。你需要让整个 Streamlit 应用无论刷新多少次,全局只使用同一个** Engine 对象。

写法 1:使用 Streamlit 缓存 Engine(强烈推荐)

修改你的utils/scm_db.py或相关文件:

importpandasaspdfromsqlalchemyimportcreate_engineimportstreamlitasst# 使用 @st.cache_resource 装饰器# 这告诉 Streamlit:这个函数的返回值(engine)要存起来(缓存),# 下次再调用这个函数,直接返回存起来的那个,不要重新执行内部代码。@st.cache_resourcedefget_db_engine():# 这里填写你的连接字符串engine=create_engine("mysql+mysqlconnector://root:password@host:3306/db_name")returnengine# 在主程序中使用defmain():# 获取唯一的 engine 实例engine=get_db_engine()# 放心读取,由 Engine 自动管理连接池df=pd.read_sql("SELECT * FROM my_table",engine)st.dataframe(df)
写法 2:如果不使用缓存(不推荐),如何强制销毁?

如果你实在不想用缓存(通常不建议),你必须显式地销毁 Engine 以断开物理连接:

engine=create_engine(...)try:df=pd.read_sql(sql,engine)finally:# 强制销毁 Engine,这会切断池子里所有的物理连接engine.dispose()

总结

  1. pd.read_sql(sql, engine)会自动处理连接的开启和关闭(归还给池子),你不需要手动写conn.close()
  2. 你的报错是因为重复创建了太多 Engine 对象,每个对象后面都拖着一堆空闲连接没断开。
  3. 解决方法:使用@st.cache_resource缓存create_engine的结果,确保全局只有一个 Engine。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 12:50:12

写个简单的ros2代码

1、再主文件夹中右击鼠标打开终端,输入以下命令进入vscode mkdir -p demo_04/src cd demo_04 code .2、右击src选择在集成终端打开 输入 ros2 pkg create test111 --build-type ament_python --dependencies rclpy然后就能在src目录下看到 3、ok现在可以看到test下方…

作者头像 李华
网站建设 2026/4/30 0:06:46

工业数据“采了白采”?有人物联网藏着采集+分析的全套打法

不少工厂老板都有过这种无奈:花几万块装了工业设备数据采集设备,买了数据采集软件,最后却只干了件“存硬盘”的活——产线数据堆了几百G,既不知道能干嘛,也不会分析,活生生把“金矿”当成了“垃圾”。其实工…

作者头像 李华
网站建设 2026/4/25 11:39:36

GitOps实践应用:通过代码仓库管理AI配置

GitOps实践应用:通过代码仓库管理AI配置 在企业级AI系统日益复杂的今天,一个看似简单的操作——更新知识库文档或切换大语言模型——却可能引发连锁反应:配置不一致、权限错乱、服务中断。传统的“登录服务器手动修改”模式早已无法满足对稳定…

作者头像 李华
网站建设 2026/4/28 19:53:43

零基础实战:完成一个LED灯阵列的PCB布线项目

从点亮第一颗LED开始:手把手带你完成人生第一个PCB设计你有没有过这样的经历?看着别人做的智能灯带、像素屏、动画面板,心里直痒痒,却总觉得“PCB设计”四个字高深莫测,像是只有科班出身的工程师才能碰的领域&#xff…

作者头像 李华
网站建设 2026/4/17 20:10:49

Anthropic 收购 Bun:当 AI 巨头决定掌控底层代码基建

硅谷的 AI 竞赛已经进入 next level 了,原本卷模型参数,现在开始卷应用生态和底层基建。 当地时间 12 月 2 日,Anthropic 宣布收购热门 JavaScript 运行时工具 Bun。这并非一次简单的人才收购(Acqui-hire)&#xff0c…

作者头像 李华
网站建设 2026/4/30 22:36:56

FPGA中时序逻辑电路构建的操作指南

FPGA时序逻辑设计实战:从触发器到跨时钟域的系统构建 你有没有遇到过这样的情况?代码写得严丝合缝,仿真波形完美无瑕,结果下载到FPGA板子上一跑,数据错乱、状态跳变异常,甚至直接“死机”?别急—…

作者头像 李华