Elasticsearch 5.6 原理和使用總結

擊水三千里發表於2019-02-19

一、倒排索引

ElasticSearch引擎把文件資料寫入到倒排索引(Inverted Index)的資料結構中,倒排索引建立的是分詞(Term)和文件(Document)之間的對映關係,在倒排索引中,資料是面向詞(Term)而不是面向文件的。

舉個例子,文件和詞條之間的關係如下圖:

欄位值被分析之後,儲存在倒排索引中,倒排索引儲存的是分詞(Term)和文件(Doc)之間的關係,簡化版的倒排索引如下圖:

從圖中可以看出,倒排索引有一個詞條的列表,每個分詞在列表中是唯一的,記錄著詞條出現的次數,以及包含詞條的文件。實際上,ElasticSearch引擎建立的倒排索引比這個複雜得多。

 

倒排索引(Inverted Index):倒排索引是實現“單詞-文件矩陣”的一種具體儲存形式,通過倒排索引,可以根據單詞快速獲取包含這個單詞的文件列表。倒排索引主要由兩個部分組成:“單詞詞典”和“倒排檔案”。

單詞詞典(Lexicon):搜尋引擎的通常索引單位是單詞,單詞詞典是由文件集合中出現過的所有單詞構成的字串集合,單詞詞典內每條索引項記載單詞本身的一些資訊以及指向“倒排列表”的指標。

倒排列表(PostingList):倒排列表記載了出現過某個單詞的所有文件的文件列表及單詞在該文件中出現的位置資訊,每條記錄稱為一個倒排項(Posting)。根據倒排列表,即可獲知哪些文件包含某個單詞。

倒排檔案(Inverted File):所有單詞的倒排列表往往順序地儲存在磁碟的某個檔案裡,這個檔案即被稱之為倒排檔案,倒排檔案是儲存倒排索引的物理檔案。
 

關於這些概念之間的關係,通過下圖可以比較清晰的看出來。

 

       單詞詞典是倒排索引中非常重要的組成部分,它用來維護文件集合中出現過的所有單詞的相關資訊,同時用來記載某個單詞對應的倒排列表在倒排檔案中的位置資訊。在支援搜尋時,根據使用者的查詢詞,去單詞詞典裡查詢,就能夠獲得相應的倒排列表,並以此作為後續排序的基礎。 

       對於一個規模很大的文件集合來說,可能包含幾十萬甚至上百萬的不同單詞,能否快速定位某個單詞,這直接影響搜尋時的響應速度,所以需要高效的資料結構來對單詞詞典進行構建和查詢,常用的資料結構包括雜湊加連結串列結構B+樹形詞典結構

 

二、寫入效能分析

    當資料被寫入分片時,它會定期釋出到磁碟上的不可變的 Lucene 分段中用於查詢。隨著分段數量的增長,這些分段會定期合併為更大的分段。 此過程稱為合併。 由於所有分段都是不可變的,這意味著所使用的磁碟空間通常會在索引期間波動,因為需要在刪除替換分段之前建立新的合併分段。 合併可能非常耗費資源,特別是在磁碟I / O方面。

 分片的大小如何影響效能?

在Elasticsearch中,每個查詢在每個分片的單個執行緒中執行。然而,可以並行處理多個分片,並可以在相同分片上執行多個查詢和聚合。

【小分片的利弊】這意味著,在不涉及快取記憶體時,最小查詢延遲將取決於資料、查詢的型別、分片的大小。查詢大量小分片將使得每個分片的處理速度更快,但是隨著更多的任務需要按順序排隊和處理,它不一定要比查詢較小數量的更大的分片更快。如果有多個併發查詢,則有很多小碎片也會降低查詢吞吐量。

提示:從查詢效能角度確定最大分片大小的最佳方法是使用逼真的資料和查詢進行基準測試(真實資料而非模擬資料)。 始終使用查詢和索引負載進行基準測試,代表節點在生產中需要處理的內容,因為單個查詢的優化可能會產生誤導性的結果。

    注1避免使用非常大的分片,因為這會對群集從故障中恢復的能力產生負面影響。 對分片的大小沒有固定的限制,但是通常情況下很多場景限制在 50GB 的分片大小以內。

    注2:當在ElasticSearch叢集中配置好你的索引後, 你要明白在叢集執行中你無法調整分片設定. 即便以後你發現需要調整分片數量, 你也只能新建建立並對資料進行重新索引(reindex)(雖然reindex會比較耗時, 但至少能保證你不會停機).
    主分片的配置與硬碟分割槽很類似, 在對一塊空的硬碟空間進行分割槽時, 會要求使用者先進行資料備份, 然後配置新的分割槽, 最後把資料寫到新的分割槽上。

    注3儘可能使用基於時間的索引來管理資料保留期。根據保留期(retention period,可以理解成有效期)將資料分組。基於時間的索引還可以輕鬆地隨時間改變主分片和副本分片的數量(以為要生成的下一個索引進行更改)。這簡化了適應不斷變化的資料量和需求。

 

三、效能調優

1.關閉data結點的http功能

針對ElasticSearch叢集中的所有資料節點,不用開啟http服務。將其中的配置 引數這樣設定:http.enabled: false,同時也不要安裝head, bigdesk, marvel等監控 外掛,這樣保證data節點伺服器只需處理建立/更新/刪除/查詢索引資料等操作。http功能可以在非資料節點伺服器上開啟,上述相關的監控外掛也安裝到這些服 務器上,用於監控ElasticSearch叢集狀態等資料資訊。可以提高服務效能及資料安全

 

2.最小主節點數量,防止腦裂

正常情況下,叢集中的所有節點,應該對主節點的選擇是一致的,即一個叢集中只有一個選舉出來的主節點。然而,在某些情況下,比如網路通訊出現問題、主節點因為負載過大停止響應等等,就會導致重新選舉主節點,此時可能會出現叢集中有多個主節點的現象,即節點對叢集狀態的認知不一致,稱之為腦裂現象。為了儘量避免此種情況的出現,可以通過discovery.zen.minimum_master_nodes來設定最少可工作的候選主節點個數,建議設定為(候選主節點數 / 2) + 1, 比如,當有三個候選主節點時,該配置項的值為(3/2)+1=2,也就是保證叢集中有半數以上的候選主節點。候選主節點的設定方法是設定node.mater為true

 

3.指定叢集及節點名字

Elasticsearch 預設啟動的叢集名字叫 elasticsearch,部署時修改預設名字,防止其它例項不小心加入叢集。

Elasticsearch 會在節點啟動的時候隨機給它指定一個名字,這些名字是在啟動的時候產生的,每次啟動節點, 它都會得到一個新的名字。這會使日誌變得很混亂,因為所有節點的名稱都是不斷變化的。因此需要手動指定每個節點 名字,方便跟蹤,排查問題

 

4.一臺伺服器上最好只部署一個Node

一臺物理伺服器上可以啟動多個Node伺服器節點(通過設定不同的啟動port),但一臺伺服器上的CPU,記憶體,硬碟等資源畢竟有限,從伺服器效能考慮,不建議一臺伺服器上啟動多個node節點。

 

5.多個path.data

如果磁碟空間和IO效能是Elasticsearch的瓶頸的話,使用多個IO裝置(通過設定多個path.data路徑)儲存shards,能夠增加總的儲存空間和提升IO效能。

如果您新增多個驅動器來提高一個單獨索引的效能,可能幫助不大,因為 大多數節點只有一個分片和這樣一個積極的驅動器。多個資料路徑只是幫助如果你有許多索引/分片在單個節點上。如果需要更高階的、穩健的、靈活的配置, 可使用軟磁碟陣列( software RAID )的軟體,而不是多個資料路徑的功能。

 

6.叢集恢復配置

當你叢集重啟時,幾個配置項影響你的分片恢復的表現。 首先,我們需要明白如果什麼也沒配置將會發生什麼。

想象一下假設你有 10 個節點,每個節點只儲存一個分片,這個分片是一個主分片或者是一個副本分片,或者說有一個有 5 個主分片/1 個副本分片的索引。有時你需要為整個叢集做離線維護(比如,為了安裝一個新的驅動程式), 當你重啟你的叢集,恰巧出現了 5 個節點已經啟動,還有 5 個還沒啟動的場景。

假設其它 5 個節點出問題,或者他們根本沒有收到立即重啟的命令。不管什麼原因,你有 5 個節點線上上,這五個節點會相互通訊,選出一個 master,從而形成一個叢集。 他們注意到資料不再均勻分佈,因為有 5 個節點在叢集中丟失了,所以他們之間會立即啟動分片複製。

最後,你的其它 5 個節點開啟加入了叢集。這些節點會發現 它們 的資料正在被複制到其他節點,所以他們刪除本地資料(因為這份資料要麼是多餘的,要麼是過時的)。 然後整個叢集重新進行平衡,因為叢集的大小已經從 5 變成了 10。在整個過程中,你的節點會消耗磁碟和網路頻寬,來回移動資料,因為沒有更好的辦法。對於有 TB 資料的大叢集, 這種無用的資料傳輸需要 很長時間 。如果等待所有的節點重啟好了,整個叢集再上線,所有的本地的資料都不需要移動。

此時我們需要指定  gateway.recover_after_nodes,gateway.recover_after_nodes,gateway.recover_after_master_nodes,gateway.recover_after_data_nodes等引數,具體參照 官網本地閘道器配置

 

7.預留一半記憶體給Lucene使用

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

 記憶體對於Elasticsearch來說絕對是重要的,用於更多的記憶體資料提供更快的操作。而且還有一個記憶體消耗大戶-Lucene。

Lucene的設計目的是把底層OS裡的資料快取到記憶體中。Lucene的段是分別儲存到單個檔案中的,這些檔案都是不會變化的,所以很利於快取,同時作業系統也會把這些段檔案快取起來,以便更快的訪問。

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

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

檔案系統快取是為了緩衝磁碟的IO操作。至少確保有一半機器的記憶體保留給作業系統,而不是JAVA JVM佔用了全部記憶體

 

8.記憶體優化

 Elasticsearch預設堆記憶體為1.9 GB,對於實際應用,顯然不夠,不建議使用預設配置。

設定Elasticsearch的記憶體又兩種方案,最簡單的方法是設定一個環境變數為ES_HEAP_SIZE。當伺服器啟動時,它將讀取此環境變數並相應設定記憶體。命令如下:

export ES_HEAP_SIZE=8g

另外一種方法則是在啟動ES的時候,設定。

./bin/elasticsearch -Xmx8g -Xms8g

推薦採用第一種設定方法。

ES記憶體建議採用分配機器實體記憶體的一半,但最大不要超過32GB(原因請看 堆記憶體配置)。如何判斷記憶體設定是否恰當,看ES啟動日誌中的:

[2017-09-28T17:07:34,334][INFO ][o.e.e.NodeEnvironment    ] [node182e] heap size [1.9gb], compressed ordinary object pointers [true]

 如果[true],則表示ok。一半超過32GB會為[false],請依次降低記憶體設定試試。

另外注意關閉Linux的swap!

 如果機器實體記憶體很大的話,可以多啟動幾個節點(每個32GB)

 

9.索引別名和零停機

Elasticsearch的API支援給索引起別名,有了別名之後可以像使用索引一樣使用它。但不只是這些,一個別名可以對映多個索引,所以在需要經常指定多個索引查詢的情況下,大可將所查詢的索引起一個別名來查。別名也可以將索引查詢的過濾條件包含在內,使用別名查詢時可以查詢索引的一個子集。別名應用例項:  elasticsearch-不停服務修改mapping Elasticsearch 之索引別名 alias 

 

10.設定最優的分片數和備份數

根據機器數,磁碟數,索引大小等硬體環境,根據測試結果,設定最優的分片數和備份數,單個分片最好不超過10GB,定期刪除不用的索引,做好冷資料的遷移。

分片數是與檢索速度非常相關的的指標,如果分片數過少或過多都會導致檢索比較慢。分片數過多會導致檢索時開啟比較多的檔案別外也會導致多臺伺服器之間通訊。而分片數過少會導致單個分片索引過大,所以檢索速度慢。一個索引會分成多個分片儲存,分片數量在索引建立後不可更改。

ElasticSearch在建立索引資料時,最好指定相關的shards數量和replicas, 否則會使用伺服器中的預設配置引數shards=5,replicas=1。

因為這兩個屬性的設定直接影響叢集中索引和搜尋操作的執行。假設你有足夠的機器來持有碎片和副本,那麼可以按如下規則設定這兩個值:

1) 擁有更多的碎片可以提升索引執行能力,並允許通過機器分發一個大型的索引;

2) 擁有更多的副本能夠提升搜尋執行能力以及叢集穩定性。

對於一個索引來說,number_of_shards只能設定一次,而number_of_replicas可以使用索引更新設定API在任何時候被增加或者減少。

這兩個配置引數在配置檔案的配置如下:

index.number_of_shards: 5 number_of_replicas: 1

Elastic官方文件建議:一個Node中一個索引最好不要多於三個shards.配置total_shards_per_node引數,限制每個index每個節點最多分配多少個分片.

副本數與索引的穩定性有比較大的關係,如果Node在非正常掛了,經常會導致分片丟失,為了保證這些資料的完整性,可以通過副本來解決這個問題。建議在建完索引後在執行Optimize後,馬上將副本數調整過來。

 

11.設定合理的重新整理時間

建立的索引,不會立馬查到,這是為什麼elasticsearch為near-real-time的原因需要配置index.refresh_interval引數,預設是1s。

這迫使Elasticsearch叢集每秒建立一個新的 segment (可以理解為Lucene 的索引檔案)。增加這個值,例如30s,60s,可以允許更大的segment寫入,減後以後的segment合併壓力。


12.根據業務設定合理的欄位型別

1、文件值(doc_values)是儲存在硬碟上的資料結構,在索引時(index time)根據文件的原始值建立,文件值是一個列式儲存風格的資料結構,非常適合執行儲存和聚合操作,除了字元型別的分析欄位之外,其他欄位型別都支援文件值儲存。預設情況下,欄位的文件值儲存是啟用的,除了字元型別的分析欄位之外。如果不需要對欄位執行排序或聚合操作,可以禁用欄位的文件值,以節省硬碟空間


"mappings": {
    "my_type": {
        "properties": {
        "status_code": { 
            "type":       "text",
            "index":      "not_analyzed"
        },
        "session_id": { 
            "type":       "text",
            "index":      "not_analyzed",
            "doc_values": false
        }
        }
    }

 

 

2、fielddata會消耗大量的JVM記憶體,因此,儘量為JVM設定大的記憶體,不要為不必要的欄位啟用fielddata儲存。通過format引數控制是否啟用欄位的fielddata特性,字元型別的分析欄位,fielddata的預設值是paged_bytes,這就意味著,預設情況下,字元型別的分析欄位啟用fielddata儲存。一旦禁用fielddata儲存,那麼字元型別的分析欄位將不再支援排序和聚合查詢。


"mappings": {
    "my_type": {
      "properties": {
        "text": {
          "type": "text",
          "fielddata": {
            "format": "disabled" 
          }
        }
      }
    }

 

四、部署拓撲示意圖

節點角色
Master 
node.master: true 節點可以作為主節點,建議至少部署3個
DataNode
node.data: true 預設是資料節點,據業務儲存資料量估算 
Coordinate node

協調節點據客戶端查詢量計算,不指定此節點會隨機叢集內所有節點

說明:

一個節點可以充當一個或多個角色,預設三個角色都有

協調節點:一個節點只作為接收請求、轉發請求到其他節點、彙總各個節點返回資料等功能的節點。就叫協調節點

 

Es寫入資料的過程

  1. 客戶端選擇一個node傳送請求過去,這個node就是coordinating node (協調節點)
  2. coordinating node,對document進行路由,將請求轉發給對應的node
  3. 實際上的node上的primary shard處理請求,然後將資料同步到replica node
  4. coordinating node,如果發現primary node和所有的replica node都搞定之後,就會返回請求到客戶端

Es讀資料過程

查詢,GET某一條的資料,寫入某個document,這個document會自動給你分配一個全域性的唯一ID,同時跟住這個ID進行hash路由到對應的primary shard上面去,當然也可以手動的設定ID

  1. 客戶端傳送任何一個請求到任意一個node,成為coordinate node
  2. coordinate node 對document進行使用隨機輪尋演算法,路由到shard(在primary shard 以及所有的replica中隨機選擇一個實現負載均衡)
  3. 接受請求的node,返回document給coordinate note
  4. coordinate node返回給客戶端

Es搜尋資料過程

  1. 客戶端傳送一個請求給coordinate node
  2. 協調節點將搜尋的請求轉發給所有的shard對應的primary shard 或replica shard
  3. query phase:每一個shard 將自己搜尋的結果(其實也就是一些唯一標識),返回給協調節點,有協調節點進行資料的合併,排序,分頁等操作,產出最後的結果
  4. fetch phase ,接著由協調節點,根據唯一標識去各個節點進行拉去資料,最總返回給客戶端

備註

document路由到shard上是什麼意思?

一個index的資料會被分為多片,每片都在一個shard中。所以說,一個document,只能存在於一個shard中。
當客戶端建立document的時候,es此時就需要決定說,這個document是放在這個index的哪個shard上。
這個過程,就稱之為document routing,資料路由。

(2)路由演算法:shard = hash(routing) % number_of_primary_shards

 

參考文章:

https://blog.csdn.net/hguisu/article/details/7962350

https://www.jianshu.com/p/a85b458e7cf3

相關文章