我負責公司的檢索平台的開發兼運維工作。
我們的場景是對網際網路上的裝置資料進行檢索。資料量大概有20億,對應的存儲量大概有6T(不帶副本的情況下)。單條資料會有上百個字段,用來刻畫網絡裝置畫像。
我們有比較特殊的需求:
- 我們有頻繁更新的需求,每天幾千萬,甚至上億。
- 我們并不能做根據時間的滾動索引。因為後進的資料需要把前邊的資料做覆寫。是以就沒有辦法做索引的生命周期管理。
- 我們有頻繁的聚類搜尋的需求。
- 我們想要基于這些資料,做到普通檢索1秒以内,聚類檢索3秒以内。
寫這篇文章的目的并不是給出解決方案,而是我希望通過提出問題,并提供思路的方式,來摸索問題的答案。當然有能力的人看到這篇文章,歡迎在評論區給出好的意見。
我的疑惑和問題
第一個問題是關于叢集規劃的問題
對于上述的需求,6T資料,20億條,來檢索。
- 到底多大的叢集規模能夠承載這些資料?
- 我們需要多少個節點?
- 我們需要什麼樣的硬體資源,多大的記憶體空間?
- 每個節點我們配置設定多少個CPU核心更好?
- 如果将這些資料都放在同一個索引,如果做才能達到普通檢索1秒以内,聚類檢索3秒以内的要求?
分享一個真實的案例:
我先說一下我對叢集規劃的了解(檢視的官方文檔,以及網上多數的叢集規劃相關的文章)。關于節點數,一個節點能夠承載的分片數,取決于堆記憶體的大小,公式計算是,1G堆記憶體不超過20個分片。而單個分片大小,理論值是 20G - 40G性能最好。按理說,我6T的資料 = 6144G ,如果每個分片30G,那麼6144/30 = 205個分片。 我的一個節點是64G記憶體,我分31G給堆記憶體。理論上來說,31能夠支撐的分片數是 600個。按照這麼算的話,我這麼多資料一個節點就夠了。
然而實際上并不是,我并沒有把這些資料隻用一個叢集來配置設定。我的測試叢集是 512G記憶體,48核心10T固态硬碟的配置(阿裡雲資料中心的人說,es的單個節點最佳配置是 16個核心,64G記憶體,2T固态硬碟。這被多數人認可,但是我現在認為es的叢集總的核心數,應該和單個索引的最大分片數對齊,甚至能多則再多一點,因為還要有一些線程去做段合并之類的。這個是根據es的檢索原理來的,實際上單個分片對es來說是最小的資源分片單元,一個分片實際上就是一個lucene執行個體。等我再研究清楚lucene再補一篇文章來證明)。現在想想es的工作原理,我們知道這個機器配置非常的不均衡,問題在CPU核心上。我是每台機器四個資料節點,一個master節點。我有測過我單台機器的IO在每秒2G以上。我用這三台機器組成了叢集,來側這6T的資料,得到的效果挺一般的。
我發現檢索速度和我的條件有很大的關系,當我的檢索結果集超過兩億以後,叢集的檢索時間在5秒左右。如果檢索的結果集有一兩千萬,那麼檢索時間通常在一秒左右,不超過兩秒。但是聚類分析通常要在 5秒左右。
另外關于這個問題,一個es社群的朋友在看了這篇文章以後給了我答案,我也放在這裡,供大家參考:
問題1. 關于叢集規劃的問題,你提到一個案例,依據是堆記憶體與分片數的經驗公式, 1G堆記憶體換20個分片,我認為官方這個換算公式的作用是引導使用者正确評估特定實體裝置上可最大承載的分片數,而不是用于反向的裝置需求評估。即可以用這個公式來限制自己在一個确定的實體裝置上最多搞幾個分片,但是不能用這個公式來反推需要多少個實體裝置。1G的堆可以放20個分片(這個更多的是基于叢集管理的角度考慮,而不是性能的考慮),但是我們不會真的放那麼多,對吧,這是一;分片也是有大小區分的,放20個小尺寸分片與20個尺寸大分片,很顯然,效果也是不一樣的,這是二。
評估到底要搞多少實體裝置,相信我們不僅僅是滿足能不能裝得下的程度,更加注重的是性能問題。
對于裝置的選用上,我不是很熟悉,你提到具體型号的伺服器,我不敢瞎說。但是我個人的了解是ES更是一個記憶體消耗型服務,它對記憶體的需求遠大于CPU,往往它的短闆最先表現在記憶體上而不是負載上,是以我跟傾向于記憶體見長型裝置。
我們團隊之前做過一個很有意思的事情(你不要在意它的經濟性),把redis植入到lucene裡面,把整個es改造成一個純記憶體的服務,跑在很廉價的機器上,經過一系列測試,我們評估這不是一個虧本兒買賣,至少虧得不明顯。
不知道你有沒有注意到官方還有另外一個經驗公式,它描述的是堆外記憶體和資料量的關系,這個和我們關注的性能問題聯系更加直接:對于日志型資料,要保證堆外記憶體(也就是給lucene的記憶體)能容納總資料的10%; 對于業務資料,要保證堆外記憶體可以容納資料總量的50%(這個具體數值我已經記不清楚了,在elastic官方部落格裡面有提到)。它這個公式不是強制的,隻是推薦值,鑒于對性能追求的推薦,多多益善,如果我們希望擁有更好的性能,那就給它更多的記憶體吧
針對這個回答,我的問題:
關于叢集規劃的問題,我隻是提一個疑問。從叢集的效果上來看,他們給的值,應該也是上限,而不是最佳性能,畢竟性能問題和業務有很大的關系。不過我覺得如果我們能夠摸索出來一個實踐公式,來推算es最佳性能,這可能是一件很有意思的事。我也在不停的去學習摸索es的檢索原理,光看書,和官網,已經無法得到我問題的解。這裡我想再提一個問題,就是你了解分片的工作原理嗎,以及一次檢索,對于es叢集來說,分片是如何工作的。 我了解可能有一點偏差哈,我了解到的分片是一個lucene執行個體。對于一次不确定性檢索來說,es無法固定到一個或者幾個分片上去。是以基本上就是所有分片同時去工作的。如果是這樣的話,那麼cpu就非常有用了,我不太确定檢索過程中,是不是一個分片去占用一個線程去執行操作。如果cpu不夠的話,是不是就要有一個分片需要等待,也就是說分片執行檢索變成了串行的,并不是并行的。是以我提出一個cpu對es的舉足輕重的看法。有機會我會去做一個實驗。如果你了解一次檢索,分片工作原理的話,可以讓我學習一下。
第二個問題是關于索引應該如何劃分的問題
像這樣的大索引,我們是否有必要将資料做拆分?我們的叢集是否能負載這麼大的索引。
第三個問題是關于索引拆分能夠提升的問題
将索引拆分成小索引,然後拆分後的小索引設定相同的别名。這樣是否能提升性能?拆分成多少個索引性能最好?
我把特定條件的資料拆分了出來,然後做了檢索的對比,比方說我一共有20億資料,其中中國的的3億。我把這3億單獨拆出來放一個索引。我發現去相同的搜尋條件,去兩個索引裡邊搜尋,明顯隻有中國3億資料的索引,搜尋時間更短。這是我測過的得出的結論:大的目标搜尋集,和小的目标搜尋集,即使要搜的内容兩個搜尋集的交集是相同的,也是在小的搜尋集搜尋的時間更短!請記住這個結論,你可以去測試一下看看!
關于問題二和三 ,這個es社群的朋友在看了這篇文章以後也給了答案,我也放在這裡,供大家參考
問題2+3. 關于索引劃分的問題,我非常贊成将大索引拆分成多個小索引。
毋庸置疑,在一個小的資料集上操作會用于更大的性能優勢。但是我的出發點不是性能上的考慮,而是叢集的穩定性上的。
先說穩定性,對于一個包含龐大索引的叢集,很難想象在弱網絡環境下出現哪些匪夷所思的問題,甚至一個節點的意外退出或者加入,對整個叢集都可能是一次災難,如果分片感覺政策、節點間資料同步配置不合适(translog+seqNo預設有效期似乎是12小時,另外還有一個size,具體多少G忘記了,誰先達到臨界值誰先生效,一旦超過這個界限,就是實體檔案segment的拷貝了),我們難以預料一次同步花費多長時間。而這些同步雖然在分片這個實體次元發生,卻是在索引這個邏輯次元去排程的,索引的大小決定這個同步工作的量級。
再說性能,因為核心資源排程都是在分片次元的,是以真正影響性能的分片狀态而不是索引狀态(準确的說應該是分片狀态對性能的影響是決定性的,但不是100%)。一個大索引擁有2個分片,和兩個小索引但各自隻有1個分片,在其他條件(硬體、資料量)相同的情況下他們的綜合性能是相當的。
注意到你的那個測試,将3億中國的資料摘出來單獨索引,測試結果發現小的索引比之前更快,也非常具有說服力。對于這個實驗我很感興趣,不過我還有一點點建議,希望把那些因素剔除後,再精确地對比一下,結果會更有說服力:
(a) 在原來20億的索引中,這3億中國的資料分散在了多少個分片當中了,分片副本數是多少,這些分片又分布到哪些節點上了; 在新的3億資料的索引中,有多少個分片,副本數設定了多少,使用可多少節點;
(b) 檢查一下緩存的使用情況,建議對比測試的時候注意不要觸發到了緩存機制
如果你對這個案例有更進一步的測試,也非常希望你能同我分享一下新的測試結果
我的追問:
第四個問題:es對頻繁修改并不友好,我們該如何應對頻繁的更新的問題
- 叢集規模無法承載更多的資料量,到達極限以後,走下坡路了。這個時候我們應該去擴充叢集。那麼問題來了,什麼時候是叢集的極限承載能力,什麼時候叢集就開始走下坡路了?
- 如果索引中的資料沒有再增加,理論上不會說越來越慢。我們還可以對索引進行強行的段合并。
- 如果es中資料在不斷的增長,有兩種情況,一種是資料不會發生修改,隻是累積增長。第二種情況是我們的資料有唯一key,新增的資料會覆寫原來的資料,也就是發生了修改操作。第二種操作是非常緻命的。特别是頻繁的更新操作,會給叢集帶來非常大的負擔。這和es的更新機制有關系,es在維護資料的時候有段的概念。對于已經放進去資料,不再修改,如果要有修改操作,也是把原來的資料标記删除,然後再添加一條新的資料。并沒有辦法把原來的資料修改了。在極差的情況下,一個段中有一半以上的資料都是标記删除的資料。并且段合并到一定程度,就永遠都無法再進行段合并了(這個可以去了解lucene底層的段合并原理)。