乾貨 | 喫透Elasticsearch 堆內存

1、什麼是堆內存?


Java 中的堆是 JVM 所管理的最大的一塊內存空間,主要用於存放各種類的實例對象。
在 Java 中,堆被劃分成兩個不同的區域:
- 新生代 ( Young )、
- 老年代 ( Old )。

新生代 ( Young ) 又被劃分爲三個區域

- Eden、
- From Survivor、
- To Survivor。

這樣劃分的目的是爲了使 JVM 能夠更好的管理堆內存中的對象,包括內存的分配以及回收。

2、堆內存的作用是什麼?


在虛擬機啓動時創建。

堆內存的唯一目的就是創建對象實例,所有的對象實例和數組都要在堆上分配。

堆是由垃圾回收來負責的,因此也叫做“GC堆”,垃圾回收採用分代算法,堆由此分爲新生代和老年代。

堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因爲它是在運行時動態分配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。

但缺點是,由於要在運行時動態分配內存,存取速度較慢。當堆內存因爲滿了無法擴展時就會拋出java.lang.OutOfMemoryError:Java heap space異常。出現這種情況的解決辦法具體參見java調優。

3、堆內存如何配置?


默認情況下,Elasticsearch JVM使用堆內存最小和最大大小爲2 GB(5.X版本以上)。

早期版本默認1GB,官網指出:這明顯不夠。

在轉移到生產環境時,配置足夠容量的堆大小以確保Elasticsearch功能和性能是必要的。

Elasticsearch將通過Xms(最小堆大小)和Xmx(最大堆大小)設置來分配jvm.options中指定的整個堆。

舉例如下:

設置方式一:
在jvm.option配置文件中設置堆內存。

-Xms2g 
-Xmx2g
1
2
設置方式二:
通過環境變量設置。
這可以通過註釋掉jvm.options文件中的Xms和Xmx設置並通過ES_JAVA_OPTS設置這些值來完成:

ES_JAVA_OPTS="-Xms2g -Xmx2g" ./bin/elasticsearch 
ES_JAVA_OPTS="-Xms4000m -Xmx4000m" ./bin/elasticsearch
1
2


4、堆內存的決定因素


堆內存的值取決於服務器上可用的內存大小。

5、堆內存配置建議


將最小堆大小(Xms)和最大堆大小(Xmx)設置爲彼此相等。

Elasticsearch可用的堆越多,可用於緩存的內存就越多。但請注意,太多的堆內存可能會使您長時間垃圾收集暫停。

將Xmx設置爲不超過物理內存的50%,以確保有足夠的物理內存留給內核文件系統緩存。

- 不要將Xmx設置爲JVM超過32GB。

大小建議:
宿主機內存大小的一半和31GB,取最小值。
 


6、堆內存爲什麼不能超過物理機內存的一半?


堆對於Elasticsearch絕對重要。
它被許多內存數據結構用來提供快速操作。但還有另外一個非常重要的內存使用者:Lucene。

Lucene旨在利用底層操作系統來緩存內存中的數據結構。 Lucene段(segment)存儲在單個文件中。因爲段是一成不變的,所以這些文件永遠不會改變。這使得它們非常容易緩存,並且底層操作系統將愉快地將熱段(hot segments)保留在內存中以便更快地訪問。這些段包括倒排索引(用於全文搜索)和文檔值(用於聚合)。

Lucene的性能依賴於與操作系統的這種交互。但是如果你把所有可用的內存都給了Elasticsearch的堆,那麼Lucene就不會有任何剩餘的內存。這會嚴重影響性能。

標準建議是將可用內存的50%提供給Elasticsearch堆,而將其他50%空閒。它不會被閒置; Lucene會高興地吞噬掉剩下的東西。

如果您不字符串字段上做聚合操作(例如,您不需要fielddata),則可以考慮進一步降低堆。堆越小,您可以從Elasticsearch(更快的GC)和Lucene(更多內存緩存)中獲得更好的性能。

7、堆內存爲什麼不能超過32GB?


在Java中,所有對象都分配在堆上並由指針引用。普通的對象指針(OOP)指向這些對象,傳統上它們是CPU本地字的大小:32位或64位,取決於處理器。

對於32位系統,這意味着最大堆大小爲4 GB。對於64位系統,堆大小可能會變得更大,但是64位指針的開銷意味着僅僅因爲指針較大而存在更多的浪費空間。並且比浪費的空間更糟糕,當在主存儲器和各種緩存(LLC,L1等等)之間移動值時,較大的指針消耗更多的帶寬。

Java使用稱爲壓縮oops的技巧來解決這個問題。而不是指向內存中的確切字節位置,指針引用對象偏移量。這意味着一個32位指針可以引用40億個對象,而不是40億個字節。最終,這意味着堆可以增長到約32 GB的物理尺寸,同時仍然使用32位指針。

一旦你穿越了這個神奇的〜32 GB的邊界,指針就會切換回普通的對象指針。每個指針的大小增加,使用更多的CPU內存帶寬,並且實際上會丟失內存。實際上,在使用壓縮oops獲得32 GB以下堆的相同有效內存之前,需要大約40-50 GB的分配堆。

以上小結爲:即使你有足夠的內存空間,儘量避免跨越32GB的堆邊界。
否則會導致浪費了內存,降低了CPU的性能,並使GC在大堆中掙扎。

8、我是內存土豪怎麼辦?


假設,我有一臺帶有1TB RAM的機器!
1
32 GB的基線相當重要。那麼當你的機器有很多內存時你怎麼做?當前具有512-768 GB RAM的超級服務器變得越來越普遍。

首先,我們建議避免使用這種大型機器。

但是如果你已經有了這些機器,你有三種實用的選擇:

1. 你是否主要進行全文搜索?

考慮給Elasticsearch提供4-32 GB,並讓Lucene通過操作系統文件系統緩存使用剩餘的內存。所有內存都會緩存段,並導致快速全文搜索。

2. 你在做很多排序/聚合?

大部分聚合數字,日期,地理位置和not_analyzed字符串?你很幸運,你的聚合將在內存緩存的文檔值上完成!

從4-32 GB的內存中給Elasticsearch一個地方,剩下的讓操作系統在內存中緩存doc值。

3. 你是否對分析過的字符串進行了很多排序/聚合(例如對於字標記或SigTerms等)?

不幸的是,這意味着你需要fielddata,這意味着你需要堆空間。

考慮在一臺機器上運行兩個或多個節點,而不是一個節點數量巨大的RAM。

儘管如此,仍然堅持50%的規則。

To土豪內存小結:

因此,如果您的機器具有128 GB的RAM,請運行兩個節點,每個節點的容量低於32 GB。這意味着小於64 GB將用於堆,而Lucene將剩餘64 GB以上。

如果您選擇此選項,請在您的配置中設置cluster.routing.allocation.same_shard.host:true。這將阻止主副本分片共享同一臺物理機(因爲這會消除副本高可用性的好處)。

9、堆內存優化建議


方式一:最好的辦法是在系統上完全禁用交。
這可以暫時完成:

sudo swapoff -a
1
要永久禁用它,你可能需要編輯你的/ etc / fstab。

方式二:控制操作系統嘗試交換內存的積極性。

如果完全禁用交換不是一種選擇,您可以嘗試降低swappiness。該值控制操作系統嘗試交換內存的積極性。這可以防止在正常情況下交換,但仍然允許操作系統在緊急內存情況下進行交換。

對於大多數Linux系統,這是使用sysctl值配置的:

vm.swappiness = 1
1
1的swappiness優於0,因爲在某些內核版本上,swappiness爲0可以調用OOM殺手。

方式三:mlockall允許JVM鎖定其內存並防止其被操作系統交換。

最後,如果兩種方法都不可行,則應啓用mlockall。文件。這允許JVM鎖定其內存並防止其被操作系統交換。在你的elasticsearch.yml中,設置這個:

bootstrap.mlockall:true


10、注意


修改JVM相關配置很容易,但容易產生難以測量的不透明效果,並最終將您的羣集解調爲緩慢,不穩定的混亂
在調試羣集時,第一步通常是刪除所有自定義配置。大約一半的時間,僅靠這一點就恢復了穩定性和性能。


11、最新認知


事實上,給ES分配的內存有一個魔法上限值26GB,

這樣可以確保啓用zero based Compressed Oops,這樣性能纔是最佳的。

參考:https://elasticsearch.cn/question/3995
https://www.elastic.co/blog/a-heap-of-trouble

12、小結


這是一篇官網文檔&原理的整合文章,主要目的是梳理認知。

13、參考


基礎:http://t.cn/RH4DDYu
設置:http://t.cn/RmKbO1i
建議:http://t.cn/RmKbjsF
注意:http://t.cn/RmKbHp5
堆:http://t.cn/RmKbRji

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章