前言
??在我們es系列文章開篇介紹中,已經提到,elasticsearch是天生支持集群的,他不需要依賴其他的服務發現和注冊的組件,如zookeeper這些,因為他內置了一個名字叫ZenDiscovery的模塊,是elasticsearch自己實現的一套用于節點發現和選主等功能的組件,所以elasticsearch做起集群來非常簡單,不需要太多額外的配置和安裝額外的第三方組件。
數據分片
??我們在前面的篇章中介紹過elasticsearch的一些重要的基本概念了,但是為了循序漸進的學習elasticsearch,我們并沒有把所有概念都一次性列出來,而是在學習的過程中不斷的補充,那現在我們需要學習elasticsearch的集群了,就需要補充一個基本概念,叫做“分片(shard)”,分片在集群中起到了非常關鍵的作用,那接下來,我們來看看分片是什么。
分片的概念和作用
??分片是一個底層的工作單元 ,它僅保存了全部數據中的一部分,一個分片是一個 Lucene 的實例,它本身就是一個完整的搜索引擎,我們的文檔被存儲和索引到分片內。
??每個分片又有一個副本分片,副本分片就是主分片的copy,對于分布式搜索引擎來說, 分片及副本的分配將是高可用及快速搜索響應的設計核心.主分片與副本都能處理查詢請求,它們的唯一區別在于只有主分片才能處理更新請求,這就有點像我們常見的“主從”的概念了。
??Elasticsearch 是利用分片將數據分發到集群內各處的。分片是數據的容器,文檔保存在分片內,分片又被分配到集群內的各個節點里。當你的集群規模擴大或者縮小時, Elasticsearch 會自動的在各節點中遷移分片,使得數據仍然均勻分布在集群里。所以集群和分片關系非常密切,學習集群之前,需要了解分片的概念和作用。
ES集群節點類型
??通過前面對elasticsearch的基本概念了解到,在一個elasticsearch集群(cluster)中,有多個節點(node),并且這些節點有不同的類型:我們部署一個elasticsearch集群常用的節點類型有:主節點,數據節點,候選主節點,客戶端節點。
這些節點的類型是通過在elasticsearch主配置文件path/to/config/elasticsearch.yml文件指定的,配置屬性如下:
node.master: true/false #該節點有機會成為master節點
node.data: true/false #該節點可以存儲數據
??通過以上兩行配置的組合,我們可以指定出elasticsearch的節點類型。
每種節點的作用如下:
1、主節點:
??主節點負責創建索引、刪除索引、分配分片、追蹤集群中的節點狀態等工作。Elasticsearch中的主節點的工作量相對較輕,用戶的請求可以發往集群中任何一個節點,由該節點負責分發和返回結果,而不需要經過主節點轉發。而主節點是由候選主節點通過ZenDiscovery機制選舉出來的,所以要想成為主節點,首先要先成為候選主節點。
2、候選主節點
??在elasticsearch集群初始化或者主節點宕機的情況下,由候選主節點中選舉其中一個作為主節點。指定候選主節點的配置為:node.master: true
。
??但是在elasticsearch集群中,會有偶爾出現這樣的一種現象,就是當主節點負載壓力過大,或者集中環境中的網絡問題,導致其他節點與主節點通訊的時候,主節點沒來的及響應,這樣的話,某些節點就認為主節點宕機,重新選擇新的主節點,這樣的話整個集群的工作就有問題了,比如我們集群中有10個節點,其中7個候選主節點,1個候選主節點成為了主節點,這種情況是正常的情況。但是如果現在出現了我們上面所說的主節點響應不及時,導致其他某些節點認為主節點宕機而重選主節點,那就有問題了,這剩下的6個候選主節點可能有3個候選主節點去重選主節點,最后集群中就出現了兩個主節點的情況,這種情況官方成為“腦裂現象”。
如果避免這種問題呢?主要從以下幾個方面來避免:
(1)盡量不要讓候選主節點同時作為數據節點,因為數據節點是需要承擔存儲和搜索的工作的,壓力會很大。所以如果該節點同時作為候選主節點和數據節點,那么一旦選上它作為主節點了,這時主節點的工作壓力將會非常大,出現腦裂現象的概率就增加了。
(2)配置主節點的響應時間,在默認情況下,主節點3秒沒有響應,其他節點就認為主節點宕機了,那我們可以把該時間設置的長一點,該配置是:discovery.zen.ping_timeout: 3
(3)配置候選主節點最小數量,從腦裂現象出現的原因中,我們看到了,如果集群中有部分候選主節點重新選擇主節點,那集群中的候選主節點就會被分成兩部分,導致集群的可用節點個數和實際的節點個數不一致,那這樣的話,我們就可用通過配置的方式,指定如果候選主節點個數達到某個值時,才能進行主節點選舉,而該配置屬性如下:discovery.zen.minimum_master_nodes
,該屬性默認值是1,官方的推薦值是(N/2)+1,其中N是候選主節點的數量,那這樣的話,比如我們集群有7個候選主節點,那么通過官方推薦值,我們應該設置為4,這樣的話,至少有4個候選主節點都認為需要重選主節點的情況下才進行選舉。
3、數據節點
??數據節點負責數據的存儲和相關具體操作,比如CRUD、搜索、聚合。所以,數據節點對機器配置要求比較高,首先需要有足夠的磁盤空間來存儲數據,其次數據操作對系統CPU、Memory和IO的性能消耗都很大。通常隨著集群的擴大,需要增加更多的數據節點來提高可用性。指定數據節點的配置:node.data: true
。
elasticsearch是允許一個節點既做候選主節點也做數據節點的,但是數據節點的負載較重,所以需要考慮將二者分離開,設置專用的候選主節點和數據節點,避免因數據節點負載重導致主節點不響應。
4、客戶端節點
??按照官方的介紹,客戶端節點就是既不做候選主節點也不做數據節點的節點,只負責請求的分發、匯總等等,但是這樣的工作,其實任何一個節點都可以完成,因為在elasticsearch中一個集群內的節點都可以執行任何請求,其會負責將請求轉發給對應的節點進行處理。所以單獨增加這樣的節點更多是為了負載均衡。指定該節點的配置為:
node.master: false
node.data: false
集群配置
接下來,我們做一個集群測試一下,我這里使用4個節點做測試,分別為:
es1 192.168.85.133:9300
es2 192,168.85.133:9500
es3 192.168.85.135:9300
es4 192.168.85.135:9500
1、es1 既是候選主節點也是數據節點,elasticsearch.yml配置如下:
cluster.name: elasticsearch #集群的名稱,同一個集群該值必須設置成相同的
node.name: es1 #該節點的名字
node.master: true #該節點有機會成為master節點
node.data: true #該節點可以存儲數據
network.bind_host: 0.0.0.0 #設置綁定的IP地址,可以是IPV4或者IPV6
network.publish_host: 192.168.85.133 #設置其他節點與該節點交互的IP地址
network.host: 192.168.85.133 #該參數用于同時設置bind_host和publish_host
transport.tcp.port: 9300 #設置節點之間交互的端口號
transport.tcp.compress: true #設置是否壓縮tcp上交互傳輸的數據
http.port: 9200 #設置對外服務的http端口號
http.max_content_length: 100mb #設置http內容的最大大小
http.enabled: true #是否開啟http服務對外提供服務
discovery.zen.minimum_master_nodes: 2 #設置這個參數來保證集群中的節點可以知道其它N個有master資格的節點。官方推薦(N/2)+1
discovery.zen.ping_timeout: 120s #設置集群中自動發現其他節點時ping連接的超時時間
discovery.zen.ping.unicast.hosts: ["192.168.85.133:9300","192.168.85.133:9500","192.168.85.135:9300"] #設置集群中的Master節點的初始列表,可以通過這些節點來自動發現其他新加入集群的節點
http.cors.enabled: true #跨域連接相關設置
http.cors.allow-origin: "*" #跨域連接相關設置
2、es2 既是候選主節點也是數據節點,elasticsearch.yml配置如下:
cluster.name: elasticsearch #集群的名稱,同一個集群該值必須設置成相同的
node.name: es2 #該節點的名字
node.master: true #該節點有機會成為master節點
node.data: true #該節點可以存儲數據
network.bind_host: 0.0.0.0 #設置綁定的IP地址,可以是IPV4或者IPV6
network.publish_host: 192.168.85.133 #設置其他節點與該節點交互的IP地址
network.host: 192.168.85.133 #該參數用于同時設置bind_host和publish_host
transport.tcp.port: 9500 #設置節點之間交互的端口號
transport.tcp.compress: true #設置是否壓縮tcp上交互傳輸的數據
http.port: 9400 #設置對外服務的http端口號
http.max_content_length: 100mb #設置http內容的最大大小
http.enabled: true #是否開啟http服務對外提供服務
discovery.zen.minimum_master_nodes: 2 #設置這個參數來保證集群中的節點可以知道其它N個有master資格的節點。官方推薦(N/2)+1
discovery.zen.ping_timeout: 120s #設置集群中自動發現其他節點時ping連接的超時時間
discovery.zen.ping.unicast.hosts: ["192.168.85.133:9300","192.168.85.133:9500","192.168.85.135:9300"] #設置集群中的Master節點的初始列表,可以通過這些節點來自動發現其他新加入集群的節點
http.cors.enabled: true #跨域連接相關設置
http.cors.allow-origin: "*" #跨域連接相關設置
3、es3 是候選主節點,elasticsearch.yml配置如下:
cluster.name: elasticsearch #集群的名稱,同一個集群該值必須設置成相同的
node.name: es3 #該節點的名字
node.master: true #該節點有機會成為master節點
node.data: false #該節點可以存儲數據
network.bind_host: 0.0.0.0 #設置綁定的IP地址,可以是IPV4或者IPV6
network.publish_host: 192.168.85.135 #設置其他節點與該節點交互的IP地址
network.host: 192.168.85.135 #該參數用于同時設置bind_host和publish_host
transport.tcp.port: 9300 #設置節點之間交互的端口號
transport.tcp.compress: true #設置是否壓縮tcp上交互傳輸的數據
http.port: 9200 #設置對外服務的http端口號
http.max_content_length: 100mb #設置http內容的最大大小
http.enabled: true #是否開啟http服務對外提供服務
discovery.zen.minimum_master_nodes: 2 #設置這個參數來保證集群中的節點可以知道其它N個有master資格的節點。官方推薦(N/2)+1
discovery.zen.ping_timeout: 120s #設置集群中自動發現其他節點時ping連接的超時時間
discovery.zen.ping.unicast.hosts: ["192.168.85.133:9300","192.168.85.133:9500","192.168.85.135:9300"] #設置集群中的Master節點的初始列表,可以通過這些節點來自動發現其他新加入集群的節點
http.cors.enabled: true #跨域連接相關設置
http.cors.allow-origin: "*" #跨域連接相關設置
4、es4 是數據節點,elasticsearch.yml配置如下:
cluster.name: elasticsearch #集群的名稱,同一個集群該值必須設置成相同的
node.name: es4 #該節點的名字
node.master: false #該節點有機會成為master節點
node.data: true #該節點可以存儲數據
network.bind_host: 0.0.0.0 #設置綁定的IP地址,可以是IPV4或者IPV6
network.publish_host: 192.168.85.135 #設置其他節點與該節點交互的IP地址
network.host: 192.168.85.135 #該參數用于同時設置bind_host和publish_host
transport.tcp.port: 9500 #設置節點之間交互的端口號
transport.tcp.compress: true #設置是否壓縮tcp上交互傳輸的數據
http.port: 9400 #設置對外服務的http端口號
http.max_content_length: 100mb #設置http內容的最大大小
http.enabled: true #是否開啟http服務對外提供服務
discovery.zen.minimum_master_nodes: 2 #設置這個參數來保證集群中的節點可以知道其它N個有master資格的節點。官方推薦(N/2)+1
discovery.zen.ping_timeout: 120s #設置集群中自動發現其他節點時ping連接的超時時間
discovery.zen.ping.unicast.hosts: ["192.168.85.133:9300","192.168.85.133:9500","192.168.85.135:9300"] #設置集群中的Master節點的初始列表,可以通過這些節點來自動發現其他新加入集群的節點
http.cors.enabled: true #跨域連接相關設置
http.cors.allow-origin: "*" #跨域連接相關設置
??以上配置就是我們集群中4個elasticsearch節點的配置,我們有兩個既是候選主節點也是數據節點,一個候選主節點,一個數據節點,沒有配置客戶端節點,因為客戶端節點僅起到負載均衡的作用。
驗證群狀態
查看集群狀態的REST API
我們逐一的把集群中的節點啟動起來,然后使用REST API查看集群狀態,訪問集群中任意一個節點即可:
GET ip:port/_cluster/health
響應的結果:
{
"cluster_name" : "elasticsearch",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 4,
"number_of_data_nodes" : 3,
"active_primary_shards" : 46,
"active_shards" : 92,
"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.0
}
我們來看看返回結果中的幾個關鍵的屬性:
- 集群狀態有3種(status):
red,表示有主分片沒有分配,某些數據不可用。
yellow,表示主分片都已分配,數據都可用,但是有副本分片沒有分配。
green,表示主分片和副本分片都已分配,一切正常。
那么通過以上返回的結果中的status屬性,我們可以知道,當前的elasticsearch集群是正常的。 - number_of_nodes屬性說明我們集群中有4個節點。
- number_of_data_nodes說明我們集群中有3個為數據節點,所有數據分片儲存在這3個數據節點中。
- active_primary_shards屬性說明主分片有46個(我們有10個索引,其中9個索引有5個主分片,1個索引有1個主分片,合計46個主分片)
- active_shards屬性說明所有分片(主分片 + 副本分片)總共是92個
使用head插件管理ES集群
??我們在之前安裝了es-head插件的時候提到,es-head插件可以管理我們elasticsearch集群的,那現在我們就使用之前安裝好的es-head插件,來圖形化界面的方式管理elasticsearch集群。
打開首頁我們可以看到以下界面:
??連接任意一個節點看到的結果都是一樣的,這個就驗證了我們上面提到的概念:用戶的請求可以發往集群中任何一個節點,由該節點負責請求分發和返回結果。
- 在該界面中,是以表格的形式展示數據的,縱向的是集群中的索引,橫向的是集群中的節點。
- 橫向有4行,分別是4個我們自己命名的節點,其中標記了es1是星號,代表它為主節點。
- 然后我們可以看到,每個單元格中都有一些綠色的小方塊,小方塊中標有數字,這些綠色小方塊就代表分片,其中邊框加粗的是主分片,沒加粗的是副本分片。
- 我們在創建索引時,如果沒有指定分片的數量,那么默認是5個,再加上每個分片都有一個副本,所以加起來就有10個綠色小方塊了。
- 每個綠色小方塊上都有0-4的數字,分別代表0-4號分片,和對應的0-4號分片的副本分片,我們點擊綠色小方塊,會彈出一個窗口,列出該分片的一些信息,其中有條信息是“primary”,如果為true,就代表主分片,為false就代表副本分片。
- 其中es3節點是沒有給它分配分片的,那是因為當時我們給該節點設置的是候選主節點,不是數據節點,所以他不承擔數據存儲的工作。
- 這10個分片(包含主分片和副本分片)被elasticsearch分配到各個數據節點中,并且根據我們上面對分片概念的了解,主分片和副本分片都可以接收數據查詢請求,但是只有主分片才能接收數據修改請求,副本分片的數據是從主分片同步過來的。
測試數據
??最后,我們從用戶的角度上來看看,向集群中各個節點發送一個查詢請求,測試數據的一致性。
我們先往集群中新增一條數據,以store索引作為測試,向任意一個節點發送一個新增文檔的請求:
PUT /store/employee/3
{
"name":"王五",
"age":28,
"about":"我叫王五,但不是隔壁老王",
"interests":["code","music"]
}
看到以下返回結果提示添加成功:
??但這里有一個問題,就是該文檔會被存儲到哪一個主分片中呢?首先這肯定不會是隨機的,否則將來要獲取文檔的時候我們就不知道從何處尋找了。實際上,這個過程是根據下面這個公式決定的:
shard = hash(routing) % number_of_primary_shards
routing
是一個可變值,默認是文檔的 _id
,也可以設置成一個自定義的值。 routing
通過 hash 函數生成一個數字,然后這個數字再除以 number_of_primary_shards
(主分片的數量)后得到 余數 。這個分布在 0
到 number_of_primary_shards-1
之間的余數,就是我們所尋求的文檔所在分片的位置。
這就順便解釋了為什么我們要在創建索引的時候就確定好主分片的數量,并且永遠不會改變這個數量,因為如果數量變化了,那么所有之前路由的值都會無效,文檔也再也找不到了。
那接下來我們看看每個節點是否可以查詢到該數據:
??這4個節點都是可以查詢到剛剛我們新增的文檔id為3的數據,集群正常運作。在我們整個集群配置的過程中,需要我們配置的東西很少,所以elasticsearch真的可以很輕松的完成一個規模超大的集群,并且在該集群中存儲海量的數據。