使用分词在 PostgreSQL 中加速模糊查询
背景与挑战
随着业务数据量突破 200 万行,传统的 LIKE '%关键词%' 模糊查询导致数据库 I/O 频繁告警,查询响应时间从毫秒级退化至秒级。为提升检索效率并支持中文语义,我们决定引入 zhparser 插件实现全文检索。
演进路线与环境适配
本次技术落地经历了四个关键阶段,每个阶段解决了不同的技术核心:
CentOS 7.9 虚拟机(可行性验证)
- 目标:验证
SCWS+zhparser在旧版系统下的兼容性。 - 核心操作:在 CentOS 7.9 环境下手动编译
postgresql-16.2源码,完成插件初步跑通。
结论:确认了分词方案对中文检索性能的显著提升。
本地 Docker 容器(容器化探索)
- 目标:在本地的完整系统进行初步测试。
- 核心操作:通过
docker cp注入二进制.so文件,解决ldconfig动态链接库路径可见性问题。 - 发现:识别出 "词典文件缺失" 会导致分词退化为单字(助词)的故障点。
EulerOS 2.0 测试服(自编译环境适配)
- 目标:适配生产环境的 OS 架构(x86_64)与自编译安装的 PostgreSQL。
- 核心问题:解决了
libscws.so.1无法加载的报错。 - 关键方案:
- 明确了
postgres运行用户对/usr/local/scws/lib的访问权限需求。 - 通过修改
systemd服务环境变量或创建/usr/lib64软链接强制刷新库搜索路径。
- 明确了
生产部署预备(最终调优)
- 目标:确保 200w+ 数据量下的查询稳定性。
- 优化点:针对"非语义片段(如:古唐合)"查询不到的问题,制定了"全文检索优先 +
pg_trgm索引辅助"的降级查询策略。
核心安装与配置步骤 (自编译环境)
安装 SCWS 分词引擎
SCWS 是 zhparser 依赖的底层核心,必须先行安装。
-
下载并解压:下载源码包(如
scws-1.2.3)。 -
编译安装:
-
验证库文件:确保
/usr/local/scws/lib/libscws.so.1存在。
编译与安装 zhparser
这一步需要用到 PostgreSQL 自编译产生的 pg_config。
-
获取源码:从 GitHub 克隆
zhparser项目。 -
指定路径编译:
注:
install步骤会自动将zhparser.so放入 PG 的pkglibdir,将脚本放入extension目录。
解决动态链接库依赖
-
刷新系统缓存:
-
权限检查:确保运行
postgres的 OS 用户对/usr/local/scws/lib有rx权限。 -
强制软链 (备选):若
ldconfig失效,可将库文件软链至/usr/lib64。
重启数据库
修改完系统共享库配置后,必须重启 PostgreSQL 进程以重新加载环境变量和链接库。
数据库内初始化
连接到 psql,执行逻辑配置:
性能分析与避坑指南
索引性能瓶颈分析
在测试中发现,即使是精确查询,由于使用了不当的联合索引(create_time 在首列),导致产生 Bitmap Index Scan,耗时高达 482ms。
- 改进:对检索高频列建立单列 B-tree 索引,利用
Index Scan将响应降至 10ms 以内。
GIN 索引与非语义匹配
- 跨词截断问题:分词引擎基于语义,类似"古唐合"这种截断词可能因为分词边界导致
@@无法命中。 - 应对方案:采用"瀑布式搜索"。全文检索(FTS)优先;若结果为空,自动降级为
LIKE模糊查询,并配合pg_trgm索引加速。
终极部署方案:双轨并行检索
经过对数据的分析,发现对于账户名称中出现了约 1.24% 的非标准简体中文的数据,并且数据库中也存在部分因为公司名称怪异而导致数据查询不出来的问题,决定采取"分步降级"策略:
- 第一步:全文检索(Fast Track): 利用 GIN 索引进行
@@匹配。 - 第二步:结果评估: 若返回结果为空,且搜索词包含字母或疑似繁体字符。
- 第三步:模糊兜底(Safe Fallback): 执行
LIKE '%关键词%'。虽然速度较慢,但由于其作为"补漏"逻辑,触发频率仅为 1% 左右,不会对数据库造成整体压力。
搜索优化:集成自定义业务词库
为了解决全文检索中公司品牌名被误切(如"元一"被切分为数词/量词)的问题,需构建一套从数据提取到索引重建的自动化维护链路。
词库提取与预处理
利用 companynameparser 的结构化解析能力剥离地名与行业后缀,并结合 jieba 进行语义复核,确保核心品牌词的完整性:
- 提取逻辑:通过 Python 脚本遍历全量
buyer_unique_name,提取核心brand字段。 - 权重补偿:针对包含"元"、"一"、"三"等易碎词条,手动将 TF(词频权重)提升至 50.0 - 60.0,确保其优先级高于内置量词规则。
- 输出规范:输出符合 SCWS 标准的 4 字段
UTF-8文本(WORD, TF, IDF, ATTR),建议使用制表符\t分隔以规避解析异常。
词库编译与部署
将文本词典转换为 SCWS 高效二进制格式(XDB):
-
编译二进制词典:
-
文件分发与权限:将生成的
.xdb文件移动至分词数据目录,并确保postgres用户具备读取权限:
数据库参数固化
修改 postgresql.conf 配置文件,强制加载 zhparser 及其自定义扩展词库:
索引热更新与验证
由于分词规则变动,存量数据必须通过重建索引实现语义同步:
物理重启服务:
在线重建索引:利用 CONCURRENTLY 关键字,在不阻塞 40 万条数据 DML 操作的前提下刷新 GIN 索引:
分词效能验证:
优化建议:
- 显式提及权重补偿:这是解决"元一"分词失败(显示为
x)的关键技术点。 - 区分重启与重载:明确
shared_preload_libraries必须通过restart激活。