問題
? ? ? ? 生產(chǎn)環(huán)境頻繁報(bào)警。查詢跨度91天的數(shù)據(jù),請求耗時(shí)已經(jīng)來到了30+s。報(bào)警的閾值為5s。我們期望值是5s內(nèi),大于該閾值的請求,我們認(rèn)為是慢查詢。這些慢查詢,最終排查,是因?yàn)樽叩搅藲v史集群上。受到了數(shù)據(jù)遷移的一定影響,也做了一些優(yōu)化,最終從30s提升到5s。
背景
查詢關(guān)鍵詞簡單,為‘北京’
單次僅檢索兩個(gè)字段
查詢時(shí)間跨度為91天,覆蓋數(shù)據(jù)為450億數(shù)據(jù)
問題分析
使用profle分析,復(fù)現(xiàn)監(jiān)控報(bào)警的語句,確實(shí)慢。集群分片太多,這里放一個(gè)分片的內(nèi)容。
{
"id" : "[YWAxM5F9Q0G1PXfTtYZKkzQ][_20230921-000001][3]",
"searches" : [
{
"query" : [
{
"type" : "FunctionScoreQuery",
"description" : "function score (+((title:北京)^2.0 | content:北京) +publish_time:[1687431307000 TO 1695254417999] +es_insert_time:[-9223372036854775808 TO 1703084327999], functions: [{scriptScript{type=stored, lang='null', idOrCode='search-score', options=null, params={}}}])",
"time" : "10s",
"time_in_nanos" : 10079315883,
"breakdown" : {
"set_min_competitive_score_count" : 0,
"match_count" : 150,
"shallow_advance_count" : 0,
"set_min_competitive_score" : 0,
"next_doc" : 2646164,
"match" : 996954485,
"next_doc_count" : 154,
"score_count" : 31,
"compute_max_score_count" : 0,
"compute_max_score" : 0,
"advance" : 1035917137,
"advance_count" : 16,
"score" : 3532211704,
"build_scorer_count" : 40,
"create_weight" : 3965124112,
"shallow_advance" : 0,
"create_weight_count" : 1,
"build_scorer" : 546462281
},
在Elasticsearch?Profile?API結(jié)果中,主要關(guān)注查詢的time和breakdown字段,這提供了查詢執(zhí)行的總時(shí)間和各個(gè)步驟的時(shí)間分解。在這個(gè)例子中,查詢的總時(shí)間為10秒。
具體來看,主要的時(shí)間花費(fèi)在FunctionScoreQuery的create_weight步驟上,該步驟耗時(shí)為3,965,124,112納秒(大約3.97秒)。create_weight是在查詢執(zhí)行之前創(chuàng)建用于評分的權(quán)重的階段。
以下是一些步驟的關(guān)鍵信息:
- create_weight步驟耗時(shí)最長,可能是性能瓶頸。
- build_scorer步驟也占用了大量時(shí)間,這通常是評分過程中的一個(gè)重要步驟。
- next_doc步驟的時(shí)間比較大,這可能涉及到遍歷文檔的過程。
疑問?為什么?create_weight?過程耗時(shí)會這么長
從我的查詢條件來看,請求是很簡單的,沒有復(fù)雜的條件,為什么?create_weight?過程耗時(shí)會這么長?
create_weight階段的耗時(shí)主要取決于查詢中使用的權(quán)重計(jì)算方式以及索引的結(jié)構(gòu)和數(shù)據(jù)量。在你提供的Profile?API結(jié)果中,create_weight的耗時(shí)非常大,說明這個(gè)步驟在整個(gè)查詢過程中占用了大量的時(shí)間。
有幾個(gè)潛在的原因可能導(dǎo)致create_weight步驟的性能下降:
- 腳本復(fù)雜性:?如果你在Function?Score?Query中使用了復(fù)雜的腳本,腳本的執(zhí)行可能會消耗大量的時(shí)間。檢查你的腳本邏輯,盡量確保它是高效的。
- 文檔數(shù)和大?。?/span>?如果你的索引中包含大量的文檔或者文檔非常大,創(chuàng)建權(quán)重可能會變得更加耗時(shí)。在這種情況下,考慮優(yōu)化索引結(jié)構(gòu)、調(diào)整分片數(shù)量等。
- Function?Score?Query的配置:?如果在Function?Score?Query中使用了復(fù)雜的函數(shù)或者過多的函數(shù),計(jì)算每個(gè)文檔的權(quán)重可能會更加耗時(shí)。檢查你的Function?Score?Query配置,確保它符合你的需求。
create_weight?究竟在干什么,源碼?
這里主要是lucene去IO底層文件。這里比較明顯的是性能問題。
第一個(gè)嘗試,去掉腳本排序
腳本排序的時(shí)間會算在create_weight過程中(猜想,待驗(yàn)證)
測試把我的搜索條件,去掉腳本排序。原來是15s,現(xiàn)在是10s,腳本排序的耗時(shí)在我請求中,占據(jù)了30%多。
繼續(xù)分析慢查詢的分片
其中,耗時(shí)最長的分片還是,create_weight?過程耗時(shí)最嚴(yán)重。
耗時(shí)發(fā)生在我的title字段上的這個(gè)子查詢上。
調(diào)整terminate_after??從200->10
檢索耗時(shí)進(jìn)一步降低。
其中還是有耗時(shí)長的個(gè)別分片
整個(gè)請求6.2s,在這個(gè)分片上的請求就花了6s,并且時(shí)間還是花在了create_weight上。
如何才能降低create_weight的耗時(shí)?
降低terminate_after的值可以降低,代價(jià)是影響整體的排序效果。
減少段的個(gè)數(shù),可以減少耗時(shí)。通過段合并。因?yàn)榭梢詼p少段的遍歷。
疑問?是不是在查詢的時(shí)候負(fù)載高?
GET?_cat/nodes?v
問題解決方案
動態(tài)調(diào)整terminate_after
??并非所有的請求,都需要每個(gè)分片都200條數(shù)據(jù)。特別在大的時(shí)間跨度下,分片可能會非常多,動輒幾千個(gè),以2000個(gè)分片算,最多會匹配2000*200=400000數(shù)據(jù)。加上腳本排序,這40W數(shù)據(jù),都需要參與分?jǐn)?shù)的計(jì)算,最終才能角逐出top20的數(shù)據(jù)。最終的結(jié)果是請求耗時(shí)長。
??實(shí)際上,terminate_after的取值,是可以動態(tài)調(diào)整的。檢索分為樂觀和悲觀情況,樂觀情況下,數(shù)據(jù)分布是均勻的,在分片上分配是均勻的,且檢索條件命中的數(shù)據(jù)較多。在悲觀情況下,檢索的數(shù)據(jù)分布不均勻,且搜索的條件比較特殊,命中的數(shù)據(jù)很少,或者命中的數(shù)據(jù)在分片上分布不均勻。
??大多數(shù)情況下,數(shù)據(jù)分布是均勻的,檢索的數(shù)據(jù)量越大,分布可能越均勻。例如檢索3個(gè)月,總數(shù)據(jù)大約450億數(shù)據(jù),隨便一個(gè)搜索條件,搜索的數(shù)據(jù)大概率是大于10000條的。所以可以設(shè)計(jì)一個(gè)動態(tài)調(diào)整方案,來調(diào)整terminate_after的取值,能夠獲取更好的性能,提升200%-300%。另外需要一個(gè)悲觀情況下的擔(dān)保機(jī)制,避免在悲觀情況下檢索丟失數(shù)據(jù)。
??terminate_after的值是限定在分片上的,假如一個(gè)索引有10個(gè)分片,如果設(shè)置terminate_after為200,則最后返回的數(shù)據(jù)總量為?10*200=2000條??紤]到分頁為500頁,每頁20條數(shù)據(jù),共計(jì)可以翻頁10000條數(shù)據(jù)。如何設(shè)置terminate_after的值呢?要考慮到翻頁的情況。
??請求的入?yún)?,一般包含了翻頁和每頁的條數(shù)。?期望數(shù)據(jù)總量=?頁碼*?每頁的數(shù)量。??es的召回總量為=?分片數(shù)*terminate_after數(shù)量*偏差。偏差可以算0.1,預(yù)期10倍可以彌補(bǔ)數(shù)據(jù)分布不均勻帶來的影響。分片數(shù)暫時(shí)可以按每天15個(gè)來算。?頁碼*?每頁的數(shù)量?=?分片數(shù)*terminate_after數(shù)量*偏差?。可以得出??terminate_after數(shù)量?=?頁碼*?每頁的數(shù)量?/?(分片數(shù)*偏差)。terminate_after數(shù)量不足10則向上取正為10。?當(dāng)查詢的天數(shù)小于7天,則可以直接取值為200。
??擔(dān)保機(jī)制,需要解決悲觀情況下的問題。根據(jù)es返回的數(shù)據(jù)總量。?如果返回的數(shù)據(jù)總量小于期望的數(shù)據(jù)總量,則觸發(fā)擔(dān)保機(jī)制。需要調(diào)大terminate_after的值(暫定為500),再去搜索一次。
索引段合并
??段合并可以提升減速效果。
調(diào)大在請求在單個(gè)節(jié)點(diǎn)上的最大并發(fā)度
默認(rèn)情況下,一個(gè)請求在單個(gè)節(jié)點(diǎn)上最大并發(fā)度為5,超過5以后則需要排隊(duì),串行執(zhí)行。這里先避免排隊(duì)的時(shí)間。我這里給了30。 注意此參數(shù),在負(fù)載不高,且線程池充足和堆空間充足的情況下可以這樣用。其它情況不適合,在聚合請求中不建議使用!
最終的檢索效果
檢索條件
檢索耗時(shí)情況
最后?
搜索優(yōu)化不是一朝一夕的事情。需要長時(shí)間的知識儲備。我已經(jīng)做了四年優(yōu)化es搜索優(yōu)化。我把一些高質(zhì)量的優(yōu)化提升的案例放在了我的專欄里。(目前還是免費(fèi)的,未來可能會收費(fèi)把...)想要做更多的搜索提升,可以看看這些文章,或許會能起到拋磚引玉的作用。文章來源:http://www.zghlxwxcb.cn/news/detail-762014.html
https://blog.csdn.net/star1210644725/category_12341074.html文章來源地址http://www.zghlxwxcb.cn/news/detail-762014.html
到了這里,關(guān)于ES慢查詢分析——性能提升6 倍的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!