ElasticSearch搜索引擎: 内存分析与设置
? ? ? ? 在?Elasticsearch 的運行過程中,如何合理分配與設置內存是一件十分重要的事情,否則十分容易出現各種問題。
一、Elasticsearch為什么吃內存:
我們先看下 ES 服務器的總體內存消耗情況:
對于Query Cache、Request Cache、FieldData Cache、Indexing Buffer 以及 Segment 的介紹,在前面的文章以及介紹過了,這里就不重復介紹了:
Elasticsearch搜索引擎之緩存:Request Cache、Query Cache、Fielddata Cache
ElasticSearch搜索引擎:數據的寫入流程
? ? ? ? 要回答?Elasticsearch 為什么這么耗費內存的問題,我們需要從兩個角度切入:
(1)ES 是 JAVA 應用,那么就與?JVM 與 GC 息息相關。 我們這里不對 JVM GC 做深入探討,只需知道:應用層面生成大量長生命周期的對象,是給 heap 造成壓力的主要原因。例如讀取大量數據在內存中進行排序,或者在 heap 內部緩存大量數據,如果 GC 釋放的空間有限,而應用層面持續大量申請新對象,GC 頻率就開始上升,不僅會消耗掉很多CPU時間,嚴重時可能惡性循環,導致整個集群停工。
(2)ES 底層存儲引擎是基于 Lucene 的,Lucene 的倒排索引(Inverted Index)是先在內存里生成,然后定期以段文件(segment file)的形式刷到磁盤的。每個段實際就是一個完整的倒排索引,并且一旦寫到磁盤上就不會做修改。API 層面的文檔更新和刪除實際上是增量寫入的一種特殊文檔,會保存在新的段里,所以不變的段文件非常容易被操作系統緩存,熱數據幾乎等效于內存訪問。
二、Elasticsearch 的內存分配:
為什么分配給ES的堆內存不能超過物理機內存的一半?
1、預留一半內存給Lucene使用:
????????為什么需要預留一半的內存給?Lucene,將所有的內存都分配給?Elasticsearch 不是更好嗎?毋庸置疑,堆內存對于 ES 來說絕對是重要的,但還有另外一個非常重要的內存使用者——Lucene。
? ? ? ? 在講 Lucene 前,我們先簡單介紹一下?segment。每個 segment 段是分別存儲到單個文件的,即 segment 文件。它其實是一個包含正排(空間占90~95%) + 倒排(占5~10%) 的完整索引文件,并且一旦寫到磁盤上就不會再修改,ES中文檔更新和刪除實際上是增量寫入的一種特殊文檔,會保存在新的段里而不修改舊的段。
????????回到 Lucene,Lucene 實際目的就是把底層 OS 里的數據緩存到內存中。由于 Lucene 的段 segment 是不會變化的,所以很利于緩存,操作系統會將這些段文件緩存起來,以便更快的訪問。這些段包括倒排索引(用于全文搜索)和文檔值(用于聚合)。
????????Lucene 的性能依賴于與 OS 的這種交互,如果把所有的內存都給了ES的堆內存,而不留一點給?Lucene,那么全文檢索的性能會很差的。所以官方建議是將可用內存的 50% 提供給ES堆,而其他 50% 的剩余內存也并不會被閑置,因為 Lucene 會利用他們來緩存被用讀取過的段文件。
????????ES的文件存儲類型默認使用的是 mmap 內存映射,將 Lucene 索引文件用映射到內存中,這樣進程就能夠直接從內存中讀取 Lucene 數據了。由于使用了內存映射,ES 進程讀取 Lucene 文件時讀取到的數據就會占用了堆外內存的空間。
2、分配給 ES 的堆內存不要超過 32G:
????????堆內存為什么不能超過32GB?事實上 JVM 在內存小于 32 G 的時候會采用一種內存對象指針壓縮技術。
????????在 Java 中,所有的對象都分配在堆上,然后有一個指針引用它。指向這些對象的指針大小通常是 CPU 的字長的大小,不是 32 bit 就是 64 bit,這取決于你的處理器,指針指向了你的值的精確位置。對于 32 位系統,你的內存最大可使用 4 G。對于 64 系統可以使用更大的內存。但是 64 位的指針意味著更大的浪費,因為你的指針本身大了。并且比浪費的空間更糟糕的是,更大的指針在主內存和緩存器(例如 LLC,L1 等)之間移動數據的時候,會占用更多的帶寬。
????????Java 使用一個叫內存指針壓縮的技術來解決這個問題。它的指針不再表示對象在內存中的精確位置,而是表示偏移量。這意味著 32 位的指針可以引用 40 億個對象,而不是 40 億個字節。最終,也就是說堆內存長到 32 G 的物理內存,也可以用 32 bit 的指針表示。
????????一旦越過那個神奇的 30 - 32 G 的邊界,指針就會切回普通對象的指針,每個對象的指針都變長了,就會使用更多的 CPU 內存帶寬,也就是說你實際上失去了更多的內存。事實上當內存到達 40 - 50 GB 的時候,有效內存才相當于使用內存對象指針壓縮技術時候的 32 G 內存。
????????所以,即便你有足夠的內存,也盡量不要超過 32 G,因為它浪費了內存,降低了 CPU 的性能,還要讓 GC 應對大內存。
3、ElasticSearch 的堆內存該設置:
????????分配給 Heap 堆的內存不要超過系統可用物理內存的一半,以確保有足夠的物理內存留給 Lucene 系統文件緩存,并且不要超過 32 GB。那么 JVM 參數呢?只需要將最小堆大小(Xms)和最大堆大小(Xmx)設置和 heap 一樣大小,避免動態分配 heap size 就好了。確保 Xms 和 Xmx 的大小是相同的,其目的是為了能夠在 java 垃圾回收機制清理完堆區后不需要重新分隔計算堆區的大小而浪費資源,可以減輕伸縮堆大小帶來的壓力。
? ? ? ? 雖然說 32 GB 是 ES 的一個內存設置限制,那如果機器有很大的內存怎么辦?比如現在的機器內存普遍都大,設置有 300 - 500 GB 內存的機器。當然,如果有這種機器,那是極好的,接下來有兩個方案:
(1)如果主要做全文檢索,可以考慮給 Elasticsearch 32 G 內存,剩下的交給 Lucene 用作操作系統的文件系統緩存,所有的 segment 都緩存起來,會加快全文檢索。
(2)如果需要更多的排序和聚合,那就需要更大的堆內存。可以考慮一臺機器上創建兩個或者更多的 ES 節點,而不要部署一個使用 32 + GB 內存的節點。仍然要堅持 50% 原則,假設你有個機器有 128 G 內存,你可以創建兩個 node,使用 32 G 內存。也就是說 64 G 內存給 ES 的堆內存,剩下的 64 G 給 Lucene。
PS:如果選擇第二種方案,需要配置?cluster.routing.allocation.same_shard.host: true,防止同一個 shard 的主副本存在同一個物理機上,因為如果存在一個機器上,副本的高可用性就沒有了
推薦文章:ES的內存問題分析:
ElasticSearch CPU和內存占用高的優化記錄
【Elasticsearch優化】Elasticsearch內存那些事兒
總結
以上是生活随笔為你收集整理的ElasticSearch搜索引擎: 内存分析与设置的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sentinel 限流熔断神器详细介绍
- 下一篇: Nacos注册中心的部署与用法详细介绍