您好,登錄后才能下訂單哦!
Elasticsearch常常作為日志存儲和分析的工具,在企業級應用中常常使用。Elasticsearch提供強大的搜索、分析功能,已經是后端技術棧不可缺少的一部分。
在維護ElastciSearch集群的時候,對Elasticsearch進行了一些調優和分析,現整理成文,純屬拙見,如果有不合理之處,歡迎指出探討。我所使用的Elasticsearch版本為5.x。
Elasticsearch有大量的查詢數據和插入數據的請求,需要大量文件句柄,centos系統默認的1024個文件句柄。如果文件句柄用完了,這就意味著操作系統會拒絕連接,意味著數據可能丟失,這是災難性的后果,
不能被接受。登陸Elasticsearch的啟動用戶,用一下命令查看:
ulimit?-a
查看結果:
core?file?size?(blocks,?-c)?0 data?seg?size?(kbytes,?-d)?unlimited scheduling?priority?(-e)?0 file?size?(blocks,?-f)?unlimited pending?signals?(-i)?127673 max?locked?memory?(kbytes,?-l)?unlimited max?memory?size?(kbytes,?-m)?unlimited open?files?(-n)?1024 pipe?size?(512?bytes,?-p)?8 POSIX?message?queues?(bytes,?-q)?819200 real-time?priority?(-r)?0 stack?size?(kbytes,?-s)?8192 cpu?time?(seconds,?-t)?unlimited max?user?processes?(-u)?2056474 virtual?memory?(kbytes,?-v)?unlimited file?locks?(-x)?unlimited
上面的文件句柄(open files)的個數為1024,在ElasticSearch大量請求的情況下,這個句柄數量是不夠的,可以改成655360。
臨時修改可以通過執行以下命令,即可立即生效,但是機器重啟后又會失效:
ulimit?-n?655360
永久生效,修改/etc/security/limits.conf,需要重啟機器生效:
u_es?-?nofile?655360
上述配置中u_es為啟動ElasticSearch的用戶,設置了該用戶的ElasticSearch的文件句柄為655360、
JVM參數優化
Elasticsearch是運行在JVM上的,對其做JVM參數調優至關重要。最常見的調優是Java內存的分配。下面是JVM的內存模型,具體每塊的作用,不在這里闡述。
新生代和老年代分配的內存比例給多大?
Jvm內存分為新生代和老年代。
新生代(或者伊甸園)
新實例化的對象分配的空間。新生代空間通常都非常小,一般在 100 MB–500 MB。新生代也包含兩個 幸存 空間。
老年代
較老的對象存儲的空間。這些對象預計將長期留存并持續上很長一段時間。老生代通常比新生代大很多。
新生代、老生代的垃圾回收都有一個階段會“stop the world”。在這段時間里,JVM 停止了程序運行,以便對對象進行可達性分析,收集死亡對象。在這個時間停止階段,一切都不會發生。請求不被服務,ping 不被回應,分片不被分配。整個世界都真的停止了。
對于新生代,這不是什么大問題;那么小的空間意味著 GC 會很快執行完。但是老生代大很多,而這里面一個慢 GC 可能就意味著 1 秒乃至 15 秒的暫停——對于服務器軟件來說這是不可接受的。
那一般我們給新生代和老年代分配多大的內存呢?他們的比例是多少呢?
一般來說,老年代和新生代的內存比例為2:1是比較合適的。比如給堆內存分配3G,則新生代分配1G,其余都給老年代。在ElasticSearce的配置文件jvm.options文件配置:
-Xms3g?//配置堆初始化大小 -Xmx3g?//配置堆的最大內存 -Xmn1g?//配置新生代內存。
該分配多大的內存給Elasticesearch?
在使用Elasticesearch的時候,我們對裝Elasticesearch的機器進行了升級,從最小的8G內存升級到了16G內存,然后到目前的32G內存。一臺機器裝一個Elasticesearch節點,我們應該怎么分配機器的內存呢?
官方給出了解決方案,把一半(少于)的內存分配給Luence,另外的內存分配給ElasticSearch.
內存對于 Elasticsearch 來說絕對是重要的,它可以被許多內存數據結構使用來提供更快的操作。但是說到這里, 還有另外一個內存消耗大戶 非堆內存 (off-heap):Lucene。
Lucene 被設計為可以利用操作系統底層機制來緩存內存數據結構。Lucene 的段是分別存儲到單個文件中的。因為段是不可變的,這些文件也都不會變化,這是對緩存友好的,同時操作系統也會把這些段文件緩存起來,以便更快的訪問。
Lucene 的性能取決于和操作系統的相互作用。如果你把所有的內存都分配給 Elasticsearch 的堆內存,那將不會有剩余的內存交給 Lucene。這將嚴重地影響全文檢索的性能。
標準的建議是把 50% 的可用內存作為 Elasticsearch 的堆內存,保留剩下的 50%。當然它也不會被浪費,Lucene 會很樂意利用起余下的內存。
我們實際的解決辦法是將機器的一半分給Elasticesearch的堆,棧內存、方法區、常量池、非堆內存占用另外一半。
分配給堆最大內存應該小于 32766 mb(~31.99 gb)
JVM 在內存小于 32 GB 的時候會采用一個內存對象指針壓縮技術。
對于 32 位的系統,意味著堆內存大小最大為 4 GB。對于 64 位的系統, 可以使用更大的內存,但是 64 位的指針意味著更大的浪費,因為你的指針本身大了。更糟糕的是, 更大的指針在主內存和各級緩存(例如 LLC,L1 等)之間移動數據的時候,會占用更多的帶寬。
Java 使用一個叫作 內存指針壓縮(compressed oops)的技術來解決這個問題。它的指針不再表示對象在內存中的精確位置,而是表示 偏移量 。這意味著 32 位的指針可以引用 40 億個 對象 , 而不是 40 億個字節。最終, 也就是說堆內存增長到 32 GB 的物理內存,也可以用 32 位的指針表示。
一旦你越過那個神奇的 ~32 GB 的邊界,指針就會切回普通對象的指針。每個對象的指針都變長了,就會使用更多的 CPU 內存帶寬,也就是說你實際上失去了更多的內存。事實上,當內存到達 40–50 GB 的時候,有效內存才相當于使用內存對象指針壓縮技術時候的 32 GB 內存。
這段描述的意思就是說:即便你有足夠的內存,也盡量不要 超過 32 GB。因為它浪費了內存,降低了 CPU 的性能,還要讓 GC 應對大內存。
關掉swap
內存交換 到磁盤對服務器性能來說是 致命 的。
如果內存交換到磁盤上,一個 100 微秒的操作可能變成 10 毫秒。再想想那么多 10 微秒的操作時延累加起來。不難看出 swapping 對于性能是多么可怕。
用以下命令關掉swap:
sudo?swapoff?-a
不要碰以下的配置
所有的調整就是為了優化,但是這些調整,你真的不需要理會它。因為它們經常會被亂用,從而造成系統的不穩定或者糟糕的性能,甚至兩者都有可能。
線程池配置
許多人 喜歡 調整線程池。無論什么原因,人們都對增加線程數無法抵抗。索引太多了?增加線程!搜索太多了?增加線程!節點空閑率低于 95%?增加線程!
Elasticsearch 默認的線程設置已經是很合理的了。對于所有的線程池(除了 搜索 ),線程個數是根據 CPU 核心數設置的。如果你有 8 個核,你可以同時運行的只有 8 個線程,只分配 8 個線程給任何特定的線程池是有道理的。
搜索線程池設置的大一點,配置為 int(( 核心數 * 3 )/ 2 )+ 1 。
垃圾回收器
Elasticsearch 默認的垃圾回收器( GC )是 CMS。這個垃圾回收器可以和應用并行處理,以便它可以最小化停頓。然而,它有兩個 stop-the-world 階段,處理大內存也有點吃力。
盡管有這些缺點,它還是目前對于像 Elasticsearch 這樣低延遲需求軟件的最佳垃圾回收器。官方建議使用 CMS。
合理設置最小主節點
minimum_master_nodes 設置及其重要,為了防止集群腦裂,這個參數應該設置為法定個數就是 ( master 候選節點個數 / 2) + 1。
分片均勻,磁盤優化,剔除掉高負載的Master競選?
筆者在實際生產環境中遇到了有一個節點的負載是其他節點的幾倍,從虛擬機監控上看,所有的節點的qps是差不多的。機器的配置是一樣的,為什么負載會有如此大的差距?
首先,我們懷疑數據分配不均勻,我們排查了下,沒有這種現象。
然后,我們監控到了高負載的節點磁盤IO非常的高,經常達到100%,我們懷疑是那個虛擬機磁盤性能不行。但是我們當時沒有更好的磁盤。
我們找到了一個適中的解決辦法是將這臺高負載的節點剔除Master競選,即將elasticsearch.yml文件中的node.master改為false然后重啟,負載下降了一些。
數據存儲天數的優化
存儲天數的優化,這個需要根據實際的業務來,下面是刪除過期數據的腳本,該腳本來源于https://stackoverflow.com/questions/33430055/removing-old-indices-in-elasticsearch#answer-39746705 ;
#!/bin/bash searchIndex=logstash-monitor elastic_url=logging.core.k94.kvk.nl elastic_port=9200 date2stamp?()?{ ?date?--utc?--date?"$1"?+%s } dateDiff?(){ ?case?$1?in ?-s)?sec=1;?shift;; ?-m)?sec=60;?shift;; ?-h)?sec=3600;?shift;; ?-d)?sec=86400;?shift;; ?*)?sec=86400;; ?esac ?dte1=$(date2stamp?$1) ?dte2=$(date2stamp?$2) ?diffSec=$((dte2-dte1)) ?if?((diffSec?<?0));?then?abs=-1;?else?abs=1;?fi ?echo?$((diffSec/sec*abs)) } for?index?in?$(curl?-s?"${elastic_url}:${elastic_port}/_cat/indices?v"?|?grep?-E?"?${searchIndex}-20[0-9][0-9]\.[0-1][0-9]\.[0-3][0-9]"?|?awk?'{?print?$3?}');?do ?date=$(echo?${index:?-10}?|?sed?'s/\./-/g') ?cond=$(date?+%Y-%m-%d) ?diff=$(dateDiff?-d?$date?$cond) ?echo?-n?"${index}?(${diff})" ?if?[?$diff?-gt?1?];?then ?echo?"?/?DELETE" ?#?curl?-XDELETE?"${elastic_url}:${elastic_port}/${index}?pretty" ?else ?echo?"" ?fi done
然后使用crontab每天定時執行一次這個腳本。
集群分片設置
ES一旦創建好索引后,就無法調整分片的設置,而在ES中,一個分片實際上對應一個lucene 索引,而lucene索引的讀寫會占用很多的系統資源,因此,分片數不能設置過大;所以,在創建索引時,合理配置分片數是非常重要的。一般來說,我們遵循一些原則:
控制每個分片占用的硬盤容量不超過ES的最大JVM的堆空間設置(一般設置不超過32G,參加上文的JVM設置原則),因此,如果索引的總容量在500G左右,那分片大小在16個左右即可;當然,最好同時考慮原則2。
考慮一下node數量,一般一個節點有時候就是一臺物理機,如果分片數過多,大大超過了節點數,很可能會導致一個節點上存在多個分片,一旦該節點故障,即使保持了1個以上的副本,同樣有可能會導致數據丟失,集群無法恢復。所以, 一般都設置分片數不超過節點數的3倍。
索引優化
1.修改index_buffer_size 的設置,可以設置成百分數,也可設置成具體的大小,大小可根據集群的規模做不同的設置測試。
indices.memory.index_buffer_size:10%(默認) indices.memory.min_index_buffer_size:48mb(默認) indices.memory.max_index_buffer_size
_id字段的使用,應盡可能避免自定義_id, 以避免針對ID的版本管理;建議使用ES的默認ID生成策略或使用數字類型ID做為主鍵。
_all字段及_source字段的使用,應該注意場景和需要,_all字段包含了所有的索引字段,方便做全文檢索,如果無此需求,可以禁用;_source存儲了原始的document內容,如果沒有獲取原始文檔數據的需求,可通過設置includes、excludes 屬性來定義放入_source的字段。
合理的配置使用index屬性,analyzed 和not_analyzed,根據業務需求來控制字段是否分詞或不分詞。只有 groupby需求的字段,配置時就設置成not_analyzed, 以提高查詢或聚類的效率。
查詢優化
查詢優化,調整filter過濾順序
如果把過濾效果不明顯的條件放在了前面,導致查詢出大量不需要的數據,導致查詢變慢。
把過濾效果明顯的條件提前,按照過濾效果把過濾條件排序
索引時間精度優化
研究Filter的工作原理可以看出,它每次工作都是遍歷整個索引的,所以時間粒度越大,對比越快,搜索時間越短,在不影響功能的情況下,時間精度越低越好,有時甚至犧牲一點精度也值得,當然最好的情況是根本不作時間限制。
es重新刷索引,增加冗余的時間字段,精確到天。帶有時間范圍的查詢使用該字段進行查詢
查詢Fetch Source優化
業務查詢語句獲取的數據集比較大,并且從source中獲取了非必須的字段,導致查詢較慢。
舉例:只需要從es中查詢id這一個字段,卻把所有字段查詢了出來
預索引數據
利用索引查詢數據是最優的方式。例如,如果所有的文檔都有 price 字段,并且大多數查詢都在一個固定的范圍列表中運行范圍聚合,那么可以通過將 index 預索引到 index 和使用 terms 聚合來更快地實現聚合。
例如,像下面這樣:
PUT?index/type/1 { ?"designation":?"spoon", ?"price":?13 }
像這樣的查詢:
GET?index/_search { ?"aggs":?{ ?"price_ranges":?{ ?"range":?{ ?"field":?"price", ?"ranges":?[ ?{?"to":?10?}, ?{?"from":?10,?"to":?100?}, ?{?"from":?100?} ?] ?} ?} ?} }
文檔在索引的時候要使用 price_range ,應該被映射為關鍵詞:
PUT?index { ?"mappings":?{ ?"type":?{ ?"properties":?{ ?"price_range":?{ ?"type":?"keyword" ?} ?} ?} ?} } PUT?index/type/1 { ?"designation":?"spoon", ?"price":?13, ?"price_range":?"10-100" }
然后這個請求就直接聚合新字段,而不是在 price 字段運行范圍查詢:
GET?index/_search { ?"aggs":?{ ?"price_ranges":?{ ?"terms":?{ ?"field":?"price_range" ?} ?} ?} }
總結
總的來說,ElasticSearch的優化,優化可以從以下方面的考慮:
硬件的優化:機器分配,機器配置,機器內存,機器CPU,機器網絡,機器磁盤性能
操作系統設置優化:文件句柄優化、swap關閉
ElasticSearch合理分配節點,合理分配參加競選Master的節點
ElasticSearch的存儲的優化,副本數量、索引數量、分片數量
ElasticSearch的使用優化,索引的優化,查詢的優化
?最后,分享一份面試寶典《Java核心知識點整理.pdf》,覆蓋了JVM、鎖、高并發、反射、Spring原理、微服務、Zookeeper、數據庫、數據結構等等
有需要的朋友可以加入Java架構技術交流Q群328993819交流、探討、
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。