100_es生產(chǎn)集群部署之jvm和服務(wù)器內(nèi)存分配的最佳實踐以及原理分析
除了之前講解的一些配置孽江,根據(jù)你的集群環(huán)境特殊的配置讶坯,我們這一講來講解最重要的內(nèi)存的分配,提出一些問題岗屏,生產(chǎn)環(huán)境部署es辆琅,不可避免要回答一個問題漱办,比如我的機器上有64G的內(nèi)存,或者32G的內(nèi)存婉烟,那么一般來說我應(yīng)該分配多少個G的內(nèi)存給es的jvm heap
1娩井、jvm heap分配
es默認會給jvm heap分配2個G的大小,對于幾乎所有的生產(chǎn)環(huán)境來說隅很,這個內(nèi)存都太小了撞牢。如果用這個默認的heap size率碾,那么生產(chǎn)環(huán)境的集群肯定表現(xiàn)不會太好叔营。
有兩個方式來調(diào)節(jié)es中的jvm heap size。最簡單的就是設(shè)置環(huán)境變量所宰,ES_HEAP_SIZE绒尊。當(dāng)es進程啟動的時候,會讀取這個環(huán)境變量的值仔粥,然后設(shè)置為jvm的heap size婴谱。舉例來說,可以這樣來設(shè)置:export ES_HEAP_SIZE=10g躯泰。此外谭羔,還可以在啟動es進程的時候,傳遞一個jvm的option麦向,比如:ES_JAVA_OPTS="-Xms10g -Xmx10g" ./bin/elasticsearch瘟裸,但是要注意-Xms和-Xmx最小和最大堆內(nèi)存一定設(shè)置的一樣,避免運行過程中的jvm heap resize诵竭,那會是一個非常耗時的過程话告。
在老版本的es中,比如es 2.x里面卵慰,一般推薦用ES_HEAP_SIZE環(huán)境變量的方式來設(shè)置jvm heap size沙郭。
在新版本的es中,比如es 5.x里面裳朋,一般推薦在jvm.options文件里面去設(shè)置jvm相關(guān)的參數(shù)病线。
2、將機器上少于一半的內(nèi)存分配給es
一個常見的問題就是將es進程的jvm heap size設(shè)置的過于大了鲤嫡。比如我們有一臺64G的機器氧苍,可能我們甚至想要給es jvm size設(shè)置64G內(nèi)存。但是這是錯誤的泛范。大家可能會覺得說让虐,直接將機器上的可用的內(nèi)存都分配給es jvm heap,性能是絕對高的罢荡,因為大量的數(shù)據(jù)都可以緩存在內(nèi)存里面赡突。
雖然heap對于es來說是非常重要的对扶,jvm heap被es用來存放很多內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)來提供更快的操作性能。但是還有另外一個內(nèi)存的用戶惭缰,那就是lucene浪南。lucene的設(shè)計就是要使用底層的os filesystem cache來緩存數(shù)據(jù)結(jié)構(gòu)。lucene的segment是保存在單獨的文件中的漱受。因為這些segment是不可變的络凿,所以這些文件實際上也從來不會改變。這樣的話昂羡,就可以更好的緩存這些文件絮记,底層的os cache會將hot segment駐留在內(nèi)存中以供更快的訪問。這些segment包括了倒排索引(為了全文檢索)以及正排索引(為了聚合操作)虐先。lucene的性能是嚴重依賴于底層的os的怨愤,但是如果我們給了過多的內(nèi)存到es的jvm heap,那么就沒有足夠的內(nèi)存留給lucene蛹批。這會極大的影響性能撰洗。
這里想告訴大家的是,就是說腐芍,es的性能很大的一塊差导,其實是由有多少內(nèi)存留給操作系統(tǒng)的os cache,供lucene去緩存索引文件猪勇,來決定的设褐。所以說lucene的os cache有多少是非常重要的。
一般建議的是埠对,將50%的內(nèi)存分配給es jvm heap络断,然后留50%的內(nèi)存給os cache。留給os cache的內(nèi)存是不會不使用的项玛,lucene會將剩下的內(nèi)存全部用光貌笨,用來cache segment file。如果我們沒有對任何分詞的text field進行聚合操作襟沮,那么我們就不需要使用fielddata锥惋,我們甚至可以考慮給os cache更多的內(nèi)存,因為fielddata是要用jvm heap开伏。如果我們給jvm heap更少的內(nèi)存膀跌,那么實際上es的性能反而會更好,因為更多的內(nèi)存留給了lucene用os cache提升索引讀寫性能固灵,同時es的jvm heap的gc耗時會更少捅伤。
es部署的機器上,內(nèi)存是如何分配的巫玻,如何使用的丛忆,如何決定我們的操作系統(tǒng)的祠汇,我們該如何給jvm和os cache分配內(nèi)存
3、不要給jvm分配超過32G內(nèi)存
還有另外一個原因不要將過多的內(nèi)存分配給es的jvm heap熄诡。如果heap小于32G的化可很,jvm會用一種技術(shù)來壓縮對象的指針,object pointer凰浮。在java中我抠,所有的對象都會被分配到heap中,然后被一個pointer給引用袜茧。object pointer會指向heap中的對象菜拓,引用的是二進制格式的地址。
對于32位的系統(tǒng)來說惫周,jvm最大的heap size就是4G尘惧,解釋一下康栈,32位递递,0和1值,0和1在32位的組合是2^32次方的字節(jié)啥么,除以1024就是多少k登舞,再除以1024就是多少mb,再除以1024就是多少gb悬荣,最后算下來就是4G菠秒。對于64位的系統(tǒng)來說,heap size可以更大氯迂,但是64位的object pointer會耗費更多的空間践叠,因為object pointer更大了。比浪費更多內(nèi)存空間更惡劣的是嚼蚀,過大的object pointer會在cpu禁灼,main memory和LLC、L1等多級緩存間移動數(shù)據(jù)的時候轿曙,吃掉更多的帶寬弄捕。
所以jvm用了一種技術(shù),叫做compressed oops來解決object pointer耗費過大空間的問題导帝。這個技術(shù)的核心思想是守谓,不要讓object pointer引用內(nèi)存中的二進制地址,而是讓object pointer引用object offset您单。這就意味著32位的pointer可以引用400萬個對象斋荞,而不是400萬字節(jié)。這也意味著虐秦,使用32位的pointer平酿,最大的heap大小可以到32G讯檐。此時只要heap size在32G以內(nèi),jvm就會自動啟用32位的object pointer染服,因為32位的對象指針别洪,足夠引用32G的內(nèi)存了,就可以用32位的pointer替代64位的pointer柳刮。但是32位的pointer比64位的pointer可以耗費更少的內(nèi)存耗費挖垛。
如果你給jvm heap分配的內(nèi)存小于32G,此時jvm會自動使用32位的object pointer秉颗,同時是讓pointer指向?qū)ο蟮膐ffset痢毒,32位的object pointer就足以引用32G的內(nèi)存,同時32位的pointer占用的內(nèi)存空間很少蚕甥,對cpu和memory之間移動數(shù)據(jù)的帶寬開銷也很少哪替。這個過程就叫做compressed oops。
但是一旦我們越過了32G這個界限菇怀,就是給jvm heap分配了超過32G的內(nèi)存凭舶,比較坑了。就沒有辦法用32位的pointer+引用object offset的模式了爱沟,因為32位的pointer最多引用32G的內(nèi)存帅霜,超過了32G,就沒法用32位pointer呼伸。不用32位pointer身冀,就只能用64位pointer,才能引用超過32G的內(nèi)存空間括享。此時pointer就會退回到傳統(tǒng)的object pointer引用對象的二進制地址的模式搂根,此時object pinter的大小會急劇增長,更多的cpu到內(nèi)存的帶寬會被占據(jù)铃辖,更多的內(nèi)存被耗費剩愧。實際上,不用compressed oops時澳叉,你如果給jvm heap分配了一個40~50G的內(nèi)存的可用空間隙咸,實際上被object pointer可能都要占據(jù)十幾G的內(nèi)存空間,可用的空間量成洗,可能跟使用了compressed oops時的32GB內(nèi)存的可用空間五督,20多個G,幾乎是一樣的瓶殃。
因此充包,即使我們有很多內(nèi)存,但是還是要分配給heap在32GB以內(nèi),否則的話浪費更多的內(nèi)存基矮,降低cpu性能淆储,而且會讓jvm回收更大的heap。
綜上所述家浇,如果你給jvm heap分配超過32G的內(nèi)存本砰,實際上是沒有什么意義的,因為用64位的pointer钢悲,1/3的內(nèi)存都給object pointer給占據(jù)了点额,這段內(nèi)存空間就浪費掉了。還不如分配32G以內(nèi)莺琳,啟用compressed oops还棱,可用空間跟你分配50個G的內(nèi)存,是一樣的惭等。
所以也正是因為32G的限制珍手,一般來說,都是建議說辞做,如果你的es要處理的數(shù)據(jù)量上億的話琳要,幾億,或者十億以內(nèi)的規(guī)模的話凭豪,建議焙蹭,就是用64G的內(nèi)存的機器比較合適晒杈,有個5臺嫂伞,差不多也夠了。給jvm heap分配32G拯钻,留下32G給os cache帖努。
4、在32G以內(nèi)的話具體應(yīng)該設(shè)置heap為多大粪般?
這個是根據(jù)具體情況而定的拼余,不是固定死的,根據(jù)不同的jvm和平臺而變亩歹。一般而言匙监,將jvm heap size設(shè)置為31G比較安全一些。主要是要確保說小作,你設(shè)置的這個jvm heap大小亭姥,可以讓es啟用compressed oops這種優(yōu)化機制。此外顾稀,可以給jvm option加入-XX:+PrintFlagsFinal达罗,然后可以打印出來UseCompressedOops是否為true。這就可以讓我們找到最佳的內(nèi)存設(shè)置静秆。因為可以不斷調(diào)節(jié)內(nèi)存大小粮揉,然后觀察是否啟用compressed oops巡李。
舉例來說,如果在mac os上啟動一個java 1.7扶认,同時將heap size設(shè)置為32600mb侨拦,那么compressed oops是會開啟的;但是如果設(shè)置為32766m辐宾,compressed oops就不會開啟阳谍。相反的是,使用jdk 1.8的化螃概,分配32766m矫夯,compressed oops是會開啟的,設(shè)置為32767m吊洼,就不會開啟训貌。所以說,這個東西不是固定的冒窍。根據(jù)不同的操作系統(tǒng)以及jvm版本而定递沪。
在es啟動日志中,我們可以查看compressed oops是否開啟综液,比如下面的字樣:[2015-12-16 13:53:33,417][INFO ][env] [Illyana Rasputin] heap size [989.8mb], compressed ordinary object pointers [true]款慨。
5、對于有1TB內(nèi)存的超大內(nèi)存機器該如何分配谬莹?
如果我們的機器是一臺超級服務(wù)器檩奠,內(nèi)存資源甚至達到了1TB,或者512G附帽,128G埠戳,該怎么辦?首先es官方是建議避免用這種超級服務(wù)器來部署es集群的蕉扮,但是如果我們只有這種機器可以用的話整胃,我們要考慮以下幾點:
(1)我們是否在做大量的全文檢索?考慮一下分配4~32G的內(nèi)存給es進程喳钟,同時給lucene留下其余所有的內(nèi)存用來做os filesystem cache屁使。所有的剩余的內(nèi)存都會用來cache segment file,而且可以提供非常高性能的搜索奔则,幾乎所有的數(shù)據(jù)都是可以在內(nèi)存中緩存的蛮寂,es集群的性能會非常高
(2)是否在做大量的排序或者聚合操作?聚合操作是不是針對數(shù)字应狱、日期或者未分詞的string共郭?如果是的化,那么還是給es 4~32G的內(nèi)存即可,其他的留給es filesystem cache除嘹,可以將聚合好用的正排索引写半,doc values放在os cache中
(3)如果在針對分詞的string做大量的排序或聚合操作?如果是的化尉咕,那么就需要使用fielddata叠蝇,這就得給jvm heap分配更大的內(nèi)存空間。此時不建議運行一個節(jié)點在機器上年缎,而是運行多個節(jié)點在一臺機器上悔捶,那么如果我們的服務(wù)器有128G的內(nèi)存,可以運行兩個es節(jié)點单芜,然后每個節(jié)點分配32G的內(nèi)存蜕该,剩下64G留給os cache。如果在一臺機器上運行多個es node洲鸠,建議設(shè)置:cluster.routing.allocation.same_shard.host: true堂淡。這會避免在同一臺物理機上分配一個primary shard和它的replica shard。
6扒腕、swapping
如果頻繁的將es進程的內(nèi)存swap到磁盤上绢淀,絕對會是一個服務(wù)器的性能殺手。想象一下瘾腰,內(nèi)存中的操作都是要求快速完成的皆的,如果需要將內(nèi)存頁的數(shù)據(jù)從磁盤swap回main memory的化,性能會有多差蹋盆。如果內(nèi)存被swap到了磁盤费薄,那么100微秒的操作會瞬間變成10毫秒,那么如果是大量的這種內(nèi)存操作呢怪嫌?這會導(dǎo)致性能急劇下降义锥。
因此通常建議徹底關(guān)閉機器上的swap,swapoff -a岩灭,如果要永久性關(guān)閉,需要在/etc/fstab中配置
如果沒法完全關(guān)閉swap赂鲤,那么可以嘗試調(diào)低swappiness至噪径,這個值是控制os會如何將內(nèi)存swap到磁盤的。這會在正常情況下阻止swap数初,但是在緊急情況下找爱,還是會swap。一般用sysctl來設(shè)置泡孩,vm.swappiness = 1车摄。如果swappiness也不能設(shè)置,那么就需要啟用mlockall,這就可以讓我們的jvm lock住自己的內(nèi)存不被swap到磁盤上去吮播,在elasticsearch.yml中可以設(shè)置:bootstrap.mlockall: true变屁。