Elasticsearch 檢索性能優(yōu)化實(shí)戰(zhàn)指南
1、當(dāng)我們在說 Elasticsearch 檢索性能優(yōu)化的時(shí)候,實(shí)際在說什么?!
檢索響應(yīng)慢! 并發(fā)檢索用戶多時(shí),響應(yīng)時(shí)間不達(dá)標(biāo) 卡死了! 怎么還沒有出結(jié)果? 怎么這么慢? 為啥競品產(chǎn)品的很快就返回結(jié)果了? 宕機(jī)了
等等......
這些都與可能檢索有關(guān),確切的說和檢索性能有關(guān)。
檢索性能的優(yōu)化涉及知識(shí)點(diǎn)比較零散,我以官方文檔的檢索性能優(yōu)化部分作為大框架和主線,結(jié)合實(shí)戰(zhàn)經(jīng)驗(yàn)和咨詢經(jīng)驗(yàn)用通俗易懂的語言做下解讀。
2、內(nèi)存要給到位
Elasticsearch 嚴(yán)重依賴文件系統(tǒng)緩存來加快搜索速度。通常,你應(yīng)該確保至少有一半的可用內(nèi)存進(jìn)入文件系統(tǒng)緩存,以便 Elasticsearch 可以將索引的熱點(diǎn)區(qū)域保留在物理內(nèi)存中。
線上環(huán)境還見過2核4G配置的,基本上跑不了太多數(shù)據(jù)量。
推薦閱讀:干貨 | 吃透Elasticsearch 堆內(nèi)存
3、磁盤必要時(shí)換 SSD
對寫入速度有超高要求的,SSD就是“祥瑞”。
SSD 成本考慮可能不能一步到位,但至少得是普通機(jī)械磁盤。
切記盡量不用:NFS 或 SMB 遠(yuǎn)程文件系統(tǒng)。
4、CPU 考慮核數(shù)和線程數(shù)
在并發(fā)寫入或查詢量大之后,就會(huì)出現(xiàn) CPU 打滿的情況。
可以優(yōu)化的空間就是:基于CPU 核數(shù)合理調(diào)節(jié)線程池和隊(duì)列的大小。
推薦閱讀:
Elasticsearch 線程池和隊(duì)列問題,請先看這一篇
5、數(shù)據(jù)建模要合理
多表關(guān)聯(lián)非 Elasticsearch 所擅長。換句話說,Elasticsearch 支持多表關(guān)聯(lián)方式有限。
像 Mysql 中的動(dòng)不動(dòng)幾個(gè)表的 join 操作,在 Elasticsearch 要考慮必要性和實(shí)現(xiàn)復(fù)雜度。
Elasticsearch 多表關(guān)聯(lián)僅限于如下幾種:
父子文檔 join:適用于子文檔頻繁更新場景。 nested 嵌套類型:適用于子文檔相對固定、更新頻率低的場景。 大寬表拉伸存儲(chǔ):本質(zhì)空間換時(shí)間。 業(yè)務(wù)層面自己結(jié)合檢索后的返回結(jié)果,自己實(shí)現(xiàn)關(guān)聯(lián)。
且:Nested 可以使查詢慢幾倍,而父子 Join 類型可以使查詢慢數(shù)百倍。
大家在建模的時(shí)候多考慮,如果不刻意建模全部使用默認(rèn)字段,看看可能帶來的“災(zāi)難”性的后果,反過來就能理解建模的重要性。
建模細(xì)節(jié)點(diǎn)有很多,推薦閱讀:
干貨 | 論Elasticsearch數(shù)據(jù)建模的重要性
Elasticsearch 數(shù)據(jù)建模實(shí)戰(zhàn)指南
干貨 | Elasticsearch多表關(guān)聯(lián)設(shè)計(jì)指南
6、盡可能減少檢索字段數(shù)目
query_string 或 multi_match 查詢目標(biāo)的字段越多,速度就越慢。
提高多個(gè)字段搜索速度的常用技術(shù)是在索引時(shí)將它們的值借助 copy_to 復(fù)制到單個(gè)字段中,然后在搜索時(shí)使用該字段。
copy_to 實(shí)現(xiàn)了 1 帶 2 、1 帶 3 甚至 1 帶 N 的效果。
7、合理設(shè)置 size 大小
在檢索請求的時(shí)候 size 值設(shè)置很大,會(huì)導(dǎo)致命中數(shù)據(jù)量大,可能會(huì)帶來嚴(yán)重的性能問題。
建議:合理設(shè)置分頁 size 值。如果著實(shí)數(shù)據(jù)量大考慮:scroll 或者 search_after 實(shí)現(xiàn)。
推薦參考:
干貨 | 全方位深度解讀 Elasticsearch 分頁查詢
8、多使用寫入前預(yù)處理操作
我之前的文章講情感分析區(qū)間查詢的時(shí)候,其實(shí)本質(zhì)就三個(gè)區(qū)間:負(fù)面、正面、中性。
如果用 range_query 區(qū)間檢索勢必會(huì)慢。
建模的時(shí)候,可以考慮數(shù)據(jù)寫入的時(shí)候,轉(zhuǎn)成:-1、0、1 的 keyword 類型值。
將 range_query 的范圍檢索變成了基于倒排索引的精準(zhǔn)查找 term query,效率自然會(huì)提升。
能借助 ingest 預(yù)處理完成的,不要放到后面借助 script + update_by_query。
使用過 script + update_by_query的自然知道有多苦。
看到這里有同感的老鐵可以留言說一下感受。
推薦閱讀:
Elasticsearch 運(yùn)行時(shí)類型 Runtime fields 深入詳解
9、能用 keyword 字段類型就不要使用其他
如果一個(gè)字段可以設(shè)置為:number 數(shù)值類型字段也可以設(shè)置為 keyword 類型。在建模階段可以參考如下考慮方式實(shí)現(xiàn)建模選型。
得看你的應(yīng)用場景,如果涉及 range query 推薦 number 類型,具體可以:integer、long 或者其他數(shù)值類型。
如果僅是精準(zhǔn)匹配 term 級(jí)別的檢索,那 keyword 就能搞定。
如果還感覺兩種都有可能,建議設(shè)置:keyword 和 number 雙類型,借助 fields 實(shí)現(xiàn)。
fields 組合類型實(shí)現(xiàn)參考如下:
PUT?test_0001
{
??"mappings":?{
????"properties":?{
??????"age":{
????????"type":"integer",
????????"fields":?{
??????????"keyword":{
????????????"type":"keyword"
??????????}
????????}
??????}
????}
??}
}
10、盡量規(guī)避使用腳本
如果可能,請避免使用:
基于腳本的排序 基于腳本的聚合 基于script_score 查詢
painless 腳本翻譯為中文是:“無痛”。
但,用過你就知道有多痛。
第一:不是很好用,很多示例,官方文檔也沒有窮盡所有樣例,需要花時(shí)間摸索。 第二:性能問題,解決問題一時(shí)爽,線上一跑“悔斷腸”。

盡量前置寫入的時(shí)候結(jié)合 ingest + script 實(shí)現(xiàn)。
如果對寫入指標(biāo)要求沒有那么高,通過稍微增大寫入的時(shí)間間接提高了檢索效率,何樂而不為?
推薦閱讀:
Elasticsearch 線上問題實(shí)戰(zhàn)——如何借助 painless 更新時(shí)間?
11、日期做舍入處理,能間接利用緩存
GET?index/_search
{
??"query":?{
????"constant_score":?{
??????"filter":?{
????????"range":?{
??????????"my_date":?{
????????????"gte":?"now-1h/m",
????????????"lte":?"now/m"
??????????}
????????}
??????}
????}
??}
}
“/m” 的方式能有效利用時(shí)間緩存。
推薦閱讀:
12、有效使用 filter 緩存
在 Elasticsearch 查詢中有效使用 filter 過濾器可以顯著提高搜索性能。
filter 過濾優(yōu)勢體現(xiàn)在:
緩存。 和query 相比,不需要計(jì)算評分,所以更快。
吃透 | Elasticsearch filter和query的不同
13、對歷史索引數(shù)據(jù)使用段合并
前提:基于時(shí)間切分索引,對于相對冷的數(shù)據(jù),訪問密集型沒有那么高的數(shù)據(jù),推薦使用段合并。
切記:不要對正在寫入數(shù)據(jù)的索引進(jìn)行段合并。
7.X 版本可以借助 ILM 索引生命周期管理實(shí)現(xiàn)。
推薦閱讀:
14、啟用 eager global ordinals 提升高基數(shù)聚合性能
適用場景:高基數(shù)聚合。
高基數(shù)聚合場景中的高基數(shù)含義:一個(gè)字段包含很大比例的唯一值。
global ordinals 的本質(zhì)是:啟用 eager_global_ordinals 時(shí),會(huì)在刷新(refresh)分片時(shí)構(gòu)建全局序號(hào)。這將構(gòu)建全局序號(hào)的成本從搜索階段轉(zhuǎn)移到了數(shù)據(jù)索引化(寫入)階段。
PUT?my-index-000001
{
??"mappings":?{
????"properties":?{
??????"tags":?{
????????"type":?"keyword",
????????"eager_global_ordinals":?true
??????}
????}
??}
}
15、預(yù)熱文件系統(tǒng)緩存
如果重新啟動(dòng)運(yùn)行 Elasticsearch 的機(jī)器,文件系統(tǒng)緩存將是空的,因此操作系統(tǒng)將索引的熱點(diǎn)區(qū)域加載到內(nèi)存中需要一些時(shí)間,以便快速搜索操作。
可以使用 index.store.preload 設(shè)置根據(jù)文件擴(kuò)展名明確告訴操作系統(tǒng)哪些文件應(yīng)該立即加載到內(nèi)存中。
PUT?/my-index-000001
{
??"settings":?{
????"index.store.preload":?["nvd",?"dvd"]
??}
}
在 lucene 中,
nvd 是指:全文檢索文件; dvd 是指:用于聚合排序的列存文件。
16、合理使用 index sort 邊寫入邊排序機(jī)制
PUT?my-index-000001
{
??"settings":?{
????"index":?{
??????"sort.field":?"date",?
??????"sort.order":?"desc"??
????}
??},
??"mappings":?{
????"properties":?{
??????"date":?{
????????"type":?"date"
??????}
????}
??}
}
這個(gè)配置相信你一看就懂,發(fā)生在寫入前,創(chuàng)建索引的時(shí)候設(shè)定的排序字段。
本質(zhì):通過降低寫入速度間接提升檢索速度。
17、通過 perference 優(yōu)化緩存利用率
perference 用在兩次檢索結(jié)果不一致的時(shí)候,本質(zhì)是:主、副本分片數(shù)據(jù)不一致導(dǎo)致的,有半路由的機(jī)制。
合理使用 perference 參數(shù)能優(yōu)化緩存使用率。
18、設(shè)置合理的分片數(shù)和副本數(shù)
主分片的設(shè)置需要結(jié)合:集群數(shù)據(jù)節(jié)點(diǎn)規(guī)模、全部數(shù)據(jù)量和日增數(shù)據(jù)量等綜合維度給出值,一般建議:設(shè)置為數(shù)據(jù)節(jié)點(diǎn)的1-3倍。
分片不宜過小、過碎。有很多小分片可能會(huì)導(dǎo)致大量的網(wǎng)絡(luò)調(diào)用和線程開銷,這會(huì)嚴(yán)重影響搜索性能。
副本數(shù)不是越多越好?
在許多情況下,擁有更多副本有助于提高搜索性能。但是不代表副本越多越好。
增加副本的前提是考慮:磁盤存儲(chǔ)空間的容量上限和磁盤警戒水位線。本質(zhì)還是以空間換時(shí)間。
一般非高可用場景,基本一個(gè)副本足夠。
官方給出了合理副本大小的公式供參考:
如果你的集群有 num_nodes 個(gè)節(jié)點(diǎn)、num_primaries 主分片,并且你希望最多同時(shí)處理 max_failures 個(gè)節(jié)點(diǎn)故障,那么適合你的副本數(shù)為:
?max(max_failures,?ceil(num_nodes?/?num_primaries)?-?1)
19、避免使用 wildcard 檢索
避免使用 wildcard 通配符檢索,尤其是前綴通配符查詢。
我自己早些年線上環(huán)境實(shí)現(xiàn)曾經(jīng)大量使用:wildcard,導(dǎo)致客戶現(xiàn)場演示宕機(jī),我自己因此也寫了“檢討書”,血淋淋的教訓(xùn)再次告訴大家。
幾年后回頭看當(dāng)時(shí)為什么選型 wildcard?復(fù)盤原因小結(jié)如下:
MySql 的使用慣性。
MySql 中 select * from table where title like ‘%長津湖%'。順理成章的認(rèn)為 Elasticsearch 中的 wildcard 也能實(shí)現(xiàn)類型功能。
對 Elasticsearch 不求甚解。
能簡單使用且測試環(huán)境小樣沒有問題,直接更新線上環(huán)節(jié)。客戶現(xiàn)場數(shù)據(jù)一多,直接崩潰。
為達(dá)目的,功能優(yōu)先,沒有考慮性能。
產(chǎn)品經(jīng)理要求字段中存在的字符都能檢索出來。
Elasticsearch 本質(zhì)是倒排索引提高檢索效率,如果分詞詞典不完備,除非 ngram 逐個(gè)字符細(xì)粒度分詞,否則幾乎做不到的。
wildcard 功能方面必然能滿足,但是性能問題當(dāng)時(shí)沒有做大量測試。
沒有對 Elasticseearch 全局認(rèn)識(shí)
全局看,Elasticsearch 就那么幾種類型,全文檢索類型、精準(zhǔn)匹配類型是重頭戲。
當(dāng)時(shí)選型的時(shí)候,摸著石頭過河,拿起石頭就用,結(jié)果石頭有“刺“,把手給扎了。
更好的方式應(yīng)該是:全局認(rèn)識(shí),有幾種類型石頭?哪里有石頭?石頭應(yīng)用場景是什么?我的業(yè)務(wù)需要哪種類型的石頭?
后面優(yōu)化的方案就是:字詞混合索引 + match_phrase 短語匹配實(shí)現(xiàn),一方面保證了匹配的精準(zhǔn)性,另一方面保證了召回率。
推薦閱讀:
Elasticsearch 警惕使用 wildcard 檢索!然后呢?
20、謹(jǐn)慎使用 Regex 正則檢索
正則檢索也會(huì)有響應(yīng)慢及性能問題,要謹(jǐn)慎使用。
21、謹(jǐn)慎使用全量聚合和多重嵌套聚合
聚合的本質(zhì)是不精準(zhǔn)的,原因在于主、副本分片數(shù)據(jù)的不一致性。
對于實(shí)時(shí)性業(yè)務(wù)數(shù)據(jù),每分、每秒都有數(shù)據(jù)寫入的,要考慮數(shù)據(jù)在變化,聚合結(jié)果也會(huì)隨之變化。
我在業(yè)務(wù)開發(fā)中使用全量聚合的目的是規(guī)避聚合結(jié)果的不精準(zhǔn)性,但是帶來的則是性能問題。
多重嵌套聚合隨之嵌套層數(shù)的增多,復(fù)雜度也會(huì)激增,檢索響應(yīng)速度會(huì)變慢甚至帶來性能問題。
推薦閱讀:
Elasticsearch 聚合數(shù)據(jù)結(jié)果不精確,怎么破?
22、設(shè)置合理的 Timeout 時(shí)間
超時(shí)參數(shù)和在參數(shù)后終止在執(zhí)行大量搜索或結(jié)果數(shù)據(jù)龐大時(shí)非常有用。
在 python 客戶端或者 java 客戶端連接的時(shí)候都建議設(shè)置好 Timeout 值。
23、合理設(shè)置刪除文檔的方式
當(dāng)數(shù)據(jù)量非常大了之后怎么辦?兩種方式做一下對比:
方式一:大索引存儲(chǔ)。
數(shù)據(jù)量大了之后,刪除部分索引數(shù)據(jù),借助:delete_by_uery 實(shí)現(xiàn)。
方式二:冷熱集群架構(gòu)+基于時(shí)間切分索引。
必要時(shí)候,刪除較早日期的索引,借助:delete 實(shí)現(xiàn)。
方式一本質(zhì)是邏輯刪除,數(shù)據(jù)看似刪除了,但磁盤空間短期內(nèi)會(huì)暴增。待段合并后,才會(huì)物理刪除。
方式二本質(zhì)是物理刪除,刪除索引會(huì)立即釋放磁盤。
所以,當(dāng)磁盤空間吃緊,尤其到了警戒水位線:85%、90%、95%之后。
方式二刪除:穩(wěn)、準(zhǔn)、狠。而方式一相對“磨磨唧唧、娘娘們們”,非必要不推薦。
推薦閱讀:
24、小結(jié)
Elasticsearch 檢索性能優(yōu)化,斷斷續(xù)續(xù)寫過幾篇,但都不夠系統(tǒng)。
本次,結(jié)合官方文檔+其他幾篇參考文獻(xiàn)+實(shí)戰(zhàn)經(jīng)驗(yàn)進(jìn)行綜合梳理,期望能給大家?guī)硎斋@。
性能優(yōu)化非一朝一夕之功,本文并沒有窮盡所有檢索優(yōu)化細(xì)節(jié),更多實(shí)踐細(xì)節(jié)需要大家結(jié)合業(yè)務(wù)實(shí)際進(jìn)行嘗試、探索、發(fā)現(xiàn)。
你在業(yè)務(wù)開發(fā)中如何優(yōu)化查詢性能的呢?歡迎留言交流。
參考
https://www.elastic.co/guide/en/elasticsearch/reference/master/tune-for-search-speed.html?
https://opster.com/blogs/improve-elasticsearch-search-performance/?
https://opster.com/elasticsearch-glossary/elasticsearch-slow-search-query-guide/?
https://medium.com/analytics-vidhya/improving-elasticsearch-query-performance-3b59c6b15a97?
https://opster.com/elasticsearch-glossary/elasticsearch-increase-search-speed/
推薦
更短時(shí)間更快習(xí)得更多干貨!
已帶領(lǐng)70位球友通過 Elastic 官方認(rèn)證!
中國僅通過百余人

