天天看点

ES使用edge n-gram实现高效前缀搜索

作者:闪念基因
ES使用edge n-gram实现高效前缀搜索

前言

近十年间,Elasticsearch 凭借其卓越性能,已成为市场上备受青睐的开源搜索和数据分析引擎。它不仅在离线数据仓库、实时检索以及面向企业客户的搜索服务中扮演着核心角色,而且积累了丰富的实战经验和优化成果。

尽管如此,在高并发、高可用性以及处理大规模数据等面向消费者端的复杂场景中,相关的优化资料仍相对稀缺。为此,我们希望通过在大屏投影搜索领域的具体优化案例,为同行提供宝贵的参考,以启发大家在 Elasticsearch 优化方面的创新思维和实践。

当贝在影视搜索领域广泛应用了 Elasticsearch 作为其核心检索引擎,并成功应对了过去几年春晚期间的海量搜索请求。然而,随着内容和数据量的飞速增长,业务处理时间和 CPU 负载也随之增加。经过深入分析,我们发现性能瓶颈主要出现在前缀搜索环节。为解决这一问题,我们采用了基于 edge n-gram 的分词策略来优化前缀搜索,大幅提升了查询效率。

背景

在当前的影视内容搜索业务中,Elasticsearch 作为主要的检索引擎,承担着至关重要的角色。为了提供更加灵活和便捷的搜索体验,业务逻辑中特别设计了针对用户输入的三个字及以下的查询条件,采用了一种特别的搜索机制,即通配符前缀搜索。

具体来说,当用户在搜索框中输入三个字或更少的字符时,系统会自动执行一个基于输入内容的前缀匹配查询,迅速检索出与用户输入的前缀相匹配的影片名称。这种设计充分考虑了用户在快速输入时的需求,使得搜索结果能够即时反映用户的查询意图,从而极大地提升了用户体验和搜索效率。通过这种方式,用户能够更加快速地找到他们所期望的影视内容,而无需完整输入整个影片名称,这在用户进行快速浏览和选择时显得尤为重要。

前缀搜索的一些痛点

查询语句示例如下:

POST /xy_test_pinyin_ik/_search
{
  "query": {
    "wildcard": {
      "text": {
        "value": "杭州当贝*"
      }
    }
  }
}
           

这样搜索通常会带来如下问题:

  • 通配符查询,尤其是那些在字符串开头使用通配符的查询,会导致 Elasticsearch 遍历大量索引项,这可能会导致查询速度变慢。
  • 由于通配符查询的复杂性,它们通常无法利用索引的优化特性,如倒排索引和缓存。
  • 当使用前缀查询时,如果前缀的长度越长,那么能匹配的文档相对越少,性能会好一些,如果前缀太短,只有一个字符,那么匹配数据量太多,会影响性能。
  • Elasticsearch 的查询缓存(如查询缓存和过滤器缓存)通常无法用于通配符查询,因为通配符查询的结果可能会因索引的更改而频繁变化。
  • 通配符查询可能会导致 Elasticsearch 使用更多的内存,因为它需要保持更多的状态来处理复杂的匹配逻辑。

什么是 n-gram

n-gram 是一种语言模型,它通过将文本划分为连续的 n 个字符序列来工作。

例如,对于单词 “Elasticsearch”:

  • 2-gram ("bigram") 会将其划分为 “El”, “la”, “as”, “si”, “ic”, “ch”, “he”, “es”, “se”。
  • 3-gram ("trigram") 则会划分为 “Ela”, “las”, “asi”, “sic”, “ich”, “che”, “hes”, “ese”。

什么是 edge_ngram

edge_ngram 是一种专为前缀匹配设计的 n-gram 分词器,它仅从文本的开头生成 n-gram。这种方法对于提升搜索性能、缩小索引体积以及增强用户体验非常有效,尤其是在自动完成和建议功能中,能够迅速返回与用户输入文本开头匹配的词语。

edge_ngram 的优势主要包括:

  • 快速前缀匹配:仅索引每个词的前缀部分,使得 Elasticsearch 能够迅速定位所有以用户输入字符开头的词语。
  • 提升搜索性能:生成的索引项数量较少,前缀查询因此更快,只需搜索索引的子集。
  • 减少索引大小:与存储整个词或使用常规 n-gram 分词器相比,edge_ngram 生成的索引更小,节省磁盘空间和内存。
  • 优化的自动完成体验:为快速响应用户输入并提供相关建议而设计,确保即时反馈。
  • 提高搜索相关性:专注于前缀匹配,确保只返回以用户输入词语开头的搜索结果。
  • 易于配置:通过调整 min_gram 和 max_gram 参数,轻松控制 n-grams 的大小,满足特定搜索需求。
  • 多语言支持:不依赖于特定语言的规则,适用于任何语言的前缀生成。
  • 降低不必要的匹配:通过精确的前缀匹配,减少无关搜索结果的出现,提高搜索精度。
  • 通过这些优势,edge_ngram分词器成为提高搜索效率和用户体验的理想选择。

通过应用 edge_ngram 分词器,我们可以将单词 "Elastic" 分解为一组以单词开头为基础的子字符串,具体如下:

  • 长度1:E
  • 长度2:El
  • 长度3:Ela
  • 长度4:Elas
  • 长度5:Elast
  • 长度6:Elasti
  • 长度7:Elastic

使用 edge_ngram 进行前缀搜索

1. 创建一个带有 edge ngram 分词器的索引

PUT xy_test_pinyin_ik
{
  "settings": {
    "number_of_shards": 1, 
    "number_of_replicas": 0, 
    "index": {
      "max_ngram_diff": 10
    },
    "analysis": {
      "analyzer": {
        "custom_analyzer": {
          "tokenizer": "custom_tokenizer"
        }
      },
      "tokenizer": {
        "custom_tokenizer": {
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 7
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "text": {
        "type": "text",
        "analyzer": "custom_analyzer"
      }
    }
  }
}
           

过滤器会生成一个最小固定值为1,最大为7的ngram。

2. 使用已经创建好的custom_tokenizer分词器查看分词结果

得到的结果符合预期

POST /xy_test_pinyin_ik/_analyze
{
  "text": "杭州当贝网络科技有限公司",
  "analyzer": "custom_analyzer"
}
           

结果:

{
  "tokens" : [
    {
      "token" : "杭",
      ...
      "position" : 0
    },
    {
      "token" : "杭州",
      ...
      "position" : 1
    },
    {
      "token" : "杭州当",
      ...
      "position" : 2
    },
    {
      "token" : "杭州当贝",
      ...
      "position" : 3
    },
    {
      "token" : "杭州当贝网",
      ...
      "position" : 4
    },
    {
      "token" : "杭州当贝网络",
      ...
      "position" : 5
    },
    {
      "token" : "杭州当贝网络科",
      ...
      "position" : 6
    }
  ]
}
           

3. 写入数据

POST /xy_test_pinyin_ik/_doc/1
{"text": "杭州当贝网络科技有限公司"}
{"text": "杭州"}
           

4. 查询数据

如果用 match,只有杭州的也会出来,全文检索,只是分数比较低。

推荐使用 match_phrase,要求每个 term 都有,而且 position 刚好靠着 1 位,符合我们的期望的。

GET /xy_test_pinyin_ik/_search
{
  "query": {
    "match_phrase": {
      "text": "杭州当贝"
    }
  }
}
           

结果:

{
  ...
  "hits" : {
    ...
    "hits" : [
      {
        "_index" : "xy_test_pinyin_ik",
        ...
        "_source" : {
          "text" : "杭州当贝网络科技有限公司"
        }
      }
    ]
  }
}
           

总结

本文主要针对搜索业务场景中遇到的问题,进行问题分析、技术选型、选择合适的解决方案、集成、验证。我们最终使用了edge n-gram,彻底解决了这个场景上的性能瓶颈。本文希望能提供一个思路,让其他同学在遇到 Elasticsearch 相关的性能问题时,也能遵循相同的路径,解决业务上的问题。

参考链接

  1. https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-edgengram-tokenizer.html
  2. https://segmentfault.com/a/1190000022100153
  3. https://blog.csdn.net/tiancityycf/article/details/114847911

来源-微信公众号:当贝技术团队

出处:https://mp.weixin.qq.com/s/tr9e6jzrP6A6IU3T3wb85g