Milvus Ngram 索引介绍:针对 Agents 工作负载的更快关键词匹配和 LIKE 查询
在 Agents 系统中,上下文检索是整个管道的基础构件,为下游推理、规划和行动提供了基础。向量搜索可以帮助 Agents 检索语义相关的上下文,从而在大型非结构化数据集中捕捉意图和意义。然而,仅有语义相关性往往是不够的。Agents 管道还依赖全文搜索来执行精确的关键字限制,例如产品名称、功能调用、错误代码或具有法律意义的术语。这一支持层可确保检索到的上下文不仅相关,而且明确满足硬性文本要求。
实际工作负载始终反映了这一需求:
客户支持助理必须找到提及特定产品或成分的对话。
编码副驾驶员需要查找包含精确函数名、API 调用或错误字符串的片段。
法律、医疗和学术 Agents 会过滤文档中必须逐字出现的条款或引文。
传统上,系统都是通过 SQLLIKE 操作符来处理的。name LIKE '%rod%' 这样的查询简单,支持范围也很广,但在高并发和大数据量的情况下,这种简单性会带来很大的性能代价。
如果没有索引,
LIKE查询会扫描整个上下文存储,并逐行应用模式匹配。在数百万条记录的情况下,即使是单次查询也需要数秒时间,对于实时代理交互来说,速度实在太慢了。即使使用传统的倒排索引,通配符模式(如
%rod%)仍然难以优化,因为引擎仍然必须遍历整个字典,并对每个条目进行模式匹配。这种操作符避免了行扫描,但从根本上说仍然是线性的,因此只能带来微不足道的改进。
这在混合检索系统中造成了明显的差距:向量搜索能有效处理语义相关性,但精确关键词过滤往往成为管道中最慢的步骤。
Milvus 本机支持带元数据过滤的混合向量和全文检索。为了解决关键字匹配的局限性,Milvus 引入了Ngram 索引,通过将文本拆分成小的子字符串并编制索引以实现高效查找,从而提高了LIKE 的性能。这大大减少了查询执行过程中检查的数据量,使LIKE 查询在实际 Agents 工作负载中的速度提高了数十到数百倍。
本篇文章的其余部分将介绍 Ngram 索引在 Milvus 中的工作原理,并评估其在实际应用场景中的性能。
什么是 Ngram 索引?
在数据库中,文本过滤通常使用SQL 来表达,这是一种用于检索和管理数据的标准查询语言。其最广泛使用的文本操作符之一是LIKE ,它支持基于模式的字符串匹配。
根据通配符的使用方式,LIKE 表达式可大致分为四种常见的模式类型:
Infix match(
name LIKE '%rod%'):匹配子串 rod 出现在文本中任何位置的记录。前缀匹配(
name LIKE 'rod%'):匹配文本以 rod 开头的记录。后缀匹配(
name LIKE '%rod'):匹配文本以 rod 结尾的记录。通配符匹配(
name LIKE '%rod%aab%bc_de'):将多个子字符串条件 (%) 与单字符通配符 (_) 组合在一个模式中。
虽然这些模式在外观和表现力上各不相同,但 Milvus 中的Ngram 索引使用相同的基本方法对所有模式进行了加速。
在建立索引之前,Milvus 会将每个文本值分割成固定长度的重叠短子串,即n-gram。例如,当 n = 3 时,单词"Milvus "被分解成以下 3 个词组:"Mil"、 "ilv"、" lvu "和"vus"。然后将每个 n-gram 保存在一个反向索引中,该索引将子串映射到出现该子串的文档 ID 集。在查询时,LIKE 条件被转化为 n-gram 查找的组合,从而使 Milvus 能够快速过滤掉大部分不匹配的记录,并根据更小的候选集评估模式。这就是将昂贵的字符串扫描转化为高效的基于索引查询的原因。
有两个参数可以控制 Ngram 索引的构建方式:min_gram 和max_gram 。它们共同定义了 Milvus 生成和索引的子串长度范围。
min_gram:要索引的最短子串长度。在实践中,这也设定了可从 Ngram 索引中获益的最小查询子串长度。max_gram:要索引的最长子串长度。在查询时,它还决定了将较长的查询字符串分割成 n-gram 时使用的最大窗口大小。
通过索引长度在min_gram 和max_gram 之间的所有连续子串,Milvus 为加速所有支持的LIKE 模式类型奠定了一致、高效的基础。
Ngram 索引如何工作?
Milvus 分两个阶段实现 Ngram 索引:
建立索引:为每个文档生成 N-grams,并在数据摄取过程中建立反转索引。
加速查询:使用索引将搜索范围缩小到一个小的候选集,然后在这些候选集上验证精确的
LIKE匹配。
一个具体的例子可以让我们更容易理解这一过程。
第 1 阶段:建立索引
将文本分解为 n 个词组:
假设我们用以下设置为文本"Apple "建立索引:
min_gram = 2max_gram = 3
在此设置下,Milvus 会生成长度为 2 和 3 的所有连续子串:
2-grams:
Ap,pp,pl、le3-grams:
App,ppl、ple
建立倒排索引:
现在考虑一个由五条记录组成的小型数据集:
文档 0:
Apple文档 1:
Pineapple文档 2:
Maple文档 3:
Apply文档 4:
Snapple
在摄取过程中,Milvus 会为每条记录生成 n 个词组,并将它们插入一个倒排索引。在这个索引中
键是 n 字符(子串)
值是出现 n-gram 的文档 ID 列表
"Ap" -> [0, 3]
"App" -> [0, 3]
"Ma" -> [2]
"Map" -> [2]
"Pi" -> [1]
"Pin" -> [1]
"Sn" -> [4]
"Sna" -> [4]
"ap" -> [1, 2, 4]
"apl" -> [2]
"app" -> [1, 4]
"ea" -> [1]
"eap" -> [1]
"in" -> [1]
"ine" -> [1]
"le" -> [0, 1, 2, 4]
"ly" -> [3]
"na" -> [4]
"nap" -> [4]
"ne" -> [1]
"nea" -> [1]
"pl" -> [0, 1, 2, 3, 4]
"ple" -> [0, 1, 2, 4]
"ply" -> [3]
"pp" -> [0, 1, 3, 4]
"ppl" -> [0, 1, 3, 4]
现在,索引已完全建立。
第二阶段:加速查询
当执行LIKE 过滤器时,Milvus 使用 Ngram 索引通过以下步骤加速查询评估:
1.提取查询词:从LIKE 表达式中提取不含通配符的连续子串(例如,'%apple%' 变成apple )。
2.分解查询词:根据查询词的长度 (L) 以及配置的min_gram 和max_gram ,将查询词分解为 n 个词组。
3.查找每个语法并进行交集:Milvus 在倒排索引中查找查询的 n 个语法,并与它们的文档 ID 列表进行交集,从而生成一个小的候选集。
4.验证并返回结果:最初的LIKE 条件只适用于这个候选集,以确定最终结果。
实际上,将查询分割成 n-gram 的方式取决于模式本身的形状。为了解其工作原理,我们将重点讨论两种常见情况:后缀匹配和通配符匹配。前缀和后缀匹配的行为与后缀匹配相同,因此我们不再单独介绍。
词缀匹配
对于词缀匹配,执行取决于字面子串 (L) 相对于min_gram 和max_gram 的长度。
min_gram ≤ L ≤ max_gram(如strField LIKE '%ppl%')
字面子串ppl 完全位于配置的 n-gram 范围内。Milvus 直接在倒排索引中查找 n-gram"ppl" ,生成候选文档 ID[0, 1, 3, 4] 。
由于字面意思本身就是一个有索引的 n-gram,因此所有候选文档都已满足下位条件。最后的验证步骤不会删除任何记录,结果仍然是[0, 1, 3, 4] 。
2.L > max_gram(例如,strField LIKE '%pple%')
字面子字符串pple 比max_gram 长,因此使用max_gram 的窗口大小将其分解为重叠的 n 个字符串。加上max_gram = 3 ,就产生了 n 个字符串"ppl" 和"ple" 。
Milvus 在倒排索引中查找每个 n-gram:
"ppl"→[0, 1, 3, 4]"ple"→[0, 1, 2, 4]
将这些列表相交,得到候选集[0, 1, 4] 。然后,原始的LIKE '%pple%' 过滤器将应用于这些候选集。三者都满足条件,因此最终结果仍然是[0, 1, 4] 。
3.L < min_gram(例如,strField LIKE '%pp%')
字面子串短于min_gram ,因此无法分解为索引 n-gram。在这种情况下,不能使用 Ngram 索引,Milvus 会退回到默认执行路径,通过模式匹配的全扫描来评估LIKE 条件。
通配符匹配(如strField LIKE '%Ap%pple%')
该模式包含多个通配符,因此 Milvus 首先将其拆分为连续的字面:"Ap" 和"pple" 。
然后,Milvus 对每个字面进行独立处理:
"Ap"长度为 2,属于 n-gram 范围。"pple"比max_gram长,分解为"ppl"和"ple"。
这就将查询缩减为以下 n 个词组:
"Ap"→[0, 3]"ppl"→[0, 1, 3, 4]"ple"→[0, 1, 2, 4]
将这些列表相交会产生一个候选词:[0] 。
最后,原始LIKE '%Ap%pple%' 过滤器被应用于文档 0 ("Apple")。由于它不符合完整模式,最终结果集为空。
Ngram 索引的局限与权衡
虽然 Ngram 索引可以显著提高LIKE 的查询性能,但它也引入了一些权衡因素,在实际部署中应加以考虑。
- 索引规模增大
Ngram 索引的主要成本是更高的存储开销。由于索引存储的是长度在min_gram 和max_gram 之间的所有连续子串,因此随着这一范围的扩大,生成的 n-gram 数量也会迅速增加。每增加一个 n-gram 长度,就会有效地为每个文本值增加一整套重叠子串,从而增加索引键及其发布列表的数量。实际上,与标准的倒排索引相比,只需扩展一个字符的范围,索引的大小就会增加大约一倍。
- 并非对所有工作负载都有效
Ngram 索引并非对所有工作负载都有效。如果查询模式非常不规则,包含非常短的字面意义,或者在过滤阶段未能将数据集缩小到一个小的候选集,那么性能优势可能会受到限制。在这种情况下,即使存在索引,查询执行的成本仍可能接近全扫描的成本。
评估 LIKE 查询中的 Ngram 索引性能
本基准测试的目的是评估 Ngram 索引在实践中如何有效地加速LIKE 查询。
测试方法
为了使其性能符合实际情况,我们将其与两种基准执行模式进行了比较:
主模式:不带任何索引的强制执行。
主-倒置:使用传统的反转索引执行。
我们设计了两个测试场景,以涵盖不同的数据特征:
维基文本数据集:100,000 行,每个文本字段截断为 1 KB。
单字数据集:1,000,000 行,每行包含一个单词。
在这两种情况下,均采用一致的以下设置:
查询使用后缀匹配模式(
%xxx%)语法索引配置为
min_gram = 2和max_gram = 4为了隔离查询执行成本并避免结果实体化开销,所有查询都返回
count(*),而不是完整的结果集。
查询结果
维基测试,每行是内容长度截断为 1000 的维基文本,100K 行
| 字面意思 | 时间(毫秒) | 加速 | 计数 | |
|---|---|---|---|---|
| 主人 | 体育场 | 207.8 | 335 | |
| 主-倒置 | 2095 | 335 | ||
| Ngram | 1.09 | 190 / 1922 | 335 | |
| 硕士 | 中学 | 204.8 | 340 | |
| 硕士转正 | 2000 | 340 | ||
| Ngram | 1.26 | 162.5 / 1587 | 340 | |
| 大师 | 是一所男女同校的中学。 | 223.9 | 1 | |
| 硕士-转制 | 2100 | 1 | ||
| Ngram | 1.69 | 132.5 / 1242.6 | 1 |
单词测试,1M 行
| 字面 | 时间(毫秒) | 加速 | 计数 | |
|---|---|---|---|---|
| 主控 | na | 128.6 | 40430 | |
| 主-倒置 | 66.5 | 40430 | ||
| Ngram | 1.38 | 93.2 / 48.2 | 40430 | |
| 主 | 态 | 122 | 5200 | |
| 主-反转 | 65.1 | 5200 | ||
| Ngram | 1.27 | 96 / 51.3 | 5200 | |
| 主 | 国家 | 118.8 | 1630 | |
| 主-反转 | 66.9 | 1630 | ||
| Ngram | 1.21 | 98.2 / 55.3 | 1630 | |
| 主 | 国家 | 118.4 | 1100 | |
| 主逆变 | 65.1 | 1100 | ||
| Ngram | 1.33 | 89 / 48.9 | 1100 | |
| 主 | 国家 | 118 | 1100 | |
| 主国倒置 | 63.3 | 1100 | ||
| Ngram | 1.4 | 84.3 / 45.2 | 1100 |
注:这些结果基于 5 月份进行的基准测试。从那时起,主分支进行了更多性能优化,因此在当前版本中观察到的性能差距有望缩小。
基准测试结果凸显了一个清晰的模式:在所有情况下,Ngram 索引都能显著加快 LIKE 查询的速度,而查询运行速度的快慢在很大程度上取决于底层文本数据的结构和长度。
对于长文本字段,如截断为 1,000 字节的维基风格文档,性能提升尤为明显。与没有索引的强制执行相比,Ngram 索引的速度提高了约100-200倍。与传统的倒排索引相比,改进幅度更大,达到了1,200-1,900 倍。这是因为对传统索引方法来说,对长文本进行 LIKE 查询的成本特别高,而 N-gram 查找可以迅速将搜索空间缩小到很小的候选集。
在由单词条目组成的数据集上,收益较小,但仍然可观。在这种情况下,N-gram 索引的运行速度比强制执行快约80-100 倍,比传统的倒排索引快45-55 倍。虽然较短文本的扫描成本较低,但基于 ngram 的方法仍能避免不必要的比较,并持续降低查询成本。
结论
Ngram 索引通过将文本分解为固定长度的 n-gram,并使用倒置结构为其编制索引,从而加快了LIKE 查询的速度。这种设计将昂贵的子串匹配转化为高效的 n-gram 查找,然后进行最少的验证。因此,既避免了全文扫描,又保留了LIKE 的准确语义。
在实践中,这种方法在各种工作负载中都很有效,尤其是在长文本字段的模糊匹配中效果显著。因此,Ngram 索引非常适合代码搜索、客户支持 Agents、法律和医学文档检索、企业知识库和学术搜索等实时场景,在这些场景中,精确的关键词匹配仍然至关重要。
同时,Ngram 索引还得益于精心的配置。选择合适的min_gram 和max_gram 值对于平衡索引大小和查询性能至关重要。根据实际查询模式进行调整后,Ngram 索引可为生产系统中的高性能LIKE 查询提供实用、可扩展的解决方案。
有关 Ngram 索引的更多信息,请查看下面的文档:
对最新 Milvus 的任何功能有疑问或想深入了解?加入我们的 Discord 频道或在 GitHub 上提交问题。您还可以通过 Milvus Office Hours 预订 20 分钟的一对一课程,以获得见解、指导和问题解答。
了解有关 Milvus 2.6 功能的更多信息
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word



