快速结论:当 Open WebUI 使用 PostgreSQL 后端且 chat.chat 的 JSON 数据被 TOAST 存储后,侧边栏聊天内容搜索(/api/v1/chats/search)会做逐元素的 JSON EXISTS 匹配,导致每次查询触发大量 TOAST detoast,单次 5–15 秒。优先排查 chat.chat 表体积与查询计划(是否顺序扫描 + 逐行 detoast)。
适用环境:Open WebUI v0.11.4(dev @ c9d3ae999 同样复现);Docker 安装;Linux;PostgreSQL 17 后端;Ollama 版本 n/a(与问题无关)。
最快修复方案:暂无确认的一步修复方案。Issue 中给出的两阶段查询 + chat_search_content 纯文本列 + GIN trigram 索引方案属于作者提出的修复方向(对应 PR #32268),但 Issue 本身未确认已合并或已在发行版中验证,可优先关注该 PR 的合并状态。
注意事项:该方案依赖 pg_trgm 扩展与索引创建权限;不允许创建索引的环境会自动回退为旧行为,性能问题不会改善。SQLite 部署不受影响。基准数据来自作者的生产库副本(2164 条 chat、含 TOAST 约 490 MB),你的数据分布不同,耗时与收益可能有差异。
问题场景
在 Docker + PostgreSQL 17 后端运行 Open WebUI v0.11.4(或当前 dev 分支),聊天记录积累到数千条、其中包含较长会话后,在侧边栏用关键词搜索聊天内容。数据量达到 chat.chat 表约 490 MB(含 TOAST)、3.3 万条 chat_message 时,每次搜索耗时 5–16 秒,侧边栏搜索基本不可用。问题出在后端查询路径(backend/open_webui/models/chats.py 中的 chat_search_message_content_match_sql、get_chats_by_user_id_and_search_text),与浏览器无关。
报错原文
issue: chat content search on PostgreSQL scans TOASTed chat.chat blobs, 5-15 s per query
the content predicate evaluates correlated per-element EXISTS JSON conditions directly against Chat.chat
the PostgreSQL planner cannot price TOAST detoasting
the chat heap is ~1 MB while its TOAST table holds hundreds of MB
it chooses plans that detoast hundreds of rows per query
chat::text NOT LIKE '%%' null-byte safety filter alone costs ~1.7 s per query
EXPLAIN ANALYZE shows sequential scans with per-row detoast of the JSON blob
原因分析
最可能的原因是 PostgreSQL 查询规划器对 TOAST detoast 成本“无感知”。内容匹配条件(history + legacy 分支)以逐元素关联 EXISTS 的 JSON 条件直接作用于 Chat.chat。由于 chat 主堆只有约 1 MB,而对应 TOAST 表存有数百 MB,规划器在比较 user_id 位图/过滤(成本约 125)与基于 blob 的 trigram 索引扫描(成本约 520)时,无法估算实际 detoast 成本,最终选择每次 detoast 数百行的执行计划。基表上任何索引都无法解决这一步,因为被错误定价的是 detoast 本身。仅 chat::text NOT LIKE '%%' 这个空字节安全过滤在我的数据上就约占 1.7 秒/查询。
环境排查
- 确认 Open WebUI 版本:v0.11.4 或
dev@ c9d3ae999(Issue 确认两条路径字节级一致)。 - 确认数据库:PostgreSQL 17,且为 Docker 部署;SQLite 部署不涉及该路径。
- 确认数据规模:
chat表含 TOAST 体积、chat 条数、chat_message行数(Issue 数据为 490 MB / 2164 条 / 33k 行)。 - 确认数据库参数:Issue 基准为 stock PostgreSQL 17 设置(
random_page_cost=4、shared_buffers=128MB)。 - 确认是否具备
pg_trgm扩展与创建索引的权限(影响修复方案是否能落地)。 - Ollama 版本与前端浏览器不影响该问题。
解决步骤
- 先用
EXPLAIN ANALYZE跑一遍实际搜索查询,确认是否出现顺序扫描以及对chat.chat的逐行 detoast,作为基线证据。 - 如果只是想在当前版本缓解,先缩小数据集或归档长会话,减少 TOAST 总量——这只降低单次查询扫描量,并非结构性修复。
- 关注/试用 Issue 作者提出的两阶段方案(对应 PR #32268):阶段 A 只匹配标题与
chat_message表(不 detoast),通常已填满分页;阶段 B 仅在阶段 A 结果不足时执行,通过新增的触发器维护、已回填的纯文本列chat_search_content搭配 GIN trigram 索引做预筛选,再用原始逐元素EXISTS条件精确确认。 - 对 trigram 索引无法服务的短 CJK 模式,方案改为在该纯文本列上做内联预筛选。
- 确认你的 PostgreSQL 允许安装
pg_trgm并创建索引;若不允许,安装逻辑会自动保留旧行为。
验证方法
用相同的搜索词对修改前后跑对比,确认结果集与旧查询完全一致,并观察耗时变化。作者在完整生产数据副本、stock PostgreSQL 17 设置下测得:proxy 4.5 s → 1.9 s、proxy server 14.1 s → 3.4 s、uptime 8.9 s → 3.0 s、opnsense 5.0 s → 0.09 s、netbird 4.6 s → 0.10 s、电池 5.6 s → 1.0 s、电池 传感器 7.5 s → 0.7 s。请以你自己数据上的实测结果为准。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


