歡迎關(guān)注公眾號“Tim在路上”
在并發(fā)情況下,Elasticsearch如果保證讀寫一致舌涨?
可以通過版本號使用樂觀并發(fā)控制糯耍,以確保新版本不會被舊版本覆蓋,由應(yīng)用層來處理具體的沖突囊嘉;
另外對于寫操作温技,一致性級別支持quorum/one/all,默認(rèn)為quorum扭粱,即只有當(dāng)大多數(shù)分片可用時才允許寫操作舵鳞。但即使大多數(shù)可用,也可能存在因為網(wǎng)絡(luò)等原因?qū)е聦懭敫北臼∽粮颍@樣該副本被認(rèn)為故障蜓堕,分片將會在一個不同的節(jié)點上重建抛虏。
對于讀操作,可以設(shè)置replication為sync(默認(rèn))俩滥,這使得操作在主分片和副本分片都完成后才會返回嘉蕾;如果設(shè)置replication為async時贺奠,也可以通過設(shè)置搜索請求參數(shù)_preference為primary來查詢主分片霜旧,確保文檔是最新版本。
Elasticsearch對于大數(shù)據(jù)量(上億量級)的聚合如何實現(xiàn)儡率?
Elasticsearch 提供的首個近似聚合是cardinality 度量挂据。它提供一個字段的基數(shù),即該字段的distinct或者unique值的數(shù)目儿普。它是基于HLL算法的崎逃。HLL 會先對我們的輸入作哈希運算,然后根據(jù)哈希運算的結(jié)果中的 bits 做概率估算從而得到基數(shù)眉孩。其特點是:可配置的精度个绍,用來控制內(nèi)存的使用(更精確 = 更多內(nèi)存);小的數(shù)據(jù)集精度是非常高的浪汪;我們可以通過配置參數(shù)巴柿,來設(shè)置去重需要的固定內(nèi)存使用量。無論數(shù)千還是數(shù)十億的唯一值死遭,內(nèi)存使用量只與你配置的精確度相關(guān)广恢。
對于GC方面,在使用Elasticsearch時要注意什么呀潭?
SEE:https://elasticsearch.cn/article/32
倒排詞典的索引需要常駐內(nèi)存钉迷,無法GC,需要監(jiān)控data node上segment memory增長趨勢钠署。
各類緩存糠聪,field cache, filter cache, indexing cache, bulk queue等等,要設(shè)置合理的大小谐鼎,并且要應(yīng)該根據(jù)最壞的情況來看heap是否夠用舰蟆,也就是各類緩存全部占滿的時候,還有heap空間可以分配給其他任務(wù)嗎该面?避免采用clear cache等“自欺欺人”的方式來釋放內(nèi)存夭苗。
避免返回大量結(jié)果集的搜索與聚合。確實需要大量拉取數(shù)據(jù)的場景隔缀,可以采用scan & scroll api來實現(xiàn)题造。
cluster stats駐留內(nèi)存并無法水平擴(kuò)展,超大規(guī)模集群可以考慮分拆成多個集群通過tribe node連接猾瘸。
想知道heap夠不夠界赔,必須結(jié)合實際應(yīng)用場景丢习,并對集群的heap使用情況做持續(xù)的監(jiān)控。
Elasticsearch在部署時淮悼,對Linux的設(shè)置有哪些優(yōu)化方法咐低?
64 GB 內(nèi)存的機(jī)器是非常理想的, 但是32 GB 和16 GB 機(jī)器也是很常見的袜腥。少于8 GB 會適得其反见擦。
如果你要在更快的 CPUs 和更多的核心之間選擇,選擇更多的核心更好羹令。多個內(nèi)核提供的額外并發(fā)遠(yuǎn)勝過稍微快一點點的時鐘頻率鲤屡。
如果你負(fù)擔(dān)得起 SSD,它將遠(yuǎn)遠(yuǎn)超出任何旋轉(zhuǎn)介質(zhì)福侈。 基于 SSD 的節(jié)點酒来,查詢和索引性能都有提升。如果你負(fù)擔(dān)得起肪凛,SSD 是一個好的選擇堰汉。
即使數(shù)據(jù)中心們近在咫尺,也要避免集群跨越多個數(shù)據(jù)中心伟墙。絕對要避免集群跨越大的地理距離翘鸭。
請確保運行你應(yīng)用程序的 JVM 和服務(wù)器的 JVM 是完全一樣的。 在 Elasticsearch 的幾個地方远荠,使用 Java 的本地序列化矮固。
通過設(shè)置gateway.recover_after_nodes、gateway.expected_nodes譬淳、gateway.recover_after_time可以在集群重啟的時候避免過多的分片交換档址,這可能會讓數(shù)據(jù)恢復(fù)從數(shù)個小時縮短為幾秒鐘。
Elasticsearch 默認(rèn)被配置為使用單播發(fā)現(xiàn)邻梆,以防止節(jié)點無意中加入集群守伸。只有在同一臺機(jī)器上運行的節(jié)點才會自動組成集群。最好使用單播代替組播浦妄。
不要隨意修改垃圾回收器(CMS)和各個線程池的大小尼摹。
把你的內(nèi)存的(少于)一半給 Lucene(但不要超過 32 GB!)剂娄,通過ES_HEAP_SIZE 環(huán)境變量設(shè)置蠢涝。
內(nèi)存交換到磁盤對服務(wù)器性能來說是致命的。如果內(nèi)存交換到磁盤上阅懦,一個 100 微秒的操作可能變成 10 毫秒和二。 再想想那么多 10 微秒的操作時延累加起來。 不難看出 swapping 對于性能是多么可怕耳胎。
Lucene 使用了大量的文件惯吕。同時惕它,Elasticsearch 在節(jié)點和 HTTP 客戶端之間進(jìn)行通信也使用了大量的套接字。 所有這一切都需要足夠的文件描述符废登。你應(yīng)該增加你的文件描述符淹魄,設(shè)置一個很大的值,如 64,000堡距。
補(bǔ)充:索引階段性能提升方法
使用批量請求并調(diào)整其大屑孜:每次批量數(shù)據(jù) 5–15 MB 大是個不錯的起始點。
存儲:使用 SSD
段和合并:Elasticsearch 默認(rèn)值是 20 MB/s吏颖,對機(jī)械磁盤應(yīng)該是個不錯的設(shè)置搔体。如果你用的是 SSD,可以考慮提高到 100–200 MB/s半醉。如果你在做批量導(dǎo)入,完全不在意搜索劝术,你可以徹底關(guān)掉合并限流缩多。另外還可以增加 index.translog.flush_threshold_size 設(shè)置,從默認(rèn)的 512 MB 到更大一些的值养晋,比如 1 GB衬吆,這可以在一次清空觸發(fā)的時候在事務(wù)日志里積累出更大的段。
如果你的搜索結(jié)果不需要近實時的準(zhǔn)確度绳泉,考慮把每個索引的index.refresh_interval 改到30s逊抡。
如果你在做大批量導(dǎo)入,考慮通過設(shè)置index.number_of_replicas: 0 關(guān)閉副本零酪。
詳細(xì)描述一下Elasticsearch搜索的過程冒嫡。
搜索被執(zhí)行成一個兩階段過程,我們稱之為 Query Then Fetch四苇;
在初始查詢階段時孝凌,查詢會廣播到索引中每一個分片拷貝(主分片或者副本分片)。 每個分片在本地執(zhí)行搜索并構(gòu)建一個匹配文檔的大小為 from + size 的優(yōu)先隊列月腋。PS:在搜索的時候是會查詢Filesystem Cache的蟀架,但是有部分?jǐn)?shù)據(jù)還在Memory Buffer,所以搜索是近實時的榆骚。
每個分片返回各自優(yōu)先隊列中 所有文檔的 ID 和排序值 給協(xié)調(diào)節(jié)點片拍,它合并這些值到自己的優(yōu)先隊列中來產(chǎn)生一個全局排序后的結(jié)果列表。
接下來就是 取回階段妓肢,協(xié)調(diào)節(jié)點辨別出哪些文檔需要被取回并向相關(guān)的分片提交多個 GET 請求捌省。每個分片加載并 豐富 文檔,如果有需要的話职恳,接著返回文檔給協(xié)調(diào)節(jié)點所禀。一旦所有的文檔都被取回了方面,協(xié)調(diào)節(jié)點返回結(jié)果給客戶端。
補(bǔ)充:Query Then Fetch的搜索類型在文檔相關(guān)性打分的時候參考的是本分片的數(shù)據(jù)色徘,這樣在文檔數(shù)量較少的時候可能不夠準(zhǔn)確恭金,DFS Query Then Fetch增加了一個預(yù)查詢的處理,詢問Term和Document frequency褂策,這個評分更準(zhǔn)確横腿,但是性能會變差。
詳細(xì)描述一下Elasticsearch更新和刪除文檔的過程斤寂。
刪除和更新也都是寫操作耿焊,但是Elasticsearch中的文檔是不可變的,因此不能被刪除或者改動以展示其變更遍搞;
磁盤上的每個段都有一個相應(yīng)的.del文件罗侯。當(dāng)刪除請求發(fā)送后,文檔并沒有真的被刪除溪猿,而是在.del文件中被標(biāo)記為刪除钩杰。該文檔依然能匹配查詢,但是會在結(jié)果中被過濾掉诊县。當(dāng)段合并時讲弄,在.del文件中被標(biāo)記為刪除的文檔將不會被寫入新段。
在新的文檔被創(chuàng)建時依痊,Elasticsearch會為該文檔指定一個版本號避除,當(dāng)執(zhí)行更新時,舊版本的文檔在.del文件中被標(biāo)記為刪除胸嘁,新版本的文檔被索引到一個新段瓶摆。舊版本的文檔依然能匹配查詢,但是會在結(jié)果中被過濾掉缴渊。
詳細(xì)描述一下Elasticsearch索引文檔的過程赏壹。
協(xié)調(diào)節(jié)點默認(rèn)使用文檔ID參與計算(也支持通過routing),以便為路由提供合適的分片衔沼。
shard = hash(document_id) % (num_of_primary_shards)
當(dāng)分片所在的節(jié)點接收到來自協(xié)調(diào)節(jié)點的請求后蝌借,會將請求寫入到Memory Buffer,然后定時(默認(rèn)是每隔1秒)寫入到Filesystem Cache指蚁,這個從Momery Buffer到Filesystem Cache的過程就叫做refresh菩佑;
當(dāng)然在某些情況下,存在Momery Buffer和Filesystem Cache的數(shù)據(jù)可能會丟失凝化,ES是通過translog的機(jī)制來保證數(shù)據(jù)的可靠性的稍坯。其實現(xiàn)機(jī)制是接收到請求后,同時也會寫入到translog中,當(dāng)Filesystem cache中的數(shù)據(jù)寫入到磁盤中時瞧哟,才會清除掉混巧,這個過程叫做flush;
在flush過程中勤揩,內(nèi)存中的緩沖將被清除咧党,內(nèi)容被寫入一個新段,段的fsync將創(chuàng)建一個新的提交點陨亡,并將內(nèi)容刷新到磁盤傍衡,舊的translog將被刪除并開始一個新的translog。
flush觸發(fā)的時機(jī)是定時觸發(fā)(默認(rèn)30分鐘)或者translog變得太大(默認(rèn)為512M)時负蠕;
補(bǔ)充:關(guān)于Lucene的Segement:
Lucene索引是由多個段組成蛙埂,段本身是一個功能齊全的倒排索引。
段是不可變的遮糖,允許Lucene將新的文檔增量地添加到索引中绣的,而不用從頭重建索引。
對于每一個搜索請求而言止吁,索引中的所有段都會被搜索被辑,并且每個段會消耗CPU的時鐘周、文件句柄和內(nèi)存敬惦。這意味著段的數(shù)量越多,搜索性能會越低谈山。
為了解決這個問題俄删,Elasticsearch會合并小段到一個較大的段,提交新的合并段到磁盤奏路,并刪除那些舊的小段畴椰。
Elasticsearch是如何實現(xiàn)Master選舉的?
Elasticsearch的選主是ZenDiscovery模塊負(fù)責(zé)的鸽粉,主要包含Ping(節(jié)點之間通過這個RPC來發(fā)現(xiàn)彼此)和Unicast(單播模塊包含一個主機(jī)列表以控制哪些節(jié)點需要ping通)這兩部分斜脂;
對所有可以成為master的節(jié)點(node.master: true)根據(jù)nodeId字典排序,每次選舉每個節(jié)點都把自己所知道節(jié)點排一次序触机,然后選出第一個(第0位)節(jié)點帚戳,暫且認(rèn)為它是master節(jié)點。
如果對某個節(jié)點的投票數(shù)達(dá)到一定的值(可以成為master節(jié)點數(shù)n/2+1)并且該節(jié)點自己也選舉自己儡首,那這個節(jié)點就是master片任。否則重新選舉一直到滿足上述條件。
補(bǔ)充:master節(jié)點的職責(zé)主要包括集群蔬胯、節(jié)點和索引的管理对供,不負(fù)責(zé)文檔級別的管理;data節(jié)點可以關(guān)閉http功能氛濒。
ES分配多少內(nèi)存?VM參數(shù)如何優(yōu)化?
生成大量長生命周期的對象产场,是給heap造成壓力的主要原因鹅髓,例如讀取一大片數(shù)據(jù)在內(nèi)存中進(jìn)行排序,或者在heap內(nèi)部建cache緩存大量數(shù)據(jù)京景。如果GC釋放的空間有限窿冯,而應(yīng)用層面持續(xù)大量申請新對象,GC頻度就開始上升醋粟,同時會消耗掉很多CPU時間靡菇。
Lucene的倒排索引(Inverted Index)是先在內(nèi)存里生成,然后定期以段文件(segment file)的形式刷到磁盤的米愿。每個段實際就是一個完整的倒排索引厦凤,并且一旦寫到磁盤上就不會做修改。 API層面的文檔更新和刪除實際上是增量寫入的一種特殊文檔育苟,會保存在新的段里较鼓。不變的段文件易于被操作系統(tǒng)cache,熱數(shù)據(jù)幾乎等效于內(nèi)存訪問违柏。
基于以上2個基本事實博烂,我們不難理解,為何官方建議的heap size不要超過系統(tǒng)可用內(nèi)存的一半漱竖。heap以外的內(nèi)存并不會被浪費禽篱,操作系統(tǒng)會很開心的利用他們來cache被用讀取過的段文件。
Heap分配多少合適馍惹?遵從官方建議就沒錯躺率。 不要超過系統(tǒng)可用內(nèi)存的一半,并且不要超過32GB万矾。JVM參數(shù)呢悼吱?對于初級用戶來說,并不需要做特別調(diào)整良狈,仍然遵從官方的建議后添,將xms和xmx設(shè)置成和heap一樣大小,避免動態(tài)分配heap size就好了薪丁。雖然有針對性的調(diào)整JVM參數(shù)可以帶來些許GC效率的提升遇西,當(dāng)有一些“壞”用例的時候,這些調(diào)整并不會有什么魔法效果幫你減輕heap壓力窥突,甚至可能讓問題更糟糕努溃。
ES的heap被什么占用?
Segment不是file嗎?segment memory又是什么阻问?前面提到過梧税,一個segment是一個完備的lucene倒排索引,而倒排索引是通過詞典 (Term Dictionary)到文檔列表(Postings List)的映射關(guān)系,快速做查詢的第队。 由于詞典的size會很大哮塞,全部裝載到heap里不現(xiàn)實,因此Lucene為詞典做了一層前綴索引(Term Index)凳谦,這個索引在Lucene4.0以后采用的數(shù)據(jù)結(jié)構(gòu)是FST (Finite State Transducer)忆畅。 這種數(shù)據(jù)結(jié)構(gòu)占用空間很小,Lucene打開索引的時候?qū)⑵淙垦b載到內(nèi)存中尸执,加快磁盤上詞典查詢速度的同時減少隨機(jī)磁盤訪問次數(shù)家凯。
說白了,ES的data node存儲數(shù)據(jù)并非只是耗費磁盤空間的,為了加速數(shù)據(jù)的訪問如失,每個segment都有會一些索引數(shù)據(jù)駐留在heap里绊诲。因此segment越多,瓜分掉的heap也越多褪贵,并且這部分heap是無法被GC掉的掂之! 理解這點對于監(jiān)控和管理集群容量很重要,當(dāng)一個node的segment memory占用過多的時候脆丁,就需要考慮刪除世舰、歸檔數(shù)據(jù),或者擴(kuò)容了。
怎么知道segment memory占用情況呢? CAT API可以給出答案。
- 查看一個索引所有segment的memory占用情況:
GET /_cat/segment/
GET /_cat/nodes?v&h=name,port
那么有哪些途徑減少data node上的segment memory占用呢馆类?
- 刪除不用的索引
- 關(guān)閉索引 (文件仍然存在于磁盤,只是釋放掉內(nèi)存)裆馒。需要的時候可以重新打開。
- 定期對不再更新的索引做optimize (ES2.0以后更改為force merge api)丐怯。這Optimze的實質(zhì)是對segment file強(qiáng)制做合并,可以節(jié)省大量的segment memory翔横。
Filter Cache
Filter cache是用來緩存使用過的filter的結(jié)果集的读跷,需要注意的是這個緩存也是常駐heap,無法GC的禾唁。我的經(jīng)驗是默認(rèn)的10% heap設(shè)置工作得夠好了效览,如果實際使用中heap沒什么壓力的情況下,才考慮加大這個設(shè)置荡短。
Field Data cache
在有大量排序丐枉、數(shù)據(jù)聚合的應(yīng)用場景,可以說field data cache是性能和穩(wěn)定性的殺手掘托。 對搜索結(jié)果做排序或者聚合操作瘦锹,需要將倒排索引里的數(shù)據(jù)進(jìn)行解析,然后進(jìn)行一次倒排。 這個過程非常耗費時間弯院,因此ES 2.0以前的版本主要依賴這個cache緩存已經(jīng)計算過的數(shù)據(jù)辱士,提升性能。但是由于heap空間有限听绳,當(dāng)遇到用戶對海量數(shù)據(jù)做計算的時候颂碘,就很容易導(dǎo)致heap吃緊,集群頻繁GC椅挣,根本無法完成計算過程头岔。 ES2.0以后,正式默認(rèn)啟用Doc Values特性(1.x需要手動更改mapping開啟)鼠证,將field data在indexing time構(gòu)建在磁盤上峡竣,經(jīng)過一系列優(yōu)化,可以達(dá)到比之前采用field data cache機(jī)制更好的性能名惩。因此需要限制對field data cache的使用澎胡,最好是完全不用,可以極大釋放heap壓力娩鹉。 需要注意的是攻谁,很多同學(xué)已經(jīng)升級到ES2.0,或者1.0里已經(jīng)設(shè)置mapping啟用了doc values弯予,在kibana里仍然會遇到問題戚宦。 這里一個陷阱就在于kibana的table panel可以對所有字段排序。 設(shè)想如果有一個字段是analyzed過的锈嫩,而用戶去點擊對應(yīng)字段的排序表頭是什么后果受楼? 一來排序的結(jié)果并不是用戶想要的,排序的對象實際是詞典呼寸; 二來analyzed過的字段無法利用doc values艳汽,需要裝載到field data cache,數(shù)據(jù)量很大的情況下可能集群就在忙著GC或者根本出不來結(jié)果对雪。
Bulk Queue
一般來說河狐,Bulk queue不會消耗很多的heap,但是見過一些用戶為了提高bulk的速度瑟捣,客戶端設(shè)置了很大的并發(fā)量馋艺,并且將bulk Queue設(shè)置到不可思議的大,比如好幾千迈套。 Bulk Queue是做什么用的捐祠?當(dāng)所有的bulk thread都在忙,無法響應(yīng)新的bulk request的時候桑李,將request在內(nèi)存里排列起來踱蛀,然后慢慢清掉窿给。 這在應(yīng)對短暫的請求爆發(fā)的時候有用,但是如果集群本身索引速度一直跟不上星岗,設(shè)置的好幾千的queue都滿了會是什么狀況呢填大? 取決于一個bulk的數(shù)據(jù)量大小,乘上queue的大小俏橘,heap很有可能就不夠用允华,內(nèi)存溢出了。一般來說官方默認(rèn)的thread pool設(shè)置已經(jīng)能很好的工作了寥掐,建議不要隨意去“調(diào)優(yōu)”相關(guān)的設(shè)置靴寂,很多時候都是適得其反的效果。
Indexing Buffer
Indexing Buffer是用來緩存新數(shù)據(jù)召耘,當(dāng)其滿了或者refresh/flush interval到了百炬,就會以segment file的形式寫入到磁盤。 這個參數(shù)的默認(rèn)值是10% heap size污它。根據(jù)經(jīng)驗剖踊,這個默認(rèn)值也能夠很好的工作,應(yīng)對很大的索引吞吐量衫贬。 但有些用戶認(rèn)為這個buffer越大吞吐量越高德澈,因此見過有用戶將其設(shè)置為40%的。到了極端的情況固惯,寫入速度很高的時候梆造,40%都被占用,導(dǎo)致OOM葬毫。
Cluster State Buffer
ES被設(shè)計成每個node都可以響應(yīng)用戶的api請求镇辉,因此每個node的內(nèi)存里都包含有一份集群狀態(tài)的拷貝。這個cluster state包含諸如集群有多少個node贴捡,多少個index忽肛,每個index的mapping是什么?有少shard烂斋,每個shard的分配情況等等 (ES有各類stats api獲取這類數(shù)據(jù))麻裁。 在一個規(guī)模很大的集群,這個狀態(tài)信息可能會非常大的源祈,耗用的內(nèi)存空間就不可忽視了。并且在ES2.0之前的版本色迂,state的更新是由master node做完以后全量散播到其他結(jié)點的香缺。 頻繁的狀態(tài)更新都有可能給heap帶來壓力。 在超大規(guī)模集群的情況下歇僧,可以考慮分集群并通過tribe node連接做到對用戶api的透明图张,這樣可以保證每個集群里的state信息不會膨脹得過大锋拖。
超大搜索聚合結(jié)果集的fetch
ES是分布式搜索引擎,搜索和聚合計算除了在各個data node并行計算以外祸轮,還需要將結(jié)果返回給匯總節(jié)點進(jìn)行匯總和排序后再返回兽埃。無論是搜索,還是聚合适袜,如果返回結(jié)果的size設(shè)置過大柄错,都會給heap造成很大的壓力,特別是數(shù)據(jù)匯聚節(jié)點苦酱。超大的size多數(shù)情況下都是用戶用例不對售貌,比如本來是想計算cardinality,卻用了terms aggregation + size:0這樣的方式; 對大結(jié)果集做深度分頁疫萤;一次性拉取全量數(shù)據(jù)等等颂跨。
小結(jié):
- 倒排詞典的索引需要常駐內(nèi)存,無法GC扯饶,需要監(jiān)控data node上segment memory增長趨勢恒削。
- 各類緩存,field cache, filter cache, indexing cache, bulk queue等等尾序,要設(shè)置合理的大小钓丰,并且要應(yīng)該根據(jù)最壞的情況來看heap是否夠用,也就是各類緩存全部占滿的時候蹲诀,還有heap空間可以分配給其他任務(wù)嗎斑粱?避免采用clear cache等“自欺欺人”的方式來釋放內(nèi)存。
- 避免返回大量結(jié)果集的搜索與聚合脯爪。缺失需要大量拉取數(shù)據(jù)可以采用scan & scroll api來實現(xiàn)则北。
- cluster stats駐留內(nèi)存并無法水平擴(kuò)展,超大規(guī)模集群可以考慮分拆成多個集群通過tribe node連接痕慢。
查詢性能
查詢性能中routing非常重要,
分合: 在實踐過程中,索引越來越大,那么單個shard分片也越來越大,查詢速度也越來越慢.
是選擇分索引還是分shards?
實驗中更多的shards會帶來額外的IO壓力.
Elastic 官方文檔建議:一個 Node 最好不要多于三個 shards尚揣。
線程池
線程池我們默認(rèn)使用 fixed,使用 cached 有可能控制不好掖举。主要是比較大的分片 relocation時快骗,會導(dǎo)致分片自動下線,集群可能處于危險狀態(tài)塔次。在集群高壓時方篮,若是 cached ,分片也可能自動下線励负。