天天看點

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