優化Refresh時間
ES在寫入數據的時候,采用延遲寫入的策略,數據會先寫到內存中,當超過默認時間1秒 (index.refresh_interval,默認)會進行一次寫入磁盤操作。如果對實時搜索要求不高的情況下,可以適當地將此值設置的高點,可以有效地減少 segment (一個segment是一個完備的lucene倒排索引,而倒排索引是通過詞典(Term Dictionary)到文檔列表(Postings List)的映射關系,快速做查詢的)合並壓力。通過如下方式設置:
1、創建索引時指定

2、對已有的索引進行設置

查看索引

合理設置副本數量
過多的副本數量會嚴重影響寫索引的效率,因為在寫索引時,會把寫入的數據同步到所有的副本節點上,如果副本數多那么寫的效率肯定會降低。一般在初始大量寫入ES操作時,應該先將副本數設置為0,等數據寫入完畢后再設置副本正常值。
設置副本數量,創建時:

對於現有索引設置副本數量:

查看索引情況:

內存設置
ES默認安裝后設置的內存是 1GB,對於任何一個現實業務來說,這個設置都太小了。 如果是通過解壓安裝的 ES,則在 ES 安裝文件中包含一個 jvm.option 文件,添加如下命令來設置 ES 的堆大小,Xms 表示堆的初始大小,Xmx 表示可分配的最大內存,都是 1GB。
%es_home%\config\jvm.options該文件中都是對jvm相關的配置,包括OOM及垃圾回收的配置

確保Xmx和Xms的大小是相同的,其目的是為了能夠在 Java 垃圾回收機制清理完堆區后不需要重新分隔計算堆區的大小而浪費資源,可以減輕伸縮堆大小帶來的壓力。 假設你有一個 64G 內存的機器,按照正常思維思考,你可能會認為把 64G 內存都給 ES 比較好,但現實是這樣嗎, 越大越好?雖然內存對 ES 來說是非常重要的,但是答案是否定的。
因為 ES堆內存的分配需要滿足以下兩個原則:
1、不要超過物理內存的 50%:Lucene 的設計目的是把底層 OS 里的數據緩存到內存中。 Lucene 的段是分別存儲到單個文件中的,這些文件都是不會變化的,所以很利於緩存,同時操作系統也會把這些段文件緩存起來,以便更快的訪問。 如果我們設置的堆內存過大,Lucene 可用的內存將會減少,就會嚴重影響降低 Lucene 的全文本查詢性能。
2、堆內存的大小最好不要超過 32GB:在 Java 中,所有對象都分配在堆上,然后有一個 Klass Pointer 指針指向它的類元數據。 這個指針在 64 位的操作系統上為 64 位,64 位的操作系統可以使用更多的內存(2^64)。在 32 位的系統上為 32 位,32 位的操作系統的最大尋址空間為 4GB(2^32)。 但是 64 位的指針意味着更大的浪費,因為你的指針本身大了。浪費內存不算,更糟糕的是,更大的指針在主內存和緩存器(例如 LLC, L1等)之間移動數據的時候,會占用更多的帶寬。 最終我們都會采用 31 G 設置 -Xms 31g -Xmx 31g 假設你有個機器有 128 GB 的內存,你可以創建兩個節點,每個節點內存分配不超過 32 GB。 也就是說不超過 64 GB 內存給 ES 的堆內存,剩下的超過 64 GB 的內存給 Lucene 。
批量操作
當你是大量的寫入數據時,建議使用Bulk API進行批量操作。Bulk默認設置批量提交不能超過100M。
請求格式:
{"action": {}} {data} // action 取值: create:文檔不存在時創建 update:更新文檔 index:創建新文檔,或者替換已經有的文檔 delete:刪除一個文檔


注意Content-Type:application/x-ndjson
每一行JSON結束必須有換行符\n(回車)。注意第七行也必須有個空行,不然報錯。
批量查看文檔

存儲設備優化
ES是高頻使用硬盤的應用,在segment合並操作時會頻繁操作硬盤,對硬盤要求較高,如果硬盤速度越高,那么es的性能肯定就會高。
Mapping建模
- 盡量避免使用nested或parent/child,能不用就不用;nested 查詢相對比較慢,parent/child query更慢,比nested query慢上百倍;因此能在mapping設計階段搞定的(大寬表設計或采用比較smart的數據結構),就不要用父子關系的mapping。
嵌套查詢的使用:


2. 如果一定要使用nested fields,保證nested fields字段不能過多,目前ES默認限制是50。參考:
index.mapping.nested_fields.limit :50。
創建索引時設置:

因為針對1個document,每一個nested field,都會生成一個獨立的document,這將使Doc數量劇增,影響查詢效率,尤其是JOIN的效率。
3. 避免使用動態值作為字段(key),動態遞增的mapping,會導致集群崩潰;同樣,也需要控制字段的數量,業務中不使用的字段,就不要索引。控制索引的字段數量、mapping深度、索引字段的類型,對於ES的性能優化是重中之重。以下是ES關於字段數、mapping深度的一些默認設置:
index.mapping.nested_objects.limit :10000
index.mapping.total_fields.limit:1000
index.mapping.depth.limit: 20
查詢優化
1、query_string 或 multi_match的查詢字段越多, 查詢越慢。可以在mapping階段,利用copy_to屬性將多字段的值索引到一個新字段,multi_match時,用新的字段查詢。
2、日期字段的查詢, 尤其是用now的查詢實際上是不存在緩存的,因此, 可以從業務的角度來考慮是否一定要用now,畢竟利用query cache是能夠大大提高查詢效率的。
3、查詢結果集的大小不能隨意設置成大得離譜的值, 如query.setSize(size)不能設置成Integer.MAX_VALUE, 因為ES內部需要建立一個數據結構來放指定大小的結果集數據。
4、避免層級過深的聚合查詢, 層級過深的group by , 會導致內存、CPU消耗,建議在服務層通過程序來組裝業務,也可以通過pipeline的方式來優化。