前言
近十年間,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 相關的性能問題時,也能遵循相同的路徑,解決業務上的問題。
參考連結
- https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-edgengram-tokenizer.html
- https://segmentfault.com/a/1190000022100153
- https://blog.csdn.net/tiancityycf/article/details/114847911
來源-微信公衆号:當貝技術團隊
出處:https://mp.weixin.qq.com/s/tr9e6jzrP6A6IU3T3wb85g