上一章節我介紹了Elasticsearch安裝與運行,本章節及后續章節將全方位介紹 Elasticsearch 的工作原理
在這個章節中,我將會再進一步介紹 cluster 、 node 、 shard 等常用術語,Elastisearch 的擴容機制, 以及如何處理硬件故障的內容。
1、分布式特性
Elasticsearch 可以橫向擴展至數百(甚至數千)的服務器節點,同時可以處理PB級數據。Elasticsearch 天生就是分布式的,並且在設計時屏蔽了分布式的復雜性。
Elasticsearch 在分布式方面幾乎是透明的。教程中並不要求了解分布式系統、分片、集群發現或其他的各種分布式概念。可以使用筆記本上的單節點輕松地運行教程里的程序,但如果你想要在 100 個節點的集群上運行程序,一切依然順暢。
Elasticsearch 盡可能地屏蔽了分布式系統的復雜性。這里列舉了一些在后台自動執行的操作:
- 分配文檔到不同的容器 或 分片 中,文檔可以儲存在一個或多個節點中
- 按集群節點來均衡分配這些分片,從而對索引和搜索過程進行負載均衡
- 復制每個分片以支持數據冗余,從而防止硬件故障導致的數據丟失
- 將集群中任一節點的請求路由到存有相關數據的節點
- 集群擴容時無縫整合新節點,重新分配分片以便從離群節點恢復
2、集群原理
ElasticSearch 的主旨是隨時可用和按需擴容。 而擴容可以通過購買性能更強大( 垂直擴容 ,或 縱向擴容) 或者數量更多的服務器( 水平擴容 ,或 橫向擴容 )來實現。
雖然 Elasticsearch 可以獲益於更強大的硬件設備,但是垂直擴容是有極限的。 真正的擴容能力是來自於水平擴容--為集群添加更多的節點,並且將負載壓力和穩定性分散到這些節點中。
對於大多數的數據庫而言,通常需要對應用程序進行非常大的改動,才能利用上橫向擴容的新增資源。 與之相反的是,ElastiSearch天生就是 分布式的 ,它知道如何通過管理多節點來提高擴容性和可用性。 這也意味着你的應用無需關注這個問題。
2.1、空集群
如果我們啟動了一個單獨的節點,里面不包含任何的數據和 索引,那我們的集群看起來就像下圖1一樣。
一個運行中的 Elasticsearch 實例稱為一個 節點,而集群是由一個或者多個擁有相同 cluster.name
配置的節點組成, 它們共同承擔數據和負載的壓力。當有節點加入集群中或者從集群中移除節點時,集群將會重新平均分布所有的數據。
當一個節點被選舉成為 主 節點時, 它將負責管理集群范圍內的所有變更,例如增加、刪除索引,或者增加、刪除節點等。 而主節點並不需要涉及到文檔級別的變更和搜索等操作,所以當集群只擁有一個主節點的情況下,即使流量的增加它也不會成為瓶頸。 任何節點都可以成為主節點。我們的示例集群就只有一個節點,所以它同時也成為了主節點。
作為用戶,我們可以將請求發送到 集群中的任何節點 ,包括主節點。 每個節點都知道任意文檔所處的位置,並且能夠將我們的請求直接轉發到存儲我們所需文檔的節點。 無論我們將請求發送到哪個節點,它都能負責從各個包含我們所需文檔的節點收集回數據,並將最終結果返回給客戶端。 Elasticsearch 對這一切的管理都是透明的。
2.2、集群健康
lasticsearch 的集群監控信息中包含了許多的統計數據,其中最為重要的一項就是 集群健康 , 它在 status
字段中展示為 green
、 yellow
或者 red
。
GET /_cluster/health
在一個不包含任何索引的空集群中,它將會有一個類似於如下所示的返回內容:
{ "cluster_name": "elasticsearch", "status": "green",
"timed_out": false, "number_of_nodes": 1, "number_of_data_nodes": 1, "active_primary_shards": 0, "active_shards": 0, "relocating_shards": 0, "initializing_shards": 0, "unassigned_shards": 0 }
status
字段指示着當前集群在總體上是否工作正常。它的三種顏色含義如下:
-
green
- 所有的主分片和副本分片都正常運行。
-
yellow
- 所有的主分片都正常運行,但不是所有的副本分片都正常運行。
-
red
- 有主分片沒能正常運行。
2.3、新增索引
我們往 Elasticsearch 添加數據時需要用到索引 —— 保存相關數據的地方。 索引實際上是指向一個或者多個物理分片 的邏輯命名空間 。
一個分片 是一個底層的工作單元 ,它僅保存了 全部數據中的一部分。 在分片內部機制
中,我們將詳細介紹分片是如何工作的,而現在我們只需知道一個分片是一個 Lucene 的實例,以及它本身就是一個完整的搜索引擎文件。 我們的文檔被存儲和索引到分片內,但是應用程序是直接與索引而不是與分片進行交互。
Elasticsearch 是利用分片將數據分發到集群內各處的。分片是數據的容器,文檔保存在分片內,分片又被分配到集群內的各個節點里。 當你的集群規模擴大或者縮小時, Elasticsearch 會自動的在各節點中遷移分片,使得數據仍然均勻分布在集群里。
一個分片可以是 主 分片或者 副本 分片。 索引內任意一個文檔都歸屬於一個主分片,所以主分片的數目決定着索引能夠保存的最大數據量。

技術上來說,一個主分片最大能夠存儲 Integer.MAX_VALUE - 128 個文檔,但是實際最大值還需要參考你的使用場景:包括你使用的硬件, 文檔的大小和復雜程度,索引和查詢文檔的方式以及你期望的響應時長。
一個副本分片只是一個主分片的拷貝。 副本分片作為硬件故障時保護數據不丟失的冗余備份,並為搜索和返回文檔等讀操作提供服務。
在索引建立的時候就已經確定了主分片數,但是副本分片數可以隨時修改。
讓我們在包含一個空節點的集群內創建名為 shops 的索引。 索引在默認情況下會被分配5個主分片, 但是為了演示目的,我們將分配3個主分片和一份副本(每個主分片擁有一個副本分片):
PUT /shops { "settings" : { "number_of_shards" : 3, "number_of_replicas" : 1 } }
我們的集群現在是圖 2 “擁有一個索引的單節點集群”。所有3個主分片都被分配在 Node 1
。
{ "cluster_name": "elasticsearch", "status": "yellow",

"timed_out": false, "number_of_nodes": 1, "number_of_data_nodes": 1, "active_primary_shards": 3, "active_shards": 3, "relocating_shards": 0, "initializing_shards": 0, "unassigned_shards": 3,

"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 }
集群的健康狀況為 yellow
則表示全部主 分片都正常運行(集群可以正常服務所有請求),但是副本 分片沒有全部處在正常狀態。 實際上,所有3個副本分片都是 unassigned
—— 它們都沒有被分配到任何節點。 在同一個節點上既保存原始數據又保存副本是沒有意義的,因為一旦失去了那個節點,我們也將丟失該節點上的所有副本數據。
當前我們的集群是正常運行的,但是在硬件故障時有丟失數據的風險。
2.4、故障轉移
當集群中只有一個節點在運行時,意味着會有一個單點故障問題——沒有冗余。 幸運的是,我們只需再啟動一個節點即可防止數據丟失。
如果啟動了第二個節點,我們的集群將會如圖 3 “擁有兩個節點的集群——所有主分片和副本分片都已被分配”所示。
當第二個節點加入到集群后,3個 副本分片 將會分配到這個節點上——每個主分片對應一個副本分片。 這意味着當集群內任何一個節點出現問題時,我們的數據都完好無損。
所有新近被索引的文檔都將會保存在主分片上,然后被並行的復制到對應的副本分片上。這就保證了我們既可以從主分片又可以從副本分片上獲得文檔。
cluster-health
現在展示的狀態為 green
,這表示所有6個分片(包括3個主分片和3個副本分片)都在正常運行。
{ "cluster_name": "elasticsearch", "status": "green",

"timed_out": false, "number_of_nodes": 2, "number_of_data_nodes": 2, "active_primary_shards": 3, "active_shards": 6, "relocating_shards": 0, "initializing_shards": 0, "unassigned_shards": 0, "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": 100 }
我們的集群現在不僅僅是正常運行的,並且還處於 始終可用 的狀態。
2.5、水平擴容
怎樣為我們的正在增長中的應用程序按需擴容呢? 當啟動了第三個節點,我們的集群將會看起來如圖 4 “擁有三個節點的集群——為了分散負載而對分片進行重新分配”所示。
Node 1
和 Node 2
上各有一個分片被遷移到了新的 Node 3
節點,現在每個節點上都擁有2個分片,而不是之前的3個。 這表示每個節點的硬件資源(CPU, RAM, I/O)將被更少的分片所共享,每個分片的性能將會得到提升。
分片是一個功能完整的搜索引擎,它擁有使用一個節點上的所有資源的能力。 我們這個擁有6個分片(3個主分片和3個副本分片)的索引可以最大擴容到6個節點,每個節點上存在一個分片,並且每個分片擁有所在節點的全部資源。
但是如果我們想要擴容超過6個節點怎么辦呢?
主分片的數目在索引創建時 就已經確定了下來。實際上,這個數目定義了這個索引能夠存儲 的最大數據量。(實際大小取決於你的數據、硬件和使用場景。) 但是,讀操作——搜索和返回數據——可以同時被主分片或 副本分片所處理,所以當你擁有越多的副本分片時,也將擁有越高的吞吐量。
在運行中的集群上是可以動態調整副本分片數目的 ,我們可以按需伸縮集群。讓我們把副本數從默認的 1
增加到 2
:
PUT /shops/_settings { "number_of_replicas" : 2 }
如圖 5 “將參數 number_of_replicas
調大到 2”所示, shops 索引現在擁有9個分片:3個主分片和6個副本分片。 這意味着我們可以將集群擴容到9個節點,每個節點上一個分片。相比原來3個節點時,集群搜索性能可以提升 3 倍。

當然,如果只是在相同節點數目的集群上增加更多的副本分片並不能提高性能,因為每個分片從節點上獲得的資源會變少。 你需要增加更多的硬件資源來提升吞吐量。
但是更多的副本分片數提高了數據冗余量:按照上面的節點配置,我們可以在失去2個節點的情況下不丟失任何數據。
2.6、應對故障
我們之前說過 Elasticsearch 可以應對節點故障,接下來讓我們嘗試下這個功能。 如果我們關閉第一個節點,這時集群的狀態為圖 6 “關閉了一個節點后的集群”
我們關閉的節點是一個主節點。而集群必須擁有一個主節點來保證正常工作,所以發生的第一件事情就是選舉一個新的主節點: Node 2
。
在我們關閉 Node 1
的同時也失去了主分片 1
和 2
,並且在缺失主分片的時候索引也不能正常工作。 如果此時來檢查集群的狀況,我們看到的狀態將會為 red
:不是所有主分片都在正常工作。
幸運的是,在其它節點上存在着這兩個主分片的完整副本, 所以新的主節點立即將這些分片在 Node 2
和 Node 3
上對應的副本分片提升為主分片, 此時集群的狀態將會為 yellow
。 這個提升主分片的過程是瞬間發生的,如同按下一個開關一般。
為什么我們集群狀態是 yellow
而不是 green
呢? 雖然我們擁有所有的三個主分片,但是同時設置了每個主分片需要對應2份副本分片,而此時只存在一份副本分片。 所以集群不能為 green
的狀態,不過我們不必過於擔心:如果我們同樣關閉了 Node 2
,我們的程序 依然 可以保持在不丟任何數據的情況下運行,因為 Node 3
為每一個分片都保留着一份副本。
如果我們重新啟動 Node 1
,集群可以將缺失的副本分片再次進行分配,那么集群的狀態也將如圖 5 “將參數 number_of_replicas
調大到 2”所示。 如果 Node 1
依然擁有着之前的分片,它將嘗試去重用它們,同時僅從主分片復制發生了修改的數據文件。
到目前為止,你應該對分片如何使得 Elasticsearch 進行水平擴容以及數據保障等知識有了一定了解