如何解决Streamlit加载NoSQL大数据量时循环重跑websocket报错问题
问题根因
26000条数据触发循环重跑、websocket报错,和Mongo服务端的server.maxMessageSize配置无关,核心是两个问题:
- 未对数据库查询逻辑做缓存,每次Streamlit脚本重跑都会重新拉取全量数据,序列化、传输开销过大触发前后端websocket心跳超时,重连后再次触发脚本重跑形成死循环
- 全量DataFrame直接传给前端渲染时未做压缩、虚拟滚动配置,传输体积超过Streamlit默认的websocket消息阈值
可落地方案
按优先级顺序操作即可解决问题:
1. 给数据加载逻辑加正确的缓存
这是解决循环重跑的核心操作,不要在脚本顶层直接写pymongo查询逻辑,封装加载函数后用st.cache_data装饰,避免重复拉数:
import streamlit as st from pymongo import MongoClient import pandas as pd @st.cache_data(ttl=3600, show_spinner="加载数据中...") def load_target_data(): # 注意:Mongo连接不要放在缓存外层,避免连接对象序列化异常 client = MongoClient("你的Mongo连接串") db = client["你的业务库名"] collection = db["你的目标集合名"] # 拉数时只传入需要展示的字段做投影,剔除_id、大文本/二进制类不需要的字段,能压缩30%以上数据体积 projection = {"_id": 0, "需要展示的字段1": 1, "需要展示的字段2": 1} data_cursor = collection.find({}, projection) df = pd.DataFrame(list(data_cursor)) client.close() return df # 全局只调用一次加载函数,缓存命中时不会重复执行查询 df = load_target_data()
注意:不要在缓存函数内做动态列生成、随机值计算这类逻辑,保证相同入参下返回的DataFrame结构和内容完全一致,避免触发Streamlit的重跑检测。
2. 调整Streamlit服务端配置
你之前修改的是Mongo侧的消息大小配置,需要修改的是Streamlit自身的服务端配置。在项目根目录新建.streamlit/config.toml文件,写入以下配置:
[server] # 调大websocket消息上限到512MB,足够覆盖10w行以内的结构化表格数据 maxMessageSize = 536870637 # 开启websocket传输压缩,通常能减少60%以上的传输体积 enableWebsocketCompression = true headless = true [client] showErrorDetails = true
重启Streamlit服务后配置生效。
3. 开启DataFrame虚拟滚动,避免前端渲染压力
不要一次性把全量数据渲染成DOM节点,调用st.dataframe时开启内置虚拟滚动,前端只会渲染可视区域内的行,2.6w行数据渲染无压力:
st.dataframe( df, use_container_width=True, hide_index=True, height=650, # 固定表格高度触发虚拟滚动 )
如果后续数据量上涨到10w条以上,直接搭配服务端分页:用Mongo的skip()+limit()做分页查询,配合st.pagination组件每次只加载当前页的100-200条数据,从根源上避免大体积数据传输问题。
常见踩坑排查
如果按以上操作还是出现重跑,检查两个点:
- 不要把pymongo的连接对象、游标对象直接传给Streamlit组件或者存入session_state,这类不可序列化的对象会触发序列化异常导致重跑
- 不要在按钮、选择框这类组件的回调函数里写全量查询逻辑,回调触发的重跑会重复执行查询
内容的提问来源于stack exchange,提问作者JimSepiol
相关产品推荐
相关产品推荐

