为什么ES中不推荐使用wildcard查询

在lucene中AutomatonQuery类的作用中,我们说了 AutomatonQuery 性能很高。

那么,为什么 Elasticsearch 的官方文档和所有最佳实践都强烈建议不要使用前缀通配符(leading wildcard) ,比如 *text?

答案在于 AutomatonQuery 的高效是有条件的,而前缀通配符恰好破坏了这个条件。


核心原因:与有序词项词典(Term Dictionary)的交互方式

Lucene 的性能秘诀在于它的倒排索引,其中一个关键部分是词项词典(Term Dictionary) 。这个词典存储了索引中所有唯一的词项,并且是按字典序(alphabetically)排序的。

这个"按字典序排序"的特性至关重要。我们来对比两种通配符查询:

1. 后缀通配符查询(Trailing Wildcard)- 性能很高 (text*)

当你执行一个像 text* 这样的查询时:

  1. 构建自动机 :Lucene 构建一个自动机,它匹配以 text 开头的任何字符串。
  2. 遍历词典 :由于词典是按字典序排序的,所有以 text 开头的词项(如 textbook, testing, texture)都连续地存放在一起。
  3. 高效查找 :Lucene 的查询执行器可以非常高效地:
    • **直接定位(Seek)**到词典中第一个以 text 开头的词项。这就像在字典里查 "text" 这个单词一样快。
    • 然后,它只需要顺序扫描 该位置之后的少量词项,直到遇到第一个不以 text 开头的词项为止。
    • 整个过程非常快,因为它只需要检查词典中一个很小的、连续的片段。

这正是 AutomatonQuery 高性能的典型场景。

2. 前缀通配符查询(Leading Wildcard)- 性能灾难 (*text)

当你执行一个像 *text 这样的查询时:

  1. 构建自动机 :Lucene 构建一个自动机,它匹配以 text 结尾的任何字符串。
  2. 遍历词典 :问题来了。因为词典是按开头 字母排序的,所以以 text 结尾的词项(如 context, subtext, hypertext)会分散在整个词典的各个角落。
  3. 低效查找 :为了找到所有匹配项,AutomatonQuery 别无选择,只能:
    • 从词典的第一个词项(比如 "a")开始。
    • 逐个检查词典中的每一个词项 ,看它是否以 text 结尾。
    • 这个过程会一直持续到词典的最后一个词项。

这相当于对整个字段的词项词典进行了一次全量扫描(Full Scan)。

这为什么在 Elasticsearch 中是灾难性的?

  • 规模放大:一个 Lucene 索引(在 ES 中称为分片/Shard)的词项词典可能包含数百万甚至数千万个唯一词项。对单个分片进行全量扫描已经非常耗费 CPU。
  • 分布式特性 :一个 Elasticsearch 索引通常由多个分片组成,这些分片分布在集群的多个节点上。当你执行一个 *text 查询时,这个全量扫描的操作会在所有相关的分片上并行执行。
  • 集群影响 :这个查询会瞬间占用大量 CPU 资源,导致集群的查询延迟飙升,影响所有其他正在进行的查询,甚至可能因为负载过高而导致节点不稳定。它被称为"Query of Death"(死亡查询)之一。

总结:理论与实践的鸿沟

  • 理论上 :AutomatonQuery 本身是一个高效的算法。
  • 实践中 :它的性能高度依赖于它所操作的数据结构。当与一个有序 的词典结合使用时,对于前缀友好 的模式(如 text*),它可以利用"有序"这一特性进行快速定位,性能极高。但对于前缀不友好 的模式(如 *text),它无法利用"有序"特性,被迫退化为全量扫描,性能极差。

Elasticsearch 作为一个大规模、多租户的系统,必须优先保证整个集群的稳定性和性能。因此,它强烈建议避免那些会导致资源浩劫的操作,而前缀通配符查询正是其中的典型代表。


那么,如何解决需要匹配"中间"或"结尾"的需求?

既然直接用 *text 不行,Elasticsearch 社区发展出了一些标准的解决方案,核心思想是在索引时做一些额外的工作,将慢查询转换为快查询。

  1. reverse Token Filter(反转词元过滤器) :

    这是解决前缀通配符最经典的方法。

    • 索引时 :对原始词项(如 context)建立一个反转后的版本(txetnoc)并存入另一个子字段。
    • 查询时 :当用户想搜 *text 时,你将查询条件反转为 txet*,然后去查询那个反转后的字段。
    • 效果 :一个慢速的、全量扫描的前缀通配符查询 ,变成了一个高速的、可以快速定位的后缀通配符查询。
  2. n-gram Token Filter(N元分词过滤器):

    • 索引时 :将一个词项(如 text)拆分成一系列的小片段(n-grams)。例如,2-grams 会拆成 te, ex, xt;3-grams 会拆成 tex, ext。
    • 查询时 :当用户搜索 *text* 或 *tex* 时,可以被转换为对 tex 这个 n-gram 的精确匹配查询,速度非常快。
    • 缺点:会极大地增加索引的体积。

AutomatonQuery 本身是把好刀,但用它来砍一个四处分散的目标(前缀通配符),就只能把整个场地犁一遍了。而用它来砍一个整齐排列的目标(后缀通配符),则快如闪电。Elasticsearch 的建议正是基于这种实际应用场景的深刻理解。

相关推荐
greatofdream4 小时前
git rebase时如何解决merge节点冲突问题
大数据·git·elasticsearch
SelectDB技术团队14 小时前
冷热分层从建到验:Storage Policy 创建、表级与分区级绑定、SHOW TABLETS 验证的完整命令
大数据·数据库·elasticsearch·搜索引擎·数据分析·apache doris·冷热分层
Sayai17 小时前
DataX 离线数据同步实战:Elasticsearch 数据批量导出 MySQL(作业配置、并发调优与定时同步平台化设计)
大数据·mysql·elasticsearch·datax·数据同步
Elastic 中国社区官方博客19 小时前
Kibana:创建包含时间序列图表的仪表板
大数据·elasticsearch·搜索引擎·信息可视化·全文检索·kibana
Elastic 中国社区官方博客21 小时前
Elastic 线下 Meetup 将于 2026 年 12 月 19 号下午在西安举行
大数据·数据库·elasticsearch·搜索引擎·ai·数据分析·全文检索
SelectDB技术团队1 天前
存算分离建表与 Storage Vault 配置全流程:日志集群从建库到弹性扩缩容的命令链
elasticsearch·日志·数据湖·apache doris·日志数据·数据库选型·多模湖仓
今年下半年1 天前
【Spring Boot】集成 Elasticsearch:中文分词与关键字高亮对接文档
spring boot·elasticsearch·中文分词
jonyleek1 天前
JVS私有化文档系统运维避坑指南:5大断点检测与加固实践(K8S+MinIO+ES)
elasticsearch·minio·clamav·jvs·k8s运维·文档系统·权限同步
骑着蜗牛撵大象3271 天前
CentOS 7 单机拉起 Elasticsearch 7.17.29:从内核报错到公网可达的全链路记录
elasticsearch·centos·jenkins·restful·内核调优·公网部署
打工仔折腾 AI1 天前
CentOS 7 单机部署 Elasticsearch 7.17.29:启动报错排查与 CRUD 验证
人工智能·后端·python·elasticsearch·性能优化·centos·jenkins