1. 程式人生 > 其它 >ElasticSearch優化系列二:機器設定(記憶體)

ElasticSearch優化系列二:機器設定(記憶體)

預留一半記憶體給Lucence使用

一個常見的問題是配置堆太大。你有一個64 GB的機器,覺得JVM記憶體越大越好,想給Elasticsearch所有64 GB的記憶體。

當然,記憶體對於Elasticsearch來說絕對是重要的,用於更多的記憶體資料提供更快的操作。而且還有一個記憶體消耗大戶-Lucene Lucene的設計目的是把底層OS裡的資料快取到記憶體中。Lucene的段是分別儲存到單個檔案中的,這些檔案都是不會變化的,所以很利於快取,同時作業系統也會把這些段檔案快取起來,以便更快的訪問。

Lucene的效能取決於和OS的互動,如果你把所有的記憶體都分配給Elasticsearch,不留一點給Lucene,那你的全文檢索效能會很差的。

最後標準的建議是把50%的記憶體給elasticsearch,剩下的50%也不會沒有用處的,Lucene會很快吞噬剩下的這部分記憶體。

32GB限制

給ES的記憶體配置不是越大越好,建議不能超過32GB,不同jdk版本最大邊界值是不同的,對於32位小於32G JVM才採用記憶體物件指標壓縮技術,不然物件指標需要佔用很大的記憶體。

使用如下命令測試最大邊界值:

java -Xmx32767m -XX:+PrintFlagsFinal 2> /dev/null | grep UseCompressedOops      
bool UseCompressedOops       = false         {lp64_product}     
java -Xmx32766m -XX:+PrintFlagsFinal 2> /dev/null | grep UseCompressedOops     
 bool UseCompressedOops       = true          {lp64_product}    
$ JAVA_HOME=`/usr/libexec/java_home -v 1.8` java -Xmx32766m -XX:+PrintFlagsFinal 2> /dev/null | grep UseCompressedOops      
bool UseCompressedOops   := true
 $ JAVA_HOME=`/usr/libexec/java_home -v 1.8` java -Xmx32767m -XX:+PrintFlagsFinal 2> /dev/null | grep UseCompressedOops
 bool UseCompressedOops   = false

在ES啟動日誌中最好能夠看到壓縮物件指標為真。

heap size [15.8gb], compressed ordinary object pointers [true] 在java中,所有的物件都分配在堆上,然後有一個指標引用它。指向這些物件的指標大小通常是CPU的字長的大小,不是32bit就是64bit,這取決於你的處理器,指標指向了你的值的精確位置。

對於32位系統,你的記憶體最大可使用4G。對於64系統可以使用更大的記憶體。但是64位的指標意味著更大的浪費,因為你的指標本身大了。浪費記憶體不算,更糟糕的是,更大的指標在主記憶體和快取器(例如LLC, L1等)之間移動資料的時候,會佔用更多的頻寬。

java 使用一個叫記憶體指標壓縮的技術來解決這個問題。它的指標不再表示物件在記憶體中的精確位置,而是表示偏移量。這意味著32位的指標可以引用40億個物件,而不是40億個位元組。最終,也就是說堆記憶體長到32G的實體記憶體,也可以用32bit的指標表示。

一旦你越過那個神奇的30-32G的邊界,指標就會切回普通物件的指標,每個物件的指標都變長了,就會使用更多的CPU記憶體頻寬,也就是說你實際上失去了更多的記憶體。事實上當記憶體到達40-50GB的時候,有效記憶體才相當於使用記憶體物件指標壓縮技術時候的32G記憶體。

這段描述的意思就是說:即便你有足夠的記憶體,也儘量不要超過32G,因為它浪費了記憶體,降低了CPU的效能,還要讓GC應對大記憶體。

機器記憶體大於64GB

你可以考慮一臺機器上建立兩個或者更多ES節點,而不要部署一個使用32+GB記憶體的節點。仍然要 堅持50%原則,假設 你有個機器有128G記憶體,你可以建立兩個node,使用32G記憶體。也就是說64G記憶體給ES的堆記憶體,剩下的64G給Lucene。

如果你選擇第二種,你需要配置cluster.routing.allocation.same_shard.host:true。這會防止同一個shard的主副本存在同一個物理機上(因為如果存在一個機器上,副本的高可用性就沒有了)

swapping是效能的墳墓

這是顯而易見的,但是還是有必要說的更清楚一點,記憶體交換到磁碟對伺服器效能來說是致命的。想想看一個記憶體的操作必須是快速的。

如果記憶體交換到磁碟上,一個100微秒的操作可能變成10毫秒,再想想那麼多10微秒的操作時延累加起來。不難看出swapping對於效能是多麼可怕。

最好的辦法就是在你的作業系統中完全禁用swapping。這樣可以暫時禁用:

sudo swapoff -a

為了永久禁用它,你可能需要修改/etc/fstab檔案,這要參考你的作業系統相關文件。

如果完全禁用swap,對你來說是不可行的。你可以降低swappiness 的值,這個值決定作業系統交換記憶體的頻率。這可以預防正常情況下發生交換。但仍允許os在緊急情況下發生交換。對於大部分Linux作業系統,可以在sysctl 中這樣配置:

vm.swappiness = 1

備註:swappiness設定為1比設定為0要好,因為在一些核心版本,swappness=0會引發OOM(記憶體溢位)

最後,如果上面的方法都不能做到,你需要開啟配置檔案中的mlockall開關,它的作用就是執行JVM鎖住記憶體,禁止OS交換出去。在elasticsearch.yml配置如下:

bootstrap.mlockall: true