您好,登錄后才能下訂單哦!
這篇“Es因scroll查詢引起的gc問題怎么解決”文章的知識點大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Es因scroll查詢引起的gc問題怎么解決”文章吧。
問題:
某日下午正開心的逛著超市,突然收到線上es機器的fgc電話告警,隨之而來的是一波es reject execution,該es機器所處集群出現(xiàn)流量抖動。
排查:
回到家打開監(jiān)控頁面,內(nèi)存占用率有明顯的上升,通常服務(wù)端不會無端的爆內(nèi)存,首先排查可能性最高的:讀寫流量變化。
通過監(jiān)控頁發(fā)現(xiàn)入口流量并沒有明顯抖動,考慮到集群中的不同索引以及不同查詢類型,總的入口流量可能會掩蓋一些問題,所以繼續(xù)查看各索引的分操作流量監(jiān)控,發(fā)現(xiàn)索引 A 的scroll流量在故障發(fā)生時存在明顯的波動,從正常的 10qps 以內(nèi)漲到最高 100qps 左右,這對于普通查詢來說并不高,看來是 scroll 查詢有些異樣。
起因1:
先說結(jié)論:scroll 查詢相對普通查詢占用的內(nèi)存開銷大很多,考慮到遍歷數(shù)據(jù)的場景,安全的量是控制在 10qps 左右。
相比于普通query,scroll 查詢需要后端保留遍歷請求的上下文,具體的就是當有init scroll請求到達時,當時的 index searcher 會持有全部索引段的句柄直至scroll請求結(jié)束,如果處理不當,比如段緩存等,容易在server端占用大量內(nèi)存;另外, scroll 查詢還需要在server端保存請求上下文,比如翻頁深度、scroll context等,也會占用內(nèi)存資源。
在后續(xù)的測試中,客戶端單線程使用scroll查詢遍歷百萬級別的索引數(shù)據(jù),server端的CPU占用率高達70%左右,觀察進程的CPU占用,發(fā)現(xiàn)大部分的CPU時間都耗在gc上,這使得server沒有足夠的CPU時間調(diào)度其他任務(wù),導(dǎo)致正常的讀寫請求不能被及時響應(yīng)。
# 壓測機器配置:1c2g x 10# 索引配置:5 number_of_shards x 1 number_of_replica,共計約180萬數(shù)據(jù)
起因2:
繼續(xù)排查scroll執(zhí)行的查詢內(nèi)容,發(fā)現(xiàn)的主要有兩種類型。
其一:
{ "query": {"bool":{"must":[{"terms":[11,22,…2003]}]}}, "size":200}# terms子句中包含200個id
上面的示例query省略了其他一些過濾條件,白話一下這個查詢的含義:
從索引中查詢id字段值為數(shù)組所包含的200條記錄
可以看到的幾個特征是:
沒有filter子句,terms條件在must子句
這個查詢最多返回200條記錄,一次查詢就可以得到全部數(shù)據(jù)
其二:
{ "query": {"bool":{"must":[ {"range":{"create_time":{"gt":0, "lte":604800}}}, {"term":{"shop_id":1}} ]}}, "size":200}# range條件包含的數(shù)據(jù)大約為1000條# 全索引包含的數(shù)據(jù)大約1000萬條# create_time不固定,但是區(qū)間固定在1周
這里也省略了一些其他干擾條件,只保留最重要的,白話過來的含義:
從1000萬全量索引中查詢shop_id=1并且create_time在符合條件區(qū)間內(nèi)的數(shù)據(jù),條件區(qū)間每10秒變更一次,也就是每10秒查詢一次當前時刻之前1周的新數(shù)據(jù).
可以得出的幾個結(jié)論:
size為200,要訪問全部數(shù)據(jù)至少需要5次查詢
create_time的變更很小,類似于 (0, 603800] => (5, 604805],所以每次查詢該子條件命中的記錄數(shù)變化也都不大,都有幾百萬條
沒有filter子句
并沒有發(fā)現(xiàn)filter或者must_not這樣在官方文檔中明確標明的filter context條件,但是實際上的filter cache在scroll發(fā)生期間單機從 500 MB 左右逐漸升高到 6 GB(配置的filter cache最大空間),理論上說不通,直接從代碼里找答案。
跟蹤query流程,發(fā)現(xiàn)bool子句中不論是must還是filter,最終被rewrite之后沒有本質(zhì)上的區(qū)別,判斷是否可以進入filter cache的條件是:
段內(nèi)最大文檔數(shù)是否在閾值范圍內(nèi)(Es的filter緩存以段為單位)
查詢出現(xiàn)頻次是否超過閾值
而在出現(xiàn)頻次這個部分,Lucene緩存策略還會有isCostly這樣的判斷,目的是盡量將高消耗的查詢盡可能早的緩存起來,提高查詢性能,符合isCostly判斷的查詢包括 terms 和 range 等查詢,只要重復(fù)出現(xiàn)2次即會被緩存起來,結(jié)合起來分析:
terms查詢并不需要scroll查詢,使用普通查詢就能解決需求,使用scroll查詢增加了server負載
range查詢重復(fù)次數(shù)達到了isCostly閾值,也就是說每次遍歷數(shù)據(jù)都會往filter cache中丟入幾百萬的緩存value,而且命中率極低(下次scroll查詢的range起止條件有細微的變化),加大了server的gc負擔
解決:
通過上面的分析,我們可以看到有兩個因素的影響導(dǎo)致了server的拒絕響應(yīng):
大量的scroll并發(fā)
不當?shù)膔ange請求,具體又可以拆分為:
高頻次,每10秒一次
變化快,每次查詢的起止范圍都有10秒的后延
命中數(shù)大,百萬級別的命中數(shù)
針對上面的幾點各個擊破就是我們的解決方案:
scroll請求:
糾正不當使用的terms+scroll查詢,使用普通查詢;
推薦使用search_after替換scroll請求,雖然在效率上有所降低,但是有兩個優(yōu)勢:
可以重試,scroll如果重試可能會丟失部分數(shù)據(jù)
資源占用低,在相同的測試環(huán)境下,CPU占用率只有10%左右
不當?shù)膔ange請求:
高頻次:降低請求頻率,限制到至少1分鐘一次,當然不是根本解決方案,推薦將類似的遍歷數(shù)據(jù)請求改到db或者hbase等介質(zhì)
變化快:粗暴點的解決方案是限制時間單位到小時級別,優(yōu)雅點的話:
將時間條件拆分為粗粒度和細粒度的組合,粗粒度以若干小時為單位,細粒度支撐到分鐘或者秒級
細粒度條件使用script方式執(zhí)行,原理是filter cache的frequency是用LinkedHashMap作為key容器的,用來累積查詢次數(shù),而key的hash計算,普通query是根據(jù)查詢的條件和值來作為hash輸入的,而script查詢是使用當前實例的引用,這樣就能避免查詢被累積(因為每次的hashcode都不一樣)
命中數(shù)大:通過粗細粒度劃分可以降低成本
以上就是關(guān)于“Es因scroll查詢引起的gc問題怎么解決”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。