Hotdry.

Article

纯PHP全文本搜索引擎php-fts的Trigram倒排索引工程实现解析

深入解析开源项目php-fts如何基于trigram构建倒排索引,涵盖索引构建、查询执行及关键工程参数。

2026-05-07systems

在 PHP 生态中,全文搜索通常依赖 Elasticsearch、MeiliSearch 等外部服务,或借助 Sphinx、MySQL FTS 等中间件。olivier-ls/php-fts 项目提供了一种完全不同的路径:仅使用原生 PHP 代码,无需任何 PHP 扩展即可实现完整的全文搜索引擎。其核心策略是基于 trigram(三元字符组)构建倒排索引,这一选择在工程上具有独特的权衡考量,本文将深入解析其实现细节。

Trigram 索引的技术原理

Trigram 的核心思想将文本拆解为连续的三个字符的重叠片段。对于字符串 "hello",其 trigram 集合为 {"hel", "ell", "llo"}。这种拆分方式的优势在于:任何长度大于等于 3 的子串,必然包含至少一个 trigram。这一特性使得基于 trigram 的索引能够支持任意子串查询,而无需像词项索引那样依赖精确的分词器。

在倒排索引结构中,每个唯一的 trigram 作为倒排表的主键,关联一个 posting list( postings 列表),记录包含该 trigram 的所有文档标识符。查询时,系统将用户输入的搜索词同样拆解为 trigram,然后对这些 trigram 对应的 posting list 取交集,得到的候选文档集合再经过原始文本验证以过滤掉假阳性结果。这种两阶段过滤策略是 trigram 索引高效运行的关键。

索引构建流程的工程实现

php-fts 的索引构建过程遵循标准的信息检索 pipeline,但针对 PHP 语言特性做了简化处理。首先是文本规范化阶段,系统会对原始文本进行小写化转换、Unicode 规范化以及可选的重音符号折叠(accent folding),确保不同形态的相同语义文本能够被统一索引。边界处理是另一个重要细节,索引器通常会在文本首尾添加填充字符(如空格),使得前缀和后缀搜索也能获得有效的 trigram 覆盖。

构建倒排表的核心代码逻辑可以简化为:遍历待索引文档的每个字段,生成该字段文本的所有 trigram,对每个唯一的 trigram,将其对应的文档 ID 加入该 trigram 的 posting list。在存储层面,对于小规模数据集,系统可能直接使用 PHP 数组结构将整个倒排表加载到内存;当数据量增大时,则需要持久化到磁盘,并设计高效的加载策略以支持快速的查询响应。

压缩是倒排索引工程实现中的重要环节。Php-fts 可以采用多种压缩策略:delta 编码将相邻的文档 ID 转换为差值以减少存储空间;可变长度整数(varint)编码进一步优化数值存储;位图(bitmap)表示则在 posting list 较短时提供紧凑的存储形式。这些压缩技术的选择直接影响索引体积和查询性能之间的平衡。

查询执行的两阶段过滤

查询执行分为候选生成和精确验证两个阶段。候选生成阶段将用户输入的查询字符串同样拆解为 trigram 集合,然后对这些 trigram 对应的 posting list 执行交集操作。高效的交集算法会优先从 posting list 最短(即出现频率最低)的 trigram 开始处理,这样可以显著减少需要合并的元素数量,提升交集中间结果的紧凑程度。

由于 trigram 的重叠特性,交集结果中必然包含真正的匹配文档,但也可能混入假阳性 —— 即那些包含查询词的所有 trigram 但整体文本并不匹配查询要求的文档。因此第二阶段的精确验证不可或缺:系统会逐一检查候选文档的原始文本,确认其是否真正满足查询条件(例如精确匹配、模糊匹配或短语匹配)。这种两阶段设计在搜索精度和性能之间取得了良好的平衡。

工程落地的关键参数

在实际项目中集成 php-fts 时,有几个关键参数值得深入关注。第一个是索引刷新策略:系统在新增或更新文档后,需要显式调用 buildIndex 或类似方法才能使新内容可被搜索,这意味着在实时性要求高的场景中,需要权衡频繁重建索引的开销与数据新鲜度的需求。

第二个参数是 trigram 稀疏度控制。对于极短的文本(如商品 SKU、标签等),生成的 trigram 数量较少,可能导致索引召回率下降。可以通过调整文本预处理策略 —— 例如对短文本进行特殊填充或降级到字符级别的 n-gram—— 来改善这一问题。

第三个参数是内存与磁盘的权衡。PHP 数组在内存中具有较好的随机访问性能,但当索引规模达到数十万文档级别时,需要考虑将倒排表的部分或全部结构持久化到磁盘,并通过内存缓存热点数据的方式来维持查询性能。

性能特征与适用边界

Trigram 倒排索引的性能特征具有鲜明的两面性。其优势在于对子串搜索的原生支持 —— 用户无需记忆完整的单词形态,系统也能返回相关结果;完全基于 PHP 实现意味着部署时无需额外部署搜索服务,简化了运维复杂度;索引构建过程独立于外部数据库,使得它可以作为一种轻量级的搜索中间件与现有系统解耦集成。

然而,trigram 索引的空间开销通常高于传统的词项索引,因为每个文本位置都会产生一个 trigram 重复映射。这一特性使其在以下场景中表现受限:大规模文档集合(百万级以上)对存储成本敏感;查询延迟要求极低(毫秒级)且需要复杂的评分排序功能。在这些情况下,专门的搜索引擎仍然是更优选择。

小结

php-fts 项目展示了在纯 PHP 环境下构建实用全文搜索能力的完整路径。通过 trigram 倒排索引这一核心技术选择,它在实现简洁性与搜索功能之间找到了合理的平衡点。对于中小规模的 PHP 应用、需要快速原型验证、或希望在主数据库之外构建独立搜索层的场景,这个项目提供了值得参考的工程实践。

资料来源:GitHub 仓库 olivier-ls/php-fts 项目主页及 README 文档;Hacker News 相关讨论。

systems