前言
本文的主要內容:
- 分散式介紹及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管理介面中我們可以看到 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.host
為 0.0.0.0
,discovery.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
如果此時 master 所在機器當機導致服務終止,此時叢集如何處理?
Hadoop2 和 Hadoop3 發現 master 無法響應一段時間後會發起 master 主節點選舉,比如這裡選擇 Hadoop2 為 master 節點。由於此時主分片 P0 和 P2 下線,叢集狀態變為 Red
node2 發現主分片 P0 和 P2 未分配,將 R0 和 R2 提升為主分片,此時由於所有主分片都正常分配,叢集狀態變為 yellow
Hadoop2 為 P0 和 P2 生成新的副本,叢集狀態變為綠色
最後看看 Hadoop2 列印的日誌
文件分散式儲存
文件最終會儲存在分片上。文件選擇分片需要文件到分片的對映演算法,目的是使得文件均勻分佈在所有分片上,以充分利用資源。
演算法:
- 隨機選擇或者round-robin演算法?不可取,因為需要維護文件到分片的對映關係,成本巨大
- 根據文件值實時計算對應的分片
文件到分片的對映演算法
ES通過如下的公式計算文件對應的分片
shard = hash(routing) % number_of_primary_shards
- hash演算法保證可以將資料均勻地分散在分片中
- routing是一個關鍵引數,預設是文件id,也可以自行指定
- number_of_primary_shards是主分片數
該演算法與主分片數相關,這也是分片數一旦確定後便不能更改的原因
文件建立流程
- Client向node3發起建立文件的請求
- node3通過routing計算該文件應該儲存在shard1上,查詢cluster state後確認主分片P1在node2上,然後轉發建立文件的請求到node2
- P1 接收並執行建立文件請求後,將同樣的請求傳送到副本分片R1
- R1接收並執行建立文件請求後,通知P1成功的結果
- P1接收副本分片結果後,通知node3建立成功
- node3返回結果到Client
文件讀取流程
- Client向node3發起獲取文件1的請求
- node3通過routing計算該文件在shard1上,查詢cluster state後獲取shard1的主副分片列表,然後以輪詢的機制獲取一個shard,比如這裡是R1,然後轉發讀取文件的請求到node1
- R1接收並執行讀取文件請求後,將結果返回node3
- node3返回結果給client
文件批量建立的流程
- client向node3發起批量建立文件的請求(bulk)
- node3通過routing計算所有文件對應的shard,然後按照主shard分配對應執行的操作,同時傳送請求到涉及的主shard,比如這裡3個主shard都需要參與
- 主shard接收並執行請求後,將同樣的請求同步到對應的副本shard
- 副本shard執行結果後返回到主shard,主shard再返回node3
- node3整合結果後返回client
文件批量讀取的流程
- client向node3發起批量獲取所有文件的請求(mget)
- node3通過routing計算所有文件對應的shard,然後通過輪詢的機制獲取要參與shard,按照shard投建mget請求,通過傳送請求到涉及shard,比如這裡有2個shard需要參與
- R1,R2返回文件結果
- node3返回結果給client
腦裂問題
腦裂問題,英文為split-brain,是分散式系統中的經典網路問題,如下圖所示:
3個節點組成的叢集,突然node1的網路和其他兩個節點中斷
node2與node3會重新選舉master,比如node2成為了新的master,此時會更新cluster state
node1自己組成叢集后,也更新cluster state
同一個叢集有兩個master,而且維護不同的cluster state,網路恢復後無法選擇正確的master
解決方案為僅在可選舉master-eligible節點數大於等於quorum時才可以進行master選舉
quorum = master-eligible節點數/2 + 1
,例如3個master-eligible節點時,quorum 為2- 設定
discovery.zen.minimun_master_nodes
為quorum
即可避免腦裂問題
倒排索引的不可變更
倒排索引一旦生成,不能更改 其好處如下:
- 不用考慮併發寫檔案的問題,杜絕了鎖機制帶來的效能問題
- 由於檔案不再更改,可以充分利用檔案系統快取,只需載入一次,只要記憶體足夠,對該檔案的讀取都會從記憶體讀取,效能高
- 利於生成快取資料
- 利於對檔案進行壓縮儲存,節省磁碟和記憶體儲存空間
壞處為需要寫入新文件時,必須重新構建倒排索引檔案,然後替換老檔案後,新文件才能被檢索,導致文件實時性差
文件搜尋實時性
解決方案是新文件直接生成新的倒排索引檔案,查詢的時候同時查詢所有的倒排檔案,然後做結果的彙總計算即可
Lucene便是採用了這種方案,它構建的單個倒排索引稱為segment,合在一起稱為index,與ES中的Index概念不同,ES中的一個shard對應一個Lucene Index
Lucene會有一個專門的檔案來記錄所有的segment資訊,稱為commit point
refresh
segment寫入磁碟的過程依然很耗時,可以藉助檔案系統快取的特性,現將segment在快取中建立並開放查詢來進一步提升實時性,該過程在ES中被稱為refresh
在refresh之前文件會先儲存在一個buffer中,refresh時將buffer中的所有文件清空並生成segment
ES預設每1秒執行一次refresh,因此文件的實時性被提高到1秒,這也是ES被稱為 近實時(Near Real Time)的原因
translog
如果在記憶體中的segment還沒有寫入磁碟前發生了當機,那麼其中的文件就無法恢復了,如何解決這個問題呢?
- ES引入translog機制,寫入文件到buffer時,同時將該操作寫入translog
- translog檔案會即時寫入磁碟(fsync),6.x預設每個請求都會落盤
flush
flush負責將記憶體中的segment寫入磁碟,主要做成如下的工作:
- 將translog寫入磁碟
- 將index buffer清空,其中的文件生成一個新的segment,相當於一個refresh操作
- 更新commit point並寫入磁碟
- 執行fsync操作,將記憶體中的segment寫入磁碟
- 刪除舊的translog檔案
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的術語對照如下所示:
Segment Merging
隨著segment的增多,由於一次查詢的segment數增多,查詢速度會變慢 ES會定時在後臺進行segment merge的操作,減少segment的數量 通過force_merge api可以手動強制做segment merge的操作
參考: 慕課網 Elastic Stack從入門到實踐
更多內容請訪問我的個人部落格:laijianfeng.org
開啟微信掃一掃,關注【小旋鋒】微信公眾號,及時接收博文推送