主要介绍 FTS 的一些实现。
分词技术
ngram
字典结构
ngram bloomfilter
- 生成 N-gram: 将一个输入字符串生成所有可能的 N-gram。
- 存储到布隆过滤器: 对每个 N-gram 使用多个哈希函数,将其存储到布隆过滤器中。
查询: 对查询的字符串生成对应的 N-gram,逐个检查它们是否存在于布隆过滤器中。
- 如果布隆过滤器判定所有 N-gram 存在,说明可能是匹配。
- 如果至少有一个 N-gram 不存在,可以确定不匹配。
缺点:
- 存在 FP
- 不支持删除
BTree
跳表
KV 结构
存储分词后的倒排索引
1 | "OceanBase" -> [Doc1, Doc2] |
FST(Finite-State Transducer)
FST 是一种 Trie 树的结构,被用在倒排索引的词典中。所以,它是用来组织 term 的结构。
对于大量的 term,需要考虑:
- 这些字符串有大量公共前缀,直接存储会浪费空间
- 我们希望能快速地按字典序查找 term,例如支持前缀查询,或者范围查询
考虑
1 | cat → 1 |
它的 FST 可能如下
1 | (start) |
MultiValue Index
Tantivy
主要结构
Posting list
在倒排索引中,每一个 term 对应一个 posting list,表示它出现的 docID
1 | "rust" -> [{doc1, tf1, ...}, {doc3, tf3, ...}, ...] |
Fast field
Tantivy 中的普通字段一般是 STORED | INDEXED 属性。其中:
- STORED 表示需要存储原文,以便后续返回结果
存储在 .store 文件中。 - INDEXED 表示要基于这个字段构建倒排索引
会对文本进行分词,并且构建 term -> posting list。
存储在 .fast 文件中。
Tantivy 还支持 FAST 属性。用来加速处理:
- 按数值字段排序
如 order by timestamp desc - 聚合统计
如 avg(price) - 按数值过滤
如 price < 100
Fast Field 仅支持定长、可序列化的类型。例如各种整数、布尔值等,而 TEXT 是不支持的。
FieldType
- TEXT 可分词字符串
- STRING 不可分词字符串
不同的 FieldType 可以指定不同的分词器。
TermList
一个 Term 的表示如下:
- 0..4 是 field id
- 4..5 是 field type
- 5.. 是 payload
真正写进 .term 文件 FST 的 key 不含前 5 字节头,只放 payload 部分。原因:
- 每个字段已经有独立的 .term 文件,field id 无需重复
- 同名字段在不同 segment 的 FST 可以直接合并,而不用担心头部不一致
内存中需要加上 field id 的原因:
- 全局区分同名字段
如title:apple和body:apple。
查询
- TermQuery
精确匹配某个 term。 - PhraseQuery
匹配多个 term 连续出现的短语。 - AllQuery
- RangeQuery
- RegexQuery
- FuzzyQuery
- BooleanQuery
向量搜索和 FTS
Inverted File Index(IVF)
Inverted File Index(IVF) 将向量搜索和倒排索引联系到了一起。
主要思想是:
使用 K-means 算法对向量进行分簇,每一簇的中心点 C1/C2/… 就是倒排表的 term
1
2C1 -> [v1, v5, v7, ...]
C2 -> [v2, v4, v6, ...]对查询向量 q 计算它到所有中心点的距离,选出最接近的几个簇,只在这些簇中寻找最近邻
IVF 的核心思想是 向量在高维空间中是可以粗聚类的。它用的多,原因在于:
- 高维向量在实际应用中通常可聚类
- 可调节性强
如果聚类效果不好,可以增大 nprobe,搜索更多的簇,从而提高召回率。
如果聚类效果好,则可以减小 nprobe,提高搜索的速度。 - 可以和 Product Quantization 联用