Elasticsearch 分散式特性

小旋鋒發表於2018-08-25

前言

本文的主要內容:

  • 分散式介紹及cerebro
  • 構建叢集
  • 副本與分片
  • 叢集狀態與故障轉移
  • 文件分散式儲存
  • 腦裂問題
  • shard詳解

分散式介紹及cerebro

ES支援叢集模式,是一個分散式系統,其好處主要有兩個:

  • 增大系統容量,如記憶體、磁碟,使得ES叢集可以支援PB級的資料
  • 提高系統可用性,即使部分節點停止服務,整個叢集依然可以正常服務

ES叢集由多個ES例項組成

  • 不同叢集通過叢集名稱來區分,可通過cluster.name進行修改,名稱預設為elasticsearch
  • 每個ES例項本質上是一個JVM程式,且有自己的名字,通過node.name進行修改

cerebro

cerebro 是一個ES Web管理工具,專案地址 github.com/lmenezes/ce…

其配置檔案為 conf/application.conf,啟動 cerebro ,預設監聽的地址為 0.0.0.0:9000

bin/cerebro
# 也可指定監聽ip和埠號
bin/cerebro -Dhttp.port=1234 -Dhttp.address=127.0.0.1
複製程式碼

訪問 http://yourhost:9000 ,填寫要監控的 ES 地址:http://eshost:9200 即可進入管理介面

cerebro管理介面

cerebro 節點資訊

cerebro 叢集配置

在cerebro管理介面中我們可以看到 ES節點、索引、shard的分佈、叢集引數配置等多種資訊

構建叢集

如果只有一臺機器,可以執行下面的命令,每次指定相同的叢集名稱,不同的節點名稱和埠,即可在同一臺機器上啟動多個ES節點

bin/elasticsearch -Ecluster.name=my_cluster -Enode.name=node1 -Ehttp.port=9200 -d
複製程式碼

作者的是在 virtualbox 上安裝Ubuntu虛擬機器,在安裝好開發環境,正常啟動ES之後,採取複製虛擬機器的做法,複製後需要修改虛擬機器的UUID,做法可自行上網搜尋。

作者複製了兩個,準備構建一個擁有三個ES節點的叢集。啟動虛擬機器後可以進行關閉防火牆,配置hosts以使相互之間能夠通過主機名訪問,配置ssh免密訪問等操作

分別修改ES節點中的 cluster.name 為相同名稱,node.name 為各自的主機名,network.host0.0.0.0discovery.zen.ping.unicast.hosts 列表中中加入各自的 node.name

在ES主目錄下執行命令啟動ES

bin/elasticsearch
複製程式碼

檢視日誌可見叢集搭建完畢

Cluster State 叢集狀態

與ES叢集相關的資料稱為cluster state,主要記錄如下資訊:

  • 節點資訊,比如節點名稱、連線地址等
  • 索引資訊,比如索引名稱,配置等
  • 其他。。

Master Node 主節點

  • 可以修改cluster state的節點成為master節點,一個叢集只能有一個
  • cluster state儲存在每個節點上,master維護最新版本並同步給其他節點
  • master節點是通過叢集中所有節點選舉產生的,可以被選舉的節點成為master-eligible(候選)節點,相關配置如下:node.master: true

Coordinating Node

  • 處理請求的節點即為coordinating節點,該節點為所有節點的預設角色,不能取消
  • 路由請求到正確的節點處理,比如建立索引的請求到master節點

Data Node 資料節點

  • 儲存資料的節點即為Data節點,預設節點都是data型別,相關配置如下:node.data: true

副本與分片

提高系統可用性

提高系統可用性可從兩個方面考慮:服務可用性和資料可用性

服務可用性

  • 2個節點的情況下,允許其中1個節點停止服務

資料可用性

  • 引入副本(Replication)解決
  • 每個節點上都有完備的資料

增大系統容量

如何將資料分佈於所有節點上?

  • 引入分片(shard)解決問題

分片是ES支援PB級資料的基石

  • 分片儲存了部分資料,可以分佈於任意節點上
  • 分片數在索引建立時指定且後續不允許再修改,預設為5個
  • 分片有主分片和副本分片之分,以實現資料的高可用
  • 副本分片的資料由主分片同步,可以有多個,從而提高讀取的吞吐量

分片的分佈

下圖演示的是 3 個節點的叢集中test_index的分片分佈情況,建立時我們指定了3個分片和副本

PUT test_index
{
  "settings": {
    "number_of_replicas": 1,
    "number_of_shards": 3
  }
}
複製程式碼

主副分片的分佈

大致是均勻分佈,實驗中如果由於磁碟空間不足導致有分片未分配,為了測試可以將叢集設定 cluster.routing.allocation.disk.threshold_enabled 設定為 false

此時增加節點是否能提高索引的資料容量?

不能,因為已經設定了分片數為 3 ,shard的數量已經確定,新增的節點無法利用,

此時增加副本數能否提高索引的讀取吞吐量?

不能,因為新增的副本分片也是分佈在這 3 臺節點上,利用了同樣的資源(CPU,記憶體,IO等)。如果要增加吞吐量,同時還需要增加節點的數量

分片數的設定很重要,需要提前規劃好

  • 過小會導致後續無法通過增加節點實現水平擴容
  • 過大會導致一個節點上分佈過多分片,造成資源浪費,同時會影響查詢效能
  • shard的數量的確定:一般建議一個shard的資料量不要超過 30G,shard數量最小為 2

Cluster Health 叢集健康

通過如下API可以檢視叢集健康狀況,狀態status包括以下三種:

  • green 健康狀態,指所有主副分片都正常分配
  • yellow 指所有主分片都正常分配,但有副本分片未正常分配
  • red 有主分片未分配
GET _cluster/health

# 結果
{
  "cluster_name": "elasticsearch",
  "status": "yellow",
  "timed_out": false,
  "number_of_nodes": 1,
  "number_of_data_nodes": 1,
  "active_primary_shards": 115,
  "active_shards": 115,
  "relocating_shards": 0,
  "initializing_shards": 0,
  "unassigned_shards": 111,
  "delayed_unassigned_shards": 0,
  "number_of_pending_tasks": 0,
  "number_of_in_flight_fetch": 0,
  "task_max_waiting_in_queue_millis": 0,
  "active_shards_percent_as_number": 50.88495575221239
}
複製程式碼

Failover 故障轉移

叢集由 3 個節點組成,名稱分別為 master,Hadoop2,Hadoop3, 其中 master 為主節點,叢集狀態status為 green

叢集狀態green

如果此時 master 所在機器當機導致服務終止,此時叢集如何處理?

Hadoop2 和 Hadoop3 發現 master 無法響應一段時間後會發起 master 主節點選舉,比如這裡選擇 Hadoop2 為 master 節點。由於此時主分片 P0 和 P2 下線,叢集狀態變為 Red

節點master當機

node2 發現主分片 P0 和 P2 未分配,將 R0 和 R2 提升為主分片,此時由於所有主分片都正常分配,叢集狀態變為 yellow

image

Hadoop2 為 P0 和 P2 生成新的副本,叢集狀態變為綠色

image

最後看看 Hadoop2 列印的日誌

image

文件分散式儲存

文件最終會儲存在分片上。文件選擇分片需要文件到分片的對映演算法,目的是使得文件均勻分佈在所有分片上,以充分利用資源。

演算法:

  • 隨機選擇或者round-robin演算法?不可取,因為需要維護文件到分片的對映關係,成本巨大
  • 根據文件值實時計算對應的分片

文件到分片的對映演算法

ES通過如下的公式計算文件對應的分片

  • shard = hash(routing) % number_of_primary_shards
  • hash演算法保證可以將資料均勻地分散在分片中
  • routing是一個關鍵引數,預設是文件id,也可以自行指定
  • number_of_primary_shards是主分片數

該演算法與主分片數相關,這也是分片數一旦確定後便不能更改的原因

文件建立流程

  1. Client向node3發起建立文件的請求
  2. node3通過routing計算該文件應該儲存在shard1上,查詢cluster state後確認主分片P1在node2上,然後轉發建立文件的請求到node2
  3. P1 接收並執行建立文件請求後,將同樣的請求傳送到副本分片R1
  4. R1接收並執行建立文件請求後,通知P1成功的結果
  5. P1接收副本分片結果後,通知node3建立成功
  6. node3返回結果到Client

文件建立流程

文件讀取流程

  1. Client向node3發起獲取文件1的請求
  2. node3通過routing計算該文件在shard1上,查詢cluster state後獲取shard1的主副分片列表,然後以輪詢的機制獲取一個shard,比如這裡是R1,然後轉發讀取文件的請求到node1
  3. R1接收並執行讀取文件請求後,將結果返回node3
  4. node3返回結果給client

文件讀取流程

文件批量建立的流程

  1. client向node3發起批量建立文件的請求(bulk)
  2. node3通過routing計算所有文件對應的shard,然後按照主shard分配對應執行的操作,同時傳送請求到涉及的主shard,比如這裡3個主shard都需要參與
  3. 主shard接收並執行請求後,將同樣的請求同步到對應的副本shard
  4. 副本shard執行結果後返回到主shard,主shard再返回node3
  5. node3整合結果後返回client

文件批量建立的流程 bulk

文件批量讀取的流程

  1. client向node3發起批量獲取所有文件的請求(mget)
  2. node3通過routing計算所有文件對應的shard,然後通過輪詢的機制獲取要參與shard,按照shard投建mget請求,通過傳送請求到涉及shard,比如這裡有2個shard需要參與
  3. R1,R2返回文件結果
  4. node3返回結果給client

文件批量讀取的流程 mget

腦裂問題

腦裂問題,英文為split-brain,是分散式系統中的經典網路問題,如下圖所示:

3個節點組成的叢集,突然node1的網路和其他兩個節點中斷

image

node2與node3會重新選舉master,比如node2成為了新的master,此時會更新cluster state

node1自己組成叢集后,也更新cluster state

同一個叢集有兩個master,而且維護不同的cluster state,網路恢復後無法選擇正確的master

image

解決方案為僅在可選舉master-eligible節點數大於等於quorum時才可以進行master選舉

  • quorum = master-eligible節點數/2 + 1,例如3個master-eligible節點時,quorum 為2
  • 設定 discovery.zen.minimun_master_nodesquorum 即可避免腦裂問題

image

倒排索引的不可變更

倒排索引一旦生成,不能更改 其好處如下:

  • 不用考慮併發寫檔案的問題,杜絕了鎖機制帶來的效能問題
  • 由於檔案不再更改,可以充分利用檔案系統快取,只需載入一次,只要記憶體足夠,對該檔案的讀取都會從記憶體讀取,效能高
  • 利於生成快取資料
  • 利於對檔案進行壓縮儲存,節省磁碟和記憶體儲存空間

壞處為需要寫入新文件時,必須重新構建倒排索引檔案,然後替換老檔案後,新文件才能被檢索,導致文件實時性差

文件搜尋實時性

解決方案是新文件直接生成新的倒排索引檔案,查詢的時候同時查詢所有的倒排檔案,然後做結果的彙總計算即可

Lucene便是採用了這種方案,它構建的單個倒排索引稱為segment,合在一起稱為index,與ES中的Index概念不同,ES中的一個shard對應一個Lucene Index

Lucene會有一個專門的檔案來記錄所有的segment資訊,稱為commit point

image

refresh

segment寫入磁碟的過程依然很耗時,可以藉助檔案系統快取的特性,現將segment在快取中建立並開放查詢來進一步提升實時性,該過程在ES中被稱為refresh

在refresh之前文件會先儲存在一個buffer中,refresh時將buffer中的所有文件清空並生成segment

ES預設每1秒執行一次refresh,因此文件的實時性被提高到1秒,這也是ES被稱為 近實時(Near Real Time)的原因

image

translog

如果在記憶體中的segment還沒有寫入磁碟前發生了當機,那麼其中的文件就無法恢復了,如何解決這個問題呢?

  • ES引入translog機制,寫入文件到buffer時,同時將該操作寫入translog
  • translog檔案會即時寫入磁碟(fsync),6.x預設每個請求都會落盤

image

flush

flush負責將記憶體中的segment寫入磁碟,主要做成如下的工作:

  • 將translog寫入磁碟
  • 將index buffer清空,其中的文件生成一個新的segment,相當於一個refresh操作
  • 更新commit point並寫入磁碟
  • 執行fsync操作,將記憶體中的segment寫入磁碟
  • 刪除舊的translog檔案

image

flush發生的時機主要有如下幾種情況:

  • 間隔時間達到時,預設是30分鐘,5.x之前可以通過index.translog.flush_threshold_period修改,之後無法修改
  • translog佔滿時,其大小可以通過index.translog.flush_threshold_size控制,預設是512mb,每個index有自己的translog

refresh

refresh發生的時機主要有如下幾種情況:

  • 間隔時間達到時,通過index.settings.refresh_interval來設定,預設是1秒
  • index.buffer佔滿時,其大小通過indices.memory.index_buffer_size設定,預設為JVM heap的10%,所有shard共享
  • flush發生時也會發生refresh

刪除與更新文件

segment一旦生成就不能更改,那麼如果你要刪除文件該如何操作?

  • Lucene專門維護一個.del檔案,記錄所有已經刪除的文件,注意.del上記錄的是文件在Lucene內部的id
  • 在查詢結果返回前會過濾掉.del中所有的文件

要更新文件如何進行呢?

  • 首先刪除文件,然後再建立新文件

整體視角

ES Index與Lucene Index的術語對照如下所示:

image

Segment Merging

隨著segment的增多,由於一次查詢的segment數增多,查詢速度會變慢 ES會定時在後臺進行segment merge的操作,減少segment的數量 通過force_merge api可以手動強制做segment merge的操作

參考: 慕課網 Elastic Stack從入門到實踐


更多內容請訪問我的個人部落格:laijianfeng.org

開啟微信掃一掃,關注【小旋鋒】微信公眾號,及時接收博文推送

小旋鋒的微信公眾號

相關文章