ElasticSearch 分散式集群
- 2021 年 3 月 1 日
- 筆記
- ElasticSearch 筆記
公號:碼農充電站pro
主頁://codeshellme.github.io
1,ES 的分散式架構
ES 是一個分散式的集群,具有高可用性和可擴展性:
- 高可用性指的是:當某些節點意外宕機或者數據丟失的時候,不影響整個集群的使用。
- 可擴展性指的是:當業務數據量增加的時候,可以增加節點的數量,從而增強整個集群的能力。
ES 集群
ES 集群中可以有一個或多個節點,ES 通過集群名字來區分不同的集群,集群名可以通過 cluster.name
進行設置,默認為 “elasticsearch”。
2,ES 的節點類型
ES 的一個節點就是一個 Java 進程,所以一台機器可以運行一個或多個節點,生產環境建議一台機器只運行一個節點。
每個節點啟動之後,都會分配一個 UID
,並保存在 data
目錄下。
每個節點都有節點名字,節點名可通過 node.name
設置。
2.1,Master 節點
Master 節點的職責:
- 處理客戶端的請求。
- 決定分片被分配到哪個節點。
- 負責索引的創建於刪除。
- 維護集群狀態。
- 等。
集群的狀態包括:
- 所有的節點資訊
- 所有的索引及其 Mapping 和 Setting 資訊
- 分片的路由資訊
所有的節點有保存了集群的狀態資訊,但只有主節點能夠修改集群狀態。
2.2,Master-eligible 節點
在 ES 集群中,只有 Master-eligible 節點可以被選舉為 Master 節點。
每個節點啟動後默認就是 Master-eligible 節點,可以通過設置 node.master
為 false
來禁止成為 Master-eligible 節點。
默認情況下,集群中的第一個節點啟動後,會將自己選舉為 Master 節點。
集群中的每個節點都保存了集群的狀態,但只有 Master 節點能夠修改集群的狀態資訊。
2.3,Data 與 Coordinating 節點
用於保存 ES 數據的節點,就是 Data 節點,它對數據擴展起到了至關重要的作用。
Coordinating 節點叫做協調節點,它負責接收 Client 的請求,將請求分發到合適的節點,並最終匯總結果返回給 Client。
在 ES 中,所有的節點都是 Coordinating 節點。
2.4,Ingest 節點
Ingest 節點用於對數據預處理,通過添加一些 processors 來完成特定的處理。
Ingest 節點是在 ES 5.0 後引入的一種節點類型,可以達到一定的 Logstash 的功能。
默認情況下,所有的節點都是 Ingest 節點。
2.5,配置節點類型
理論上,一個節點可以扮演過多個角色,但生產環境中,建議設置單一角色。
節點的類型可以通過下面參數進行配置:
節點類型 | 配置參數 | 默認值 |
---|---|---|
Master-eligible | node.master | true |
Data Node | node.data | true |
Ingest Node | node.ingest | true |
Coordinating Node | 無 | 設置上面 3 個都為 false |
3,集群的健康狀態
我們可以通過下面的 API 來查看整個集群的健康狀態:
集群有 3 種級別的健康狀態:
green
:所有的主分片與副本分片都正常。yellow
:所有的主分片都正常,某些副本分片不正常。red
:部分主分片不正常。
我們也可以通過 Kibana 中的索引管理,來查看每個索引的健康狀態:
索引的狀態級別與集群的狀態級別一致。
4,腦裂問題
腦裂問題是分散式系統中的經典問題。
腦裂問題指的是,當出現網路故障時,一些節點無法與另一些節點連接,這時這兩大部分節點會各自為主;當網路恢復時,也無法恢復成一個整體。
如何避免腦裂問題
要限定一個選舉條件,設置 Quorum(仲裁):
- Quorum = (master 節點總數 / 2)+ 1
只有當 Master eligible 節點數大於 Quorum 時,才能進行選舉。
在 ES 7.0 之前,為了避免腦裂問題,需要手動設置 discovery.zen.minimum_master_nodes
為 Quorum。
在 ES 7.0 之後,ES 會自己處理腦裂問題,不需要用戶處理。
5,ES 中的分片
ES 中的分片(Shard)用於存儲數據,是存儲的最小單元。
分片有兩種:主分片(Primary Shard)和副本分片(Replica Shard),副本分片是主分片的拷貝。
主分片用於數據水平擴展的問題,主分片數在索引創建時指定,之後不允許修改。
副本分片用於解決數據高可用的問題,副本分片數可以動態調整。
分片數可以通過索引的 setting
進行設置,比如:
PUT /index_name
{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 1
}
}
其中 number_of_shards
表示主分片數,number_of_replicas
表示每個主分片的副本分片數。
如果一個集群有 3 個數據節點,某個索引有 3 個主分片,1 一個副本分片,那麼它的節點分布會像下面這樣:
其中藍色框為主分片,白色框為副本分片。
ES 在分配主副分片時,會將副本分片與主分片應該在不同的節點上。
主分片和副本分片分別分布到不同的數據節點上,這樣的話,如果有某個數據節點宕機,也不會影響整個系統的使用。
ES 7.0 開始,默認的主分片數為 1,默認的副本分片數為 0。在生產環境中,副本分片數至少為 1。
5.1,生產環境如何設置分片數
分片數設置不合理引發的問題:
5.2,集群節點的變化
根據這樣的配置(3 個主分片,1 個副本分片),如果只有一個節點,則會導致副本分片無法分配(ES 會將主副分片分配在不同的節點上),集群狀態為 yellow。
如果此時增加一個數據節點,那麼副本分片就得以分配,集群具備了故障轉移能力,集群狀態轉為 green。
如果此時再增加一個數據節點,那麼主節點會重新分配分片的分布。同時,集群的整體能力也得到了提升。
5.3,故障轉移
如果此時有一個節點發生故障,比如主節點發生了故障:
此時集群的狀態會變為 yellow,然後會重新選舉主節點(假設選舉了 Node2 為主節點),並且原來的 Node1 節點上的 p0 和 R1 分片,會被分配到 Node2 和 Node3 上。
集群調整完畢後,會重新恢復到 green 狀態。
6,分片的內部原理
ES 中的一個分片對應了 Lucene 中的一個 Index。
6.1,Lucene Index
在 Lucene 中,單個倒排索引文件稱為 Segment。
Segment 是不可變的,當有新的文檔寫入時,會生成新的 Segment(放在文件系統快取中)。
多個 Segment 匯總在一起稱為 Lucene 中的 Index,也就是 ES 中的分片。
6.2,Refresh 刷新
ES 的文檔在寫入時,會先放在 Index Buffer
(記憶體) 中,當 Index Buffer 的空間被佔用到一定程度/時間周期後,會 Refresh 到 Segment 中,Index Buffer 則會被清空。
Refresh 的刷新頻率可以通過 index.refresh_interval 參數進行設置,默認為 1 秒。
或者當 Index Buffer 被佔用到 JVM 的 10%(默認值),也會觸發 Refresh。
當文檔被 Refresh 到 Segment 後,就可以被 ES 檢索到了。
6.3,Transaction log
寫入文檔時,會先放在 Index Buffer
中,而 Index Buffer 是在記憶體中,為了防止記憶體意外(比如斷電)丟失,在寫入 Index Buffer 的同時,也會寫到 Transaction log(磁碟)中。
一個 Transaction log 默認是 512M。
6.4,Flush 操作
ES 的 Flush 會觸發以下操作:
- 調用 Refresh
- 調用 fsync,將文件系統快取中的 Segment 寫入磁碟。
- 清空 Transaction log。
Flush 操作默認 30 分鐘調用一次,或者當 Transaction log 滿(默認 512 M)時也會觸發 Flush。
6.5,Merge 合併
當越來越多的 Segment 被寫入到磁碟後,磁碟上的 Segment 會變得很多,ES 會定期 Merge 這些 Segment。
文檔的刪除操作並不會馬上被真正的刪除,而是會寫入 del 文件中,Merge 操作也會刪除該文件。
Merge 操作可以由 ES 自動觸發,也可以手動強制 Merge,語法如下:
POST index_name/_forcemerge
7,文檔的分散式存儲
文檔會均勻分布在分片上,充分利用硬體資源,避免資源利用不均。
文檔到分片的路由演算法:
shard_index = hash(_routing) % number_of_primary_shards
- Hash 演算法可以保證文檔均勻的分散到分片上。
- 默認的
_routing
值為文檔 id。 _routing
的值也可以自行指定。
正是因為文檔的路由演算法是基於主分片數來計算的,所以主分片數一旦確定以後,就不能修改。
_routing 的設置語法如下:
POST index_name/_doc/doc_id/routing=xxx
{
# 文檔數據
}
文檔的 Write 操作(插入,更新,刪除)的流程:
- 客戶將文檔的 Write 請求發送到 Coordinating 節點(Coordinating 節點負責處理客戶請求,而不是 Master 節點)。
- Coordinating 節點通過 Hash 演算法找到該文檔的主分片。
- 在主分片上進行 Write 操作,主分片 Write 成功後,將該 Write 請求發送到所有的副本分片。
- 所有副本分片進行相應的 Write 操作,成功後,將結果返回給主分片。
- 主分片將所以的執行結果回饋給 Coordinating 節點。
- Coordinating 節點將最終的結果回饋給客戶端。
8,分散式查詢及相關性算分
8.1,Query Then Fetch 過程
ES 的搜索過程分兩個階段:
- Query 階段:
- 用戶將查詢請求發送到 Coordinating 節點,Coordinating 節點會隨機選擇 N(主分片數) 個分片,發送查詢請求。
- 收到查詢請求的分片執行查詢,並進行排序。然後每個分片都會返回
From + Size
個排好序的文檔 ID 和排序值(score),給 Coordinating 節點。
- Fetch 階段:
- Coordinating 節點會將從所有分片得到的文檔重新排序,重新得到
From + Size
個文檔的 ID。 - Coordinating 節點以
Multi Get
的方式,到相應的分片獲取具體的文檔資訊,並返回給用戶。
- Coordinating 節點會將從所有分片得到的文檔重新排序,重新得到
這兩個階段合稱為 Query Then Fetch。
8.2,Query Then Fetch 的問題
8.3,算分不準的解決辦法
(本節完。)
推薦閱讀:
歡迎關注作者公眾號,獲取更多技術乾貨。