站内搜索该不该换?我拿 98 万字中文试了四种做法

经验 2026-09-25 20:03 · 72 次阅读 ·搜索,踩坑,实测,折腾记录

四种搜索做法的实测对比:命中数与耗时(98 万字符中文语料)

一句话结论:我最后没换。站上这点内容量,最笨的写法就已经够快;真要换,中文有两个坑——其中一个会悄悄搜不到东西,还不报错。

背景

站上的搜索框一次搜三张表:文章、资源、工具。

现在的写法是把关键词前后各加一个百分号,让数据库挨行去看谁含有这几个字:

like = f"%{kw}%"        # 搜「打包」,就变成 %打包%

搜「打包」,数据库就一行行翻,谁含有这两个字就留下。用起来没问题,但它用不上数据库的「目录」,每次搜索都要把每篇文章从头到尾读一遍。内容越多越慢。

我想知道两件事:要不要换成数据库自带的全文搜索?换了之后中文会不会出问题?

实测:四种做法,同一批词

站上只有 1.7 万字,拿它比快慢等于在噪音里比速度。我改用自己电脑里的 207 个中文 markdown 文件,一共 98 万字,四种做法各建一个库,同一批词各跑 30 次,取中间那次耗时。

做法 搜「搜索」 搜「签名」 搜「验证码」 搜「重打包」 耗时 库变大到
现在的写法 38 条 15 条 17 条 3 条 3 毫秒 1.4 MB
全文搜索(自带切词) 10 条 9 条 8 条 1 条 0.02 毫秒 3.7 MB
全文搜索(自己切词) 36 条 15 条 17 条 3 条 0.04 毫秒 5.6 MB
全文搜索(三字一组) 0 条 0 条 17 条 3 条 0.03 毫秒 7.4 MB

库的大小是压缩收拾过之后量的。三个结果都不在预料里。

一、数据库自带的切词方式,会把整段中文当成一个词。

全文搜索得先把文章切成一个个「词」,再建一份清单,跟书后面的人名索引一个道理。它默认认为:连续出现的字母和数字算一个词。汉字也被算作字母数字,于是一整段中文就变成了一个大词。

我翻了它建出来的词表,最长的一条有 33 个字,就是一句话:「腾讯微信openclaw插件api通信过程剖析与python原生代码复刻原理」。里面带「搜索」那条,是「支持关键词精确搜索和多关键词组合查询」这一整句。

所以搜「搜索」时,只有「这两个字正好被别的字符隔开、单独成词」的文章才找得到,命中数从 38 条掉到 10 条。

二、三字一组的切法能救短句,但两个字以内直接返回 0 条。

换个切法:每 3 个字符建一条清单项。这样「验证码」「重打包」这种 3 个字以上的词,结果和现在的写法一样,耗时只要 0.03 毫秒。

但两个字的词,比如「搜索」「签名」,它一条都不返回,也不报错。用户看到的不是错误提示,而是「本站没有相关内容」。中文里最常用的就是两个字、三个字的词。

三、自己切词,建清单和查清单两边都得改。

既然自带的不切,那就自己切:只在汉字上按两个字一组切开,灌进清单。

第一版我只改了灌进去的那边,结果搜「搜索」36 条(接近 38)、搜「签名」15 条(一样),但搜「验证码」0 条、搜「重打包」0 条。原因是查询时送进去的还是完整的一个词,而清单里已经没有这个词了。

把搜索词也按同样规则切开之后才对上:验证码 17 条、重打包 3 条,与现在的写法逐条一致。

结论与踩坑

  1. 这个量级先不用换。 98 万字全表翻一遍只要 3 毫秒,站上现在只有 1.7 万字,而全文搜索省下的只有 2 毫秒,换来的是索引维护、切词改造和一串新坑。等文章涨到几百万字再说。
  2. 真要上全文搜索,中文必须自己写切词规则。 直接用自带的,搜索质量是变差,不只是变快——一整段话被当成一个词,等于把「关键词搜索」降级成「整句搜索」。
  3. 三字一组不能单独用在中文站。 两字以内返回空且不提示,必须加兜底:字数不够 3 个就退回现在的写法。
if kw_ft and len(kw) >= 3:           # 切好词、3 个字以上
    rows = q("SELECT rowid FROM docs WHERE docs MATCH ?", (f'"{kw_ft}"',))
else:                                 # 两个字、一个字,走老的写法
    rows = q("SELECT id FROM docs WHERE body LIKE ?", (f'%{kw}%',))
  1. 清单占地方。 原文 1.2 MB,自带切词的清单把库撑到 3.7 MB,自己切词 5.6 MB,三字一组到 7.4 MB,是原文的 4.8 倍。小站盘不心疼,但要知道代价花在哪。
  2. 验证搜索,不能只看「能不能搜出东西」。 这次踩的三个坑——自带切词少命中、两字词静默返回 0 条、只改一边漏结果——没有一个会报错。我的办法是:同一批词,两种写法各跑一遍,对命中数;差得超过正常波动就是有 bug。

继续看

全部 →