HM-SpringCloud微服務系列7.4【ES叢集】
1 叢集結構介紹
- 單機的elasticsearch做資料儲存,必然面臨兩個問題:海量資料儲存問題、單點故障問題。
ES叢集相關概念:
- 叢集(cluster):一組擁有共同的 cluster name 的 節點。
- 節點(node) :叢集中的一個 Elasticearch 例項
- 分片(shard):索引可以被拆分為不同的部分進行儲存,稱為分片。在叢集環境下,一個索引的不同分片可以拆分到不同的節點中,解決資料量太大,單點儲存量有限的問題。
- 主分片(Primary shard):相對於副本分片的定義。
- 副本分片(Replica shard)每個主分片可以有一個或者多個副本,資料和主分片一樣。
- 資料備份可以保證高可用,但是每個分片備份一份,所需要的節點數量就會翻一倍,成本實在是太高了!
- 為了在高可用和成本間尋求平衡,我們可以這樣做:
- 首先對資料分片,儲存到不同節點
- 然後對每個分片進行備份,放到對方節點,完成互相備份
- 這樣可以大大減少所需要的服務節點數量,如圖,我們以3分片,每個分片備份一份為例:
- 現在,每個分片都有1個備份,儲存在3個節點:
- node0:儲存了分片0和1
- node1:儲存了分片0和2
- node2:儲存了分片1和2
2 搭建ES叢集
計劃:利用3個docker容器模擬3個es的節點
2.1 部署ES叢集
部署es叢集可以直接使用docker-compose來完成,不過要求你的Linux虛擬機器至少有4G
然而虛擬機器最大允許3G,所以不改了,2G先搭建試試
首先編寫一個docker-compose檔案,內容如下:
點選檢視程式碼
version: '2.2' services: es01: image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1 container_name: es01 environment: - node.name=es01 - cluster.name=es-docker-cluster - discovery.seed_hosts=es02,es03 - cluster.initial_master_nodes=es01,es02,es03 - bootstrap.memory_lock=true - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ulimits: memlock: soft: -1 hard: -1 volumes: - data01:/usr/share/elasticsearch/data ports: - 9200:9200 networks: - elastic es02: image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1 container_name: es02 environment: - node.name=es02 - cluster.name=es-docker-cluster - discovery.seed_hosts=es01,es03 - cluster.initial_master_nodes=es01,es02,es03 - bootstrap.memory_lock=true - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ulimits: memlock: soft: -1 hard: -1 volumes: - data02:/usr/share/elasticsearch/data networks: - elastic es03: image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1 container_name: es03 environment: - node.name=es03 - cluster.name=es-docker-cluster - discovery.seed_hosts=es01,es02 - cluster.initial_master_nodes=es01,es02,es03 - bootstrap.memory_lock=true - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ulimits: memlock: soft: -1 hard: -1 volumes: - data03:/usr/share/elasticsearch/data networks: - elastic volumes: data01: driver: local data02: driver: local data03: driver: local networks: elastic: driver: bridge
上傳到虛擬機器root目錄下
es執行需要修改一些linux系統許可權,修改/etc/sysctl.conf檔案vi /etc/sysctl.conf
新增下面的內容:vm.max_map_count=262144
然後執行以下命令讓配置生效sysctl -p
通過docker-compose啟動叢集docker-compose up -d
原先的單es節點佔據了9200埠,現在停止一下
由於記憶體佔用過高,現將mq和kibana也停掉
ok
2.2 使用cerebro監控叢集狀態
kibana可以監控es叢集,不過新版本需要依賴es的x-pack 功能,配置比較複雜。
這裡推薦使用cerebro來監控es叢集狀態,官方網址:https://github.com/lmenezes/cerebro
課前資料已經提供了安裝包:
解壓即可使用,非常方便。
解壓好的目錄如下:
進入對應的bin目錄:
雙擊其中的cerebro.bat檔案即可啟動服務。
訪問http://localhost:9000即可進入管理介面:
輸入你的elasticsearch的任意節點的地址和埠(比如http://10.193.193.141:9200),點選connect即可:
綠色的條,代表叢集處於綠色(健康狀態)。
2.3 建立索引庫
2.3.1 方式1:利用kibana的DevTools建立索引庫
每個索引庫的分片數量、副本數量都是在建立索引庫時指定的,並且分片數量一旦設定以後無法修改。DSL語法如下:
PUT /itcast
{
"settings": {
"number_of_shards": 3, // 分片數量
"number_of_replicas": 1 // 副本數量
},
"mappings": {
"properties": {
// mapping對映定義 ...
}
}
}
因為kibana已經停掉了,所以此處我們採用方式2
2.3.2 方式2:利用cerebro建立索引庫
利用cerebro也可以建立索引庫:
填寫索引庫資訊,點選右下角的create按鈕:
提示建立成功後,手動返回overview
3 叢集腦裂問題
3.1 叢集職責劃分
- ES叢集節點有不同的職責劃分(節點角色)
- 預設情況下,叢集中的任何一個節點都同時具備上述四種角色。
- 但是真實的叢集一定要將叢集職責分離:
- master節點:對CPU要求高,但是記憶體要求第
- data節點:對CPU和記憶體要求都高
- coordinating節點:對網路頻寬、CPU要求高
- 職責分離可以讓我們根據不同節點的需求分配不同的硬體去部署。而且避免業務之間的互相干擾。
- ES中的每個節點角色都有自己不同的職責,因此建議叢集部署時,每個節點都有獨立的角色。
- 一個典型的es叢集職責劃分如圖(ES叢集的分散式查詢):
LB:load balance 負載均衡,比如nginx
三個協調節點
n個數據節點
一個master主節點,兩個備用,即從節點(萬一掛掉一個,可以從另兩個中選出一個來頂上)
3.2 腦裂問題
- 腦裂是因為叢集中的節點失聯導致的。
- 例如一個叢集中,主節點與其它節點失聯:
- 此時,node2和node3認為node1宕機,就會重新選主:
- 當node3當選後,叢集繼續對外提供服務,node2和node3自成叢集,node1自成叢集,兩個叢集資料不同步,出現數據差異。
- 當網路恢復後,因為叢集中有兩個master節點,叢集狀態的不一致,出現腦裂的情況:
- 解決腦裂的方案是,要求選票超過 ( eligible節點數量 + 1 )/ 2 才能當選為主,因此eligible節點數量最好是奇數。對應配置項是discovery.zen.minimum_master_nodes,在es7.0以後,已經成為預設配置,因此一般不會發生腦裂問題
- 例如:3個節點形成的叢集,選票必須超過 (3 + 1) / 2 ,也就是2票。node3得到node2和node3的選票,當選為主。node1只有自己1票,沒有當選。叢集中依然只有1個主節點,沒有出現腦裂。
3.3 小結
- master eligible節點的作用是什麼?
- 參與叢集選主
- 主節點可以管理叢集狀態、管理分片資訊、處理建立和刪除索引庫的請求
- data節點的作用是什麼?
- 資料的CRUD
- coordinator節點的作用是什麼?
- 路由請求到其它節點
- 合併查詢到的結果,返回給使用者
4 叢集分散式儲存
當新增文件時,應該儲存到不同分片,保證資料均衡,那麼coordinating node如何確定資料該儲存到哪個分片呢?
4.1 分片儲存測試
因為kibana未啟動,此處使用api測試工具進行新增文件測試
在9200,即es1節點插入了三條資料
9200查詢
點選檢視程式碼
{
"took": 16117,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 3,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_index": "itcast",
"_type": "_doc",
"_id": "5",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=5"
}
},
{
"_index": "itcast",
"_type": "_doc",
"_id": "3",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=3"
}
},
{
"_index": "itcast",
"_type": "_doc",
"_id": "1",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=1"
}
}
]
}
}
9201查詢、9202查詢也都能查出9200中插入的資料
點選檢視程式碼
{
"took": 393,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 3,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_index": "itcast",
"_type": "_doc",
"_id": "5",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=5"
}
},
{
"_index": "itcast",
"_type": "_doc",
"_id": "3",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=3"
}
},
{
"_index": "itcast",
"_type": "_doc",
"_id": "1",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=1"
}
}
]
}
}
點選檢視程式碼
{
"took": 1585,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 3,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_index": "itcast",
"_type": "_doc",
"_id": "5",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=5"
}
},
{
"_index": "itcast",
"_type": "_doc",
"_id": "3",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=3"
}
},
{
"_index": "itcast",
"_type": "_doc",
"_id": "1",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=1"
}
}
]
}
}
通過explain命令檢視資料到底被儲存到哪個分片上了
測試可以看到,三條資料分別在不同分片:
點選檢視程式碼
{
"took": 333,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 3,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_shard": "[itcast][0]",
"_node": "opfq11MgSRGyAdEW9ccG_A",
"_index": "itcast",
"_type": "_doc",
"_id": "5",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=5"
},
"_explanation": {
"value": 1.0,
"description": "*:*",
"details": []
}
},
{
"_shard": "[itcast][1]",
"_node": "opfq11MgSRGyAdEW9ccG_A",
"_index": "itcast",
"_type": "_doc",
"_id": "3",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=3"
},
"_explanation": {
"value": 1.0,
"description": "*:*",
"details": []
}
},
{
"_shard": "[itcast][2]",
"_node": "5D357h36SKS7oQHZ6yjzhQ",
"_index": "itcast",
"_type": "_doc",
"_id": "1",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=1"
},
"_explanation": {
"value": 1.0,
"description": "*:*",
"details": []
}
}
]
}
}
4.2 分片儲存原理
- elasticsearch會通過hash演算法來計算文件應該儲存到哪個分片:
- 說明:
- _routing預設是文件的id
- 演算法與分片數量有關,因此索引庫一旦建立,分片數量不能修改!
- 新增文件的流程如下:
- 解讀:
- 1)新增一個id=1的文件
- 2)對id做hash運算,假如得到的是2,則應該儲存到shard-2
- 3)shard-2的主分片在node3節點,將資料路由到node3
- 4)儲存文件
- 5)同步給shard-2的副本replica-2,在node2節點
- 6)返回結果給coordinating-node節點
5 叢集分散式查詢
elasticsearch的查詢分成兩個階段:
- scatter phase:分散階段,coordinating node會把請求分發到每一個分片
- gather phase:聚集階段,coordinating node彙總data node的搜尋結果,並處理為最終結果集返回給使用者
6 叢集故障轉移
叢集的master節點會監控叢集中的節點狀態,如果發現有節點宕機,會立即將宕機節點的分片資料遷移到其它節點,確保資料安全,這個叫做故障轉移。
-
例如一個叢集結構如圖:
現在,node1是主節點,其它兩個節點是從節點。 -
突然,node1發生了故障:
宕機後的第一件事,需要重新選主,例如選中了node2:
node2成為主節點後,會檢測叢集監控狀態,發現:shard-1、shard-0沒有副本節點。因此需要將node1上的資料遷移到node2、node3: -
演示
目前es02是主節點,現在手動將其停掉,模擬宕機
黃色條表示不健康
稍等一會,會自動故障轉移
雖然現在es02已經掛掉了,但9202查詢仍可用
點選檢視程式碼
{
"took": 47,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 3,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_index": "itcast",
"_type": "_doc",
"_id": "5",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=5"
}
},
{
"_index": "itcast",
"_type": "_doc",
"_id": "3",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=3"
}
},
{
"_index": "itcast",
"_type": "_doc",
"_id": "1",
"_score": 1.0,
"_source": {
"title": "試著插入一條 id=1"
}
}
]
}
}
現在重啟es02節點
再來檢視叢集狀態
稍等一下
再等一下
ok