1. 程式人生 > >ElasticSearch 5.X 最佳實踐

ElasticSearch 5.X 最佳實踐

轉自大佬:https://www.jishux.com/p/2ccc199e939f5830

Author: 袁野
Date: 2018.01.05
Version: 1.0

注意事項:

  • 本文件所述為通用情況,不可作為特定業務參照;
  • 本文件所述適用於 ELK 棧中的 ElasticSearch 優化;
  • 本文件所述基於 elastic 公司的 5.2.1 版本;
  • 本文件只描述最佳實踐,不包含所需變更步驟;
  • 本文件針對 CentOS 7.2,其他 Unix 發行版不在討論範圍內;

硬體選擇

       目前公司的物理機機型在CPU和記憶體方面都滿足需求,建議使用SSD機型

。原因在於,可以快速把 Lucene 的索引檔案載入入記憶體(這在宕機恢復的情況下尤為明顯),減少 IO 負載和 IO wait以便CPU不總是在等待IO中斷。建議使用多裸盤而非raid,因為 ElasticSearch 本身就支援多目錄,raid 要麼犧牲空間要麼犧牲可用性。

系統配置

       ElasticSearch 理論上必須單獨部署,並且會獨佔幾乎所有系統資源,因此需要對系統進行配置,以保證執行 ElasticSearch 的使用者可以使用足夠多的資源。生產叢集需要調整的配置如下:

  • 設定 JVM 堆大小;
  • 關閉 swap;
  • 增加檔案描述符;
  • 保證足夠的虛存;
  • 保證足夠的執行緒;
  • 暫時不建議使用G1GC;

設定 JVM 堆大小

       ElasticSearch 需要有足夠的 JVM 堆支撐索引資料的載入,對於公司的機型來說,因為都是大於 128GB 的,所以推薦的配置是 32GB(如果 JVM 以不等的初始和最大堆大小啟動,則在系統使用過程中可能會因為 JVM 堆的大小調整而容易中斷。 為了避免這些調整大小的暫停,最好使用初始堆大小等於最大堆大小的 JVM 來啟動),預留足夠的 IO Cache 給 Lucene(官方建議超過一半的記憶體需要預留

)。

       設定方法(需要重啟程序):

# Step1. 修改 ${PATH_TO_ElasticSearch_HOME}/config/jvm.options 中的 Xms 和 Xmx
-Xms32g
-Xmx32g
# Step2. 重啟 elasticsearch
sudo sytemctl restart elasticsearch

關閉 swap & 禁用交換

        必須要關閉 swap,因為在實體記憶體不足時,如果發生 FGC,在回收虛擬記憶體的時候會造成長時間的 stop-the-world,最嚴重的後果是造成叢集雪崩。公司的預設模板是關閉的,但是要巡檢一遍,避免有些機器存在問題。

設定方法:

# Step1. root 使用者臨時關閉
sudo swapoff -a
sudo sysctl vm.swappiness=0
# Step2. 修改 /etc/fstab,註釋掉 swap 這行
# Step3. 修改 /etc/sysctl.conf,新增:
vm.swappiness = 0
# Step4. 確認是否生效
sudo sysctl vm.swappiness

        也可以通過修改 yml 配置檔案的方式從 ElasticSearch 層面禁止實體記憶體和交換區之間交換記憶體:

Linux 把它的物理 RAM 分成多個記憶體塊,稱之為分頁。記憶體交換(swapping)是這樣一個過程,它把記憶體分頁複製到預先設定的叫做交換區的硬碟空間上,以此釋放記憶體分頁。實體記憶體和交換區加起來的大小就是虛擬記憶體的可用額度。

記憶體交換有個缺點,跟記憶體比起來硬碟非常慢。記憶體的讀寫速度以納秒來計算,而硬碟是以毫秒來計算,所以訪問硬碟比訪問記憶體要慢幾萬倍。交換次數越多,程序就越慢,所以應該不惜一切代價避免記憶體交換的發生。

ElasticSearch 的 memory_lock 屬性允許 Elasticsearch 節點不交換記憶體。(注意只有Linux/Unix系統可設定。)這個屬性可以在yml檔案中設定。

# Step1. 修改 ${PATH_TO_ES_HOME}/config/elasticsearch.yml,新增:
bootstrap.memory_lock: true

增加檔案描述符

        單個使用者可用的最大程序數量(軟限制)&單個使用者可用的最大程序數量(硬限制),超過軟限制會有警告,但是無法超過硬限制。 ElasticSearch 會使用大量的檔案控制代碼,如果超過限制可能會造成宕機或者資料缺失。

檔案描述符是用於跟蹤開啟“檔案”的 Unix 結構體。在Unix中,一切都皆檔案。 例如,“檔案”可以是物理檔案,虛擬檔案(例如/proc/loadavg)或網路套接字。 ElasticSearch 需要大量的檔案描述符(例如,每個 shard 由多個 segment 和其他檔案組成,以及到其他節點的 socket 連線等)。

設定方法(假設是 admin 使用者啟動的 ElasticSearch 程序):

# Step1. 修改 /etc/security/limits.conf,新增:
admin soft nofile 65536
admin hard nofile 65536
# Step2. 確認是否生效
su - admin
ulimit -n
# Step3. 通過 rest 確認是否生效
GET /_nodes/stats/process?filter_path=**.max_file_descriptors

保證足夠的虛存

         單程序最多可以佔用的記憶體區域,預設為 65536。Elasticsearch 預設會使用 mmapfs 去儲存 indices,預設的 65536 過少,會造成 OOM 異常。

設定方法:

# Step1. root 使用者修改臨時引數
sudo sysctl -w vm.max_map_count=262144
# Step2. 修改 /etc/sysctl.conf,在文末新增:
vm.max_map_count = 262144
# Step3. 確認是否生效
sudo sysctl vm.max_map_count

保證足夠的執行緒

         Elasticsearch 通過將請求分成幾個階段,並交給不同的執行緒池執行(Elasticsearch 中有各種不同的執行緒池執行器)。 因此,Elasticsearch 需要建立大量執行緒的能力。程序可建立執行緒的最大數量確保 Elasticsearch 程序有權在正常使用情況下建立足夠的執行緒。 這可以通過 /etc/security/limits.conf 使用 nproc 設定來完成。

設定方法(假設是 admin 使用者啟動的 Elasticsearch 程序):

# Step1. 修改 /etc/security/limits.d/90-nproc.conf,新增:
admin soft nproc 2048

暫時不建議使用G1GC

       已知 JDK 8 附帶的 HotSpot JVM 的早期版本在啟用 G1GC 收集器時會導致索引損壞。受影響的版本是早於 JDK 8u40 附帶的HotSpot 的版本,出於穩定性的考慮暫時不建議使用。

記憶體優化

       ElasticSearch 自身對記憶體管理進行了大量優化,但對於持續增長的業務仍需進行一定程度的記憶體優化(而不是純粹的新增節點和擴充套件實體記憶體),以防止 OOM 發生。ElasticSearch 使用的 JVM 堆中主要包括以下幾類記憶體使用:

  • Segment Memory;
  • Filter Cache;
  • Field Data Cache;
  • Bulk Queue;
  • Indexing Buffer;
  • Cluster State Buffer;
  • 超大搜索聚合結果集的 fetch;

     詳細資料可以參閱我寫的這篇博文

減少 Segment Memory

      刪除辦法,使用 rest API:

# 刪除指定某個索引
DELETE /${INDEX_NAME}
# 刪除符合 pattern 的某些索引
DELETE /${INDEX_PATTERN}
  • 關閉無需實時查詢的歷史索引,檔案仍然存在於磁碟,只是釋放掉記憶體,需要的時候可以重新開啟

      關閉辦法,使用 rest API:

# 關閉指定某個索引
POST /${INDEX_NAME}/_close
# 關閉符合 pattern 的某些索引
POST /${INDEX_PATTERN}/_close
  • 定期對不再更新的索引做 force merge(會佔用大量 IO,建議業務低峰期觸發

       force merge 辦法,使用 rest API:

# Step1. 在合併前需要對合並速度進行合理限制,預設是 20mb,SSD可以適當放寬到 80mb:
PUT /_cluster/settings -d '
{
    "persistent" : {
        "indices.store.throttle.max_bytes_per_sec" : "20mb"
    }
}'

# Step2. 強制合併 API,示例表示的是最終合併為一個 segment file:
# 對某個索引做合併
POST /${INDEX_NAME}/_forcemerge?max_num_segments=1
# 對某些索引做合併
POST /${INDEX_PATTERN}/_forcemerge?max_num_segments=1

Filter Cache

        預設的 10% heap 設定工作得夠好,如果實際使用中 heap 沒什麼壓力的情況下,才考慮加大這個設定。

Field Data Cache

         對需要排序的欄位不進行 analyzed,儘量使用 doc values(5.X版本天然支援,不需要特別設定)。對於不參與搜尋的欄位 ( fields ),將其 index 方法設定為 no,如果對分詞沒有需求,對參與搜尋的欄位,其 index 方法設定為 not_analyzed

Bulk Queue

        一般來說官方預設的 thread pool 設定已經能很好的工作了,建議不要隨意去調優相關的設定,很多時候都是適得其反的效果。

Indexing Buffer

      這個引數的預設值是10% heap size。根據經驗,這個預設值也能夠很好的工作,應對很大的索引吞吐量。 但有些使用者認為這個 buffer 越大吞吐量越高,因此見過有使用者將其設定為 40% 的。到了極端的情況,寫入速度很高的時候,40%都被佔用,導致OOM。

Cluster State Buffer

       在超大規模叢集的情況下,可以考慮分叢集並通過 tribe node 連線做到對使用者透明,這樣可以保證每個叢集裡的 state 資訊不會膨脹得過大。在單叢集情況下,縮減 cluster state buffer 的方法就是減少 shard 數量,shard 數量的確定有以下幾條規則:

  • 避免有非常大的分片,因為大的分片可能會對叢集從故障中恢復的能力產生負面影響。 對於多大的分片沒有固定的限制,但是分片大小為 50GB 通常被界定為適用於各種用例的限制;
  • 儘可能使用基於時間的索引來管理資料。根據保留期(retention period,可以理解成有效期)將資料分組。基於時間的索引還可以輕鬆地隨時間改變主分片和副本分片的數量(以為要生成的下一個索引進行更改)。這簡化了適應不斷變化的資料量和需求;(週期性的通過刪除或者關閉歷史索引以減少分片
  • 小分片會導致小分段(segment),從而增加開銷。目的是保持平均分片大小在幾GB和幾十GB之間。對於具有基於時間的資料的用例,通常看到大小在 20GB 和 40GB 之間的分片;
  • 由於每個分片的開銷取決於分段數和大小,通過強制操作迫使較小的段合併成較大的段可以減少開銷並提高查詢效能。一旦沒有更多的資料被寫入索引,這應該是理想的。請注意,這是一個消耗資源的(昂貴的)操作,較為理想的處理時段應該在非高峰時段執行;(對應使用 force meger 以減少 segment 數量的優化,目的是降低 segment memory 佔用
  • 可以在叢集節點上儲存的分片數量與可用的堆記憶體大小成正比,但這在 Elasticsearch 中沒有的固定限制。 一個很好的經驗法則是:確保每個節點的分片數量保持在低於每 1GB 堆記憶體對應叢集的分片在 20-25 之間。 因此,具有 32GB 堆記憶體的節點最多可以有 600-750 個分片;
  • 對於單索引的主分片數,有這麼 2 個公式:節點數 <= 主分片數 *(副本數 + 1) 以及 (同一索引 shard 數量 * (1 + 副本數)) < 3 * 資料節點數,比如有 3 個節點全是資料節點,1 個副本,那麼主分片數大於等於 1.5,同時同一索引總分片數需要小於 4.5,因為副本數為 1,所以單節點主分片最適為 2,索引總分片數最適為 6,這樣每個節點的總分片為 4;
  • 單分片小於 20GB 的情況下,採用單分片較為合適,請求不存在網路抖動的顧慮;

      小結:分片不超 20GB,且單節點總分片不超 600。比如網際網路區域,每天新建索引(lw-greenbay-online) 1 個分片 1 個副本,3 個月前的歷史索引都關閉,3 節點總共需要扛 90 * 2 = 180 個分片,每個分片大約 6 GB,可謂比較健康的狀態。

超大搜索聚合結果集的 fetch

      避免使用者 fetch 超大搜索聚合結果集,確實需要大量拉取資料可以採用 scan & scroll API 來實現。在 ElasticSearch 上搜索資料時,預設只會返回10條文件,當我們想獲取更多結果,或者只要結果中的一個區間的資料時,可以通過 size 和 from 來指定。

GET /_search?size=3&from=20

     如上的查詢語句,會返回排序後的結果中第 20 到第 22 條資料。ElasticSearch 在收到這樣的一個請求之後,每一個分片都會返回一個 top22 的搜尋結果,然後將這些結果彙總排序,再選出 top22 ,最後取第 20 到第 22 條資料作為結果返回。
這樣會帶來一個問題,當我們搜尋的時候,如果想取出第 10001 條資料,那麼就相當於每個一分片都要對資料進行排序,取出前 10001 條文件,然後 ElasticSearch 再將這些結果彙總再次排序,之後取出第 10001 條資料。這樣對於 ElasticSearch 來說就會產生相當大的資源和效能開銷。如果我們不要求 ElasticSearch 對結果進行排序,那麼就會消耗很少的資源,所以針對此種情況,ElasticSearch 提供了scan & scroll的搜尋方式。

GET /old_index/_search?search_type=scan&scroll=1m 
{
    "query": { "match_all": {}},
    "size":  1000
}

      我們可以首先通過如上的請求發起一個搜尋,但是這個請求不會返回任何文件,它會返回一個 _scroll_id ,接下來我們再通過這個 id 來從 ElasticSearch 中讀取資料:

GET /_search/scroll?scroll=1m 
c2Nhbjs1OzExODpRNV9aY1VyUVM4U0NMd2pjWlJ3YWlBOzExOTpRNV9aY1VyUVM4U0 NMd2pjWlJ3YWlBOzExNjpRNV9aY1VyUVM4U0NMd2pjWlJ3YWlBOzExNzpRNV9aY1VyUVM4U0NMd2pjWlJ3YWlBOzEyMDpRNV9aY1VyUVM4U0NMd2pjWlJ3YWlBOzE7dG90YWxfaGl0czoxOw==

      此時除了會返回搜尋結果以外,還會再次返回一個 _scroll_id,當我們下次繼續取資料時,需要用最新的 id。

儲存優化

關閉不需要的功能

       預設情況下 ElasticSearch 並將會將 indexs 和 doc values 新增到大多數字段中,以便可以搜尋和聚合它們。 例如,如果有一個名為 foo 的數字欄位,需要執行 histograms 但不需要 filter,則可以安全地禁用對映中此欄位的索引:

PUT ${INDEX_NAME}
{
  "mappings": {
    "type": {
      "properties": {
        "foo": {
          "type": "integer",
          "index": false
        }
      }
    }
  }
}

       text 欄位在索引中儲存規範化因子以便能夠對文件進行評分。 如果只需要在 text 欄位上使用 matching 功能,但不關心生成的 score,則可以命令 ElasticSearch 配置為不將規範寫入索引:

PUT ${INDEX_NAME}
{
  "mappings": {
    "type": {
      "properties": {
        "foo": {
          "type": "text",
          "norms": false
        }
      }
    }
  }
}

        text 欄位也預設儲存索引中的頻率和位置。 頻率用於計算分數,位置用於執行短語查詢(phrase queries)。 如果不需要執行短語查詢,可以告訴 ElasticSearch 不要索引位置:

PUT ${INDEX_NAME}
{
  "mappings": {
    "type": {
      "properties": {
        "foo": {
          "type": "text",
          "index_options": "freqs"
        }
      }
    }
  }
}

            此外,如果不關心計分,則可以配置 ElasticSearch 以僅索引每個 term 的匹配文件。 這樣做仍然可以在此欄位上進行搜尋(search),但是短語查詢會引發錯誤,評分將假定 term 在每個文件中只出現一次。

PUT ${INDEX_NAME}
{
  "mappings": {
    "type": {
      "properties": {
        "foo": {
          "type": "text",
          "norms": false,
          "index_options": "freqs"
        }
      }
    }
  }
}

強制清除已標記刪除的資料

         Elasticsearch 是建立在 Apache Lucene 基礎上的實時分散式搜尋引擎,Lucene 為了提高搜尋的實時性,採用不可再修改(immutable)方式將文件儲存在一個個 segment 中。也就是說,一個 segment 在寫入到儲存系統之後,將不可以再修改。那麼 Lucene 是如何從一個 segment 中刪除一個被索引的文件呢?簡單的講,當用戶發出命令刪除一個被索引的文件#ABC 時,該文件並不會被馬上從相應的儲存它的 segment 中刪除掉,而是通過一個特殊的檔案來標記該文件已被刪除。當用戶再次搜尋到 #ABC 時,Elasticsearch 在 segment 中仍能找到 #ABC,但由於 #ABC 文件已經被標記為刪除,所以Lucene 會從發回給使用者的搜尋結果中剔除 #ABC,所以給使用者感覺的是 #ABC 已經被刪除了。

       Elasticseach 會有後臺執行緒根據 Lucene 的合併規則定期進行 segment merging 合併操作,一般不需要使用者擔心或者採取任何行動。被刪除的文件在 segment 合併時,才會被真正刪除掉。在此之前,它仍然會佔用著 JVM heap 和作業系統的檔案 cache 等資源。在某些情況下,我們需要強制 Elasticsearch 進行 segment merging,已釋放其佔用的大量系統資源。

POST /${INDEX_NAME}/_forcemerge?max_num_segments=1&only_expunge_deletes=true&wait_for_completion=true
POST /${INDEX_PATTERN}/_forcemerge?max_num_segments=1&only_expunge_deletes=true&wait_for_completion=true

         Force Merge 命令可強制進行 segment 合併,並刪除所有標記為刪除的文件。Segment merging 要消耗 CPU,以及大量的 I/O 資源,所以一定要在 ElasticSearch 叢集處於維護視窗期間,並且有足夠的 I/O 空間的(如:SSD)的條件下進行;否則很可能造成叢集崩潰和資料丟失。

減少副本數

        最直接的儲存優化手段是調整副本數,預設 ElasticSearch 是有 1 個副本的,假設對可用性要求不高,允許磁碟損壞情況下可能的資料缺失,可以把副本數調整為 0,具體操作如下:

PUT  /_template/${TEMPLATE_NAME}
{

  "template":"${TEMPLATE_PATTERN}",
  "settings" : {
    "number_of_replicas" : 0
  },
  "version"  : 1
}

        其中 ${TEMPLATE_NAME} 表示模板名稱,可以是不存在的,系統會新建。${TEMPLATE_PATTERN} 是用於匹配索引的表示式,比如 lw-greenbay-online-*。

         與此相關的一個系統引數為:index.merge.scheduler.max_thread_count,預設值為 Math.max(1, Math.min(4, Runtime.getRuntime().availableProcessors() / 2)),這個值在 SSD 上工作沒問題,但是 SATA 盤上還是使用 1 個執行緒為好,因為太多也來不及完成。

# SATA 請設定 merge 執行緒為 1
PUT  /_template/${TEMPLATE_NAME}
{

  "template":"${TEMPLATE_PATTERN}",
  "settings" : {
    "index.merge.scheduler.max_thread_count": 1
  },
  "version"  : 1
}

請勿使用預設的動態字串對映

          預設的動態字串對映會將字串欄位索引為文字(text)和關鍵字(keyword)。 如果只需要其中的一個,這樣做無疑是浪費的。 通常情況下,一個 id 欄位只需要被索引為一個 keyword,而一個 body 欄位只需要被索引為一個 text 欄位。

         可以通過在字串欄位上配置顯式對映或設定將字串欄位對映為文字(text)或關鍵字(keyword)的動態模板來禁用此功能。

         例如下面的模板,可以用來將 strings 欄位對映為關鍵字:

PUT ${INDEX_NAME}
{
  "mappings": {
    "type": {
      "dynamic_templates": [
        {
          "strings": {
            "match_mapping_type": "string",
            "mapping": {
              "type": "keyword"
            }
          }
        }
      ]
    }
  }
}

禁用 _all 欄位

         _all 欄位是由所有欄位拼接成的超級欄位,如果在查詢中已知需要查詢的欄位,就可以考慮禁用它。

PUT /_template/${TEMPLATE_NAME}
{
  "template": "${TEMPLATE_PATTERN}",
  "settings" : {...},
  "mappings": {
    "type_1": {
      "_all": {
         "enabled": false
       },
      "properties": {...}
   }
  },
  "version"  : 1
}

使用 best_compression

        _source 欄位和 stored fields 會佔用大量儲存,可以考慮使用 best_compression 進行壓縮。預設的壓縮方式為 LZ4,但需要更高壓縮比的話,可以通過 inex.codec 進行設定,修改為 DEFLATE,在 force merge 後生效:

# Step1. 修改壓縮演算法為 best_compression
PUT  /_template/${TEMPLATE_NAME}
{

  "template":"${TEMPLATE_PATTERN}",
  "settings" : {
    "index.codec" : "best_compression"
  },
  "version"  : 1
}
# Step2. force merge
POST /${INDEX_NAME}/_forcemerge?max_num_segments=1&wait_for_completion=true
POST /${INDEX_PATTERN}/_forcemerge?max_num_segments=1&wait_for_completion=true

使用最優資料格式

          我們為數字資料選擇的型別可能會對磁碟使用量產生重大影響。 首先,應使用整數型別(byte,short,integer或long)來儲存整數,浮點數應該儲存在 scaled_float 中,或者儲存在適合用例的最小型別中:使用 float 而不是 double,使用 half_float 而不是 float。

PUT /_template/${TEMPLATE_NAME}
{
  "template": "${TEMPLATE_PATTERN}",
  "settings" : {...},
  "mappings": {
    "type_1": {
      "${FIELD_NAME}": {
         "type": "integer"
       },
      "properties": {...}
   }
  },
  "version"  : 1
}

搜尋速度優化

避免Join和Parent-Child

         Join會使查詢慢數倍、 Parent-Child會使查詢慢數百倍,請在進行 query 語句編寫的時候儘量避免。

對映

        某些資料本身是數字,但並不意味著它應該總是被對映為一個數字欄位。 通常儲存著識別符號的欄位(如ISBN)或來自另一個數據庫的數字型記錄,可能對映為 keyword 而不是 integer 或者 long 會更好些。

避免使用 Scripts

           之前 Groovy 指令碼曝出了很大的漏洞,總的來說是需要避免使用的。如果必須要使用,儘量用 5.X 以上版本自帶的 painless 和 expressions 引擎。

根據四捨五入的日期進行查詢

          根據 timestamp 欄位進行的查詢通常不可快取,因為匹配的範圍始終在變化。 但就使用者體驗而言,以四捨五入對日期進行轉換通常是可接受的,這樣可以有效利用系統快取。

舉例說明,有以下查詢:

PUT index/type/1
{
  "my_date": "2016-05-11T16:30:55.328Z"
}

GET index/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "my_date": {
            "gte": "now-1h",
            "lte": "now"
          }
        }
      }
    }
  }
}

可以對時間範圍進行替換:

GET index/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "my_date": {
            "gte": "now-1h/m",
            "lte": "now/m"
          }
        }
      }
    }
  }
}

        在這種情況下,我們四捨五入到分鐘,所以如果當前時間是 16:31:29 ,範圍查詢將匹配 my_date 欄位的值在 15:31:00 和16:31:59 之間的所有內容。 如果多個使用者在同一分鐘內執行包含這個範圍的查詢,查詢快取可以幫助加快速度。 用於四捨五入的時間間隔越長,查詢快取可以提供的幫助就越多,但要注意過於積極的舍入也可能會傷害使用者體驗。

         為了能夠利用查詢快取,建議將範圍分割成大的可快取部分和更小的不可快取的部分,如下所示:

GET index/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "bool": {
          "should": [
            {
              "range": {
                "my_date": {
                  "gte": "now-1h",
                  "lte": "now-1h/m"
                }
              }
            },
            {
              "range": {
                "my_date": {
                  "gt": "now-1h/m",
                  "lt": "now/m"
                }
              }
            },
            {
              "range": {
                "my_date": {
                  "gte": "now/m",
                  "lte": "now"
                }
              }
            }
          ]
        }
      }
    }
  }
}

          然而,這種做法可能會使查詢在某些情況下執行速度較慢,因為由 bool 查詢引入的開銷可能會因更好地利用查詢快取而失敗。

對只讀 indices 進行 force merge

        建議將只讀索引被合併到一個單獨的分段中。 基於時間的索引通常就是這種情況:只有當前時間索引會寫入資料,而舊索引是隻讀索引。

預熱 global ordinals

        全域性序號(global ordinals)是用於在關鍵字(keyword)欄位上執行 terms aggregations 的資料結構。 由於 ElasticSearch 不知道聚合使用哪些欄位、哪些欄位不使用,所以它們在記憶體中被載入得很慢。 我們可以通過下面的 API 來告訴 ElasticSearch 通過配置對映來在 refresh 的時候載入全域性序號:

PUT index
{
  "mappings": {
    "type": {
      "properties": {
        "foo": {
          "type": "keyword",
          "eager_global_ordinals": true
        }
      }
    }
  }
}

寫入效能優化

          之前描述了 ElasticSearch 在記憶體管理方面的優化,接下來梳理下如何對寫入效能進行優化,寫入效能的優化也和 HBase 類似,無非就是增加吞吐,而增加吞吐的方法就是增大刷寫間隔、合理設定執行緒數量、開啟非同步刷寫(允許資料丟失的情況下)。

增大刷寫間隔

        通過修改主配置檔案 elasticsearch.yml 或者 Rest API 都可以對 index.refresh_interval進行修改,增大該屬性可以提升寫入吞吐。

PUT  /_template/{TEMPLATE_NAME}
{
  "template":"{INDEX_PATTERN}",
  "settings" : {
    "index.refresh_interval" : "30s"
  }
}
PUT {INDEX_PAATERN}/_settings
{
    "index.refresh_interval" : "30s"
}

合理設定執行緒數量

       調整 elasticsearch.yml ,對 bulk/flush 執行緒池進行調優,根據本機實際配置:

threadpool.bulk.type:fixed
threadpool.bulk.size:8 #(CPU核數)
threadpool.flush.type:fixed
threadpool.flush.size:8 #(CPU核數)

開啟非同步刷寫

       如果允許資料丟失,可以對特定 index 開啟非同步刷寫:

PUT  /_template/{TEMPLATE_NAME}
{
  "template":"{INDEX_PATTERN}",
  "settings" : {
    "index.translog.durability": "async"
  }
}

PUT  {INDEX_PAATERN}/_settings
{
  "index.translog.durability": "async"
}

審計優化

開啟慢查詢日誌

     不論是資料庫還是搜尋引擎,對於問題的排查,開啟慢查詢日誌是十分必要的,ElasticSearch 開啟慢查詢的方式有多種,但是最常用的是呼叫模板 API 進行全域性設定:

PUT  /_template/{TEMPLATE_NAME}
{

  "template":"{INDEX_PATTERN}",
  "settings" : {
    "index.indexing.slowlog.level": "INFO",
    "index.indexing.slowlog.threshold.index.warn": "10s",
    "index.indexing.slowlog.threshold.index.info": "5s",
    "index.indexing.slowlog.threshold.index.debug": "2s",
    "index.indexing.slowlog.threshold.index.trace": "500ms",
    "index.indexing.slowlog.source": "1000",
    "index.search.slowlog.level": "INFO",
    "index.search.slowlog.threshold.query.warn": "10s",
    "index.search.slowlog.threshold.query.info": "5s",
    "index.search.slowlog.threshold.query.debug": "2s",
    "index.search.slowlog.threshold.query.trace": "500ms",
    "index.search.slowlog.threshold.fetch.warn": "1s",
    "index.search.slowlog.threshold.fetch.info": "800ms",
    "index.search.slowlog.threshold.fetch.debug": "500ms",
    "index.search.slowlog.threshold.fetch.trace": "200ms"
  },
  "version"  : 1
}

對於已經存在的 index 使用 settings API:

PUT {INDEX_PAATERN}/_settings
{
    "index.indexing.slowlog.level": "INFO",
    "index.indexing.slowlog.threshold.index.warn": "10s",
    "index.indexing.slowlog.threshold.index.info": "5s",
    "index.indexing.slowlog.threshold.index.debug": "2s",
    "index.indexing.slowlog.threshold.index.trace": "500ms",
    "index.indexing.slowlog.source": "1000",
    "index.search.slowlog.level": "INFO",
    "index.search.slowlog.threshold.query.warn": "10s",
    "index.search.slowlog.threshold.query.info": "5s",
    "index.search.slowlog.threshold.query.debug": "2s",
    "index.search.slowlog.threshold.query.trace": "500ms",
    "index.search.slowlog.threshold.fetch.warn": "1s",
    "index.search.slowlog.threshold.fetch.info": "800ms",
    "index.search.slowlog.threshold.fetch.debug": "500ms",
    "index.search.slowlog.threshold.fetch.trace": "200ms"
}

這樣,在日誌目錄下的慢查詢日誌就會有輸出記錄必要的資訊了。