1. 程式人生 > 其它 >HM-SpringCloud微服務系列7.4【ES叢集】

HM-SpringCloud微服務系列7.4【ES叢集】

1 叢集結構介紹

  • 單機的elasticsearch做資料儲存,必然面臨兩個問題:海量資料儲存問題、單點故障問題。

ES叢集相關概念:

  • 叢集(cluster):一組擁有共同的 cluster name 的 節點。
  • 節點(node) :叢集中的一個 Elasticearch 例項
  • 分片(shard):索引可以被拆分為不同的部分進行儲存,稱為分片。在叢集環境下,一個索引的不同分片可以拆分到不同的節點中,解決資料量太大,單點儲存量有限的問題。
  • 主分片(Primary shard):相對於副本分片的定義。
  • 副本分片(Replica shard)每個主分片可以有一個或者多個副本,資料和主分片一樣。

  1. 資料備份可以保證高可用,但是每個分片備份一份,所需要的節點數量就會翻一倍,成本實在是太高了!
  2. 為了在高可用和成本間尋求平衡,我們可以這樣做:
    • 首先對資料分片,儲存到不同節點
    • 然後對每個分片進行備份,放到對方節點,完成互相備份
  3. 這樣可以大大減少所需要的服務節點數量,如圖,我們以3分片,每個分片備份一份為例:
  4. 現在,每個分片都有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 叢集職責劃分

  1. ES叢集節點有不同的職責劃分(節點角色)
  2. 預設情況下,叢集中的任何一個節點都同時具備上述四種角色。
  3. 但是真實的叢集一定要將叢集職責分離:
    • master節點:對CPU要求高,但是記憶體要求第
    • data節點:對CPU和記憶體要求都高
    • coordinating節點:對網路頻寬、CPU要求高
  4. 職責分離可以讓我們根據不同節點的需求分配不同的硬體去部署。而且避免業務之間的互相干擾。
  5. ES中的每個節點角色都有自己不同的職責,因此建議叢集部署時,每個節點都有獨立的角色。
  6. 一個典型的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 小結

  1. master eligible節點的作用是什麼?
    • 參與叢集選主
    • 主節點可以管理叢集狀態、管理分片資訊、處理建立和刪除索引庫的請求
  2. data節點的作用是什麼?
    • 資料的CRUD
  3. 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 分片儲存原理

  1. elasticsearch會通過hash演算法來計算文件應該儲存到哪個分片:
  2. 說明:
    • _routing預設是文件的id
    • 演算法與分片數量有關,因此索引庫一旦建立,分片數量不能修改!
  3. 新增文件的流程如下:
  4. 解讀:
    • 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節點會監控叢集中的節點狀態,如果發現有節點宕機,會立即將宕機節點的分片資料遷移到其它節點,確保資料安全,這個叫做故障轉移。

  1. 例如一個叢集結構如圖:

    現在,node1是主節點,其它兩個節點是從節點。

  2. 突然,node1發生了故障:

    宕機後的第一件事,需要重新選主,例如選中了node2:

    node2成為主節點後,會檢測叢集監控狀態,發現:shard-1、shard-0沒有副本節點。因此需要將node1上的資料遷移到node2、node3:

  3. 演示

    目前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