Elasticsearch-02-入門:叢集、節點、分片、索引及常用API

PrimaBruceXu發表於2021-07-14

2. 基礎入門

2.1 重要概念

2.1.1 叢集和節點

1)cluster

Elasticsearch叢集是由一個或多個節點組成,通過其叢集名稱來進行唯一標識。節點在搜尋到叢集之後,通過判斷自身的 cluster.name 來決定是否加入該叢集

2)node

節點就是單個的Elasticsearch例項。在一般情況下,每個節點都在獨立的容器中執行。根據節點的作用,node具有如下的角色分配:

  • master-eligible node

    • 主節點負責輕量級群集範圍的操作,例如建立或刪除索引,跟蹤哪些節點是群集的一部分,並決定哪些分片分配給哪些節點。 群集健康是具有穩定主節點的重要性。只有具有主節點資格的、不是隻能投票的節點才有可能被選舉為主節點

    • 專職的主節點

      • 通常來說,master 是保證叢集穩定執行的關鍵。如果 master 身兼數職的話,就可能導致無法及時的管理叢集,從而導致叢集無法正常執行。這種情況在叢集規模較小的時候極少發生,一旦叢集規模擴大,一些專職的 master 就顯得十分必要。

      • 這麼設定可以建立一個專職的主節點

        node.roles: [ master ]
        
    • 只能投票的主節點

      • 該型別的節點只能投票,且無法被選舉成為主節點。(注意,只有具有master角色的節點才能被設定為voting_only

      • 這種角色通常被用於哪些你不想讓他成為主節點的節點,這些節點可能承擔了很多職責,從而不適合成為主節點。但為了有足夠的主節點參與投票,所以不得不出此下策。這是歷史遺留的問題,未來能不能修復我們不得而知

        node.roles: [ data, master, voting_only ]
        
    • 一個節點能成功當選master需要的票數為 N/2 + 1,其中 N 是具有主節點資格的節點數

      • 在ES7之前的版本中,這項配置是避免叢集腦裂的關鍵,但這往往難以配置,因為往叢集中加入一個新節點這個值可能就會發生變化。
      • 在ES7,我們就不需要這個配置,後面會詳細描述
  • data node

    • 資料節點儲存著分片。其主要職責是和資料相關的操作,如CRUD、搜尋、聚合等。因此資料節點對IO、記憶體、CPU等硬體資源要求較高。使用專職的資料節點有助於把管理和資料儲存分隔開,能更充分的利用系統資源

      node.roles: [ data ]
      
    • 資料節點還可以根據資料的使用頻率劃分為 hot data nodewarm data nodecold data node三種,資料的使用更新頻率依次降低,對硬體資源的需求也逐漸降低

      node.roles: [ data_hot/data_warm/data_cold ]
      
  • ingest node

    • ingest node 是用於資料的預處理操作,是預處理程式的組成部分之一

      node.roles: [ ingest ]
      
  • coordinating node

    • 一些類似於搜尋操作、批量索引操作等請求中可能會涉及到多個資料節點。協調節點的作用就是將這些跨節點的請求分發到具體的、正確的資料節點上。資料節點本地執行請求之後,再將操作結果返回給協調節點,由協調節點完成結果的組裝輸出。

    • 在預設情況下, 每個節點都具有協調這個角色。但在規模較大的叢集中,擁有專職的協調節點是非常有必要的,因為分發請求和生成最終的結果需要較多的記憶體空間和cpu資源。我們可以通過讓節點不具有任何角色來充當專職的協調節點

      node.roles: []
      

2.1.2 文件和索引

1)文件

​ 文件是ES搜尋的最小資料單元。簡單說,文件相當於關係型資料庫中的行。但他有具有一些關係型資料庫所沒有的特點

  • 文件是獨立的
  • 文件中還可以包含文件,可以套娃
  • 文件的結構非常靈活。不同於關係型資料庫,無需實現定義文件的結構,可以隨用隨加
2)索引

在ES中,索引是文件的集合,每個索引由一個或多個文件組成,並且這些文件可以分佈在不同的分片之中。這類似於關係型資料庫中的 database 。它只是一種邏輯名稱空間,內部包含的是 1~N 個主分片,0~N 個副本

2.1.3 分片

一個索引可以儲存海量的資料。比如一個具有10 億文件的索引佔據1 TB 的磁碟空間,而任一節點都沒有這麼大的磁碟空間或單個節點處理響應太慢。

為了解決這個問題,ES選擇將索引劃分為多分,這些劃分出來的部分就叫分片(shard)。ES會自動管理這些分片的分佈和排列,我們不用操心

有了分片,我們可以隨意擴充套件資料容量,可以在多個節點上進行並行操作,進一步提高了吞吐量

1)Primary shard

​ 索引可以劃分為一個或多個主分片。一旦主分片的數量被確定之後,就不能再修改。這是因為資料被索引在主分片上。如果要變更主分片的數量,那麼在需要重新索引資料,否則可能導致查詢失敗

2)Replica shard

副本就是主分片的備份,其數量可以隨意更改而不影響資料的儲存。使用副本有兩個目的:

  • 冗餘主分片,在主分片出現故障的時候頂上
  • 提供併發操作。副本分片也可以執行讀操作,這樣可以大大的提高系統的吞吐量

ES永遠不會在主分片所在節點上啟動副本

3)分片健康狀態

分片健康狀態就是分片的分配狀態,可以間接指示叢集當前工作狀態

  • 紅色:至少存在一個主分片未分配
  • 黃色:主分片均已分配,但至少一個副本為分配
  • 綠色:everything is good

2.1.4 搜尋原理

ES是通過封裝Lucene來實現其搜尋功能。Lucene採用一種叫倒排索引的技術來實現快速搜尋。

1)倒排索引

假設我們有兩個文件,每個文件的正文欄位如下:

  • The quick brown fox jumped over the lazy dog

  • Quick brown foxes leap over lazy dogs in summer

那麼倒排索引之後的結果如下:

image-20210705153127300

現在我們要搜尋 Quick brown只需要根據倒排索引之後的結果反向去查詢文件即可:

image-20210705153240715

兩個文件都匹配,如果我們僅從匹配數量來判斷相關性來說,文件1比文件2更符合我們預期的搜尋結果

2)分析

目前來說,我們的倒排索引存在著一些問題,如果解決了這些問題的話,我們的搜尋結果會更準確

  • Quickquick 以獨立的詞條出現,然而使用者可能認為它們是相同的詞。

  • foxfoxes 非常相似, 就像 dogdogs ;他們有相同的詞根。

  • jumpedleap, 儘管沒有相同的詞根,但他們的意思很相近。他們是同義詞

如果我們將詞條規範為標準模式,那麼我們可以找到與使用者搜尋的詞條不完全一致,但具有足夠相關性的文件。例如:

  • Quick 可以小寫化為 quick
  • foxes 可以 詞幹提取 --變為詞根的格式-- 為 fox 。類似的, dogs 可以為提取為 dog
  • jumpedleap 是同義詞,可以索引為相同的單詞 jump。

現在,我們的倒排索引像這樣

image-20210705155030806

到這一步,已經算是完成了50%,因為我們格式化了源資料,還需要對我們的查詢輸入做同樣的操作,這樣,才能完美的查詢

2.2 常用API

2.2.1 資訊API

請求 作用
GET /_cat/aliases 獲取別名資訊
GET /_cat/indices 獲取索引資訊
GET /_cat/shards 獲取索引的分片資訊
GET /_cat/nodes 獲取節點的基本資訊
GET /_cluster/settings 獲取叢集的設定資訊,只展示通過API修改過的設定
GET /_cluster/health 獲取叢集的健康狀態
GET /_cluster/state 獲取叢集的詳細資訊
GET /_cluster/stats 獲取叢集的簡略狀態資訊
GET /_nodes 獲取節點的資訊
GET /_nodes/stats 獲取節點的資訊

2.2.2 CRUD

1)操作文件
  • GET <index>/_doc/<_id>GET <index>/_source/<_id>

    • 獲取制定的文件ID的文件,使用 _doc 獲得的是全部文件內容,_source 值獲取正文內容
  • POST <index>/_doc/<_id>

    • 建立一個具有給定ID的文件,如果沒有給出ID,ES會自動分配一個
  • DELETE /<index>/_doc/<_id>

    • 刪除給出id的文件
  • POST /<index>/_delete_by_query

    • 根據條件刪除文件,請求體如下:
    {
      "query": {
        "match": {
          
        }
      }
    }
    
  • POST /<index>/_update/<_id>

    • 更新制定文件,請求體中為需要更新的內容
  • POST /_bulkPOST /<index>/_bulk

    • 批量操作,請求體格式如下

      { "index" : { "_index" : "test", "_id" : "1" } }	// 建立索引,下一行是需要建立索引的資料
      { "field1" : "value1" }
      { "delete" : { "_index" : "test", "_id" : "2" } }	// 刪除文件
      { "create" : { "_index" : "test", "_id" : "3" } } 	// 建立文件,下一行是文件的資料
      { "field1" : "value3" }
      { "update" : {"_id" : "1", "_index" : "test"} }		// 更新文件,下一行是資料
      { "doc" : {"field2" : "value2"} }
      
  • POST _reindex

    • 重構索引,請求體如下

      {
        "source": {
          "index": "my-index-000001"
        },
        "dest": {
          "index": "my-new-index-000001"
        }
      }
      
2)操作索引
  • 建立索引,如果索引已存在的話則更新

    PUT /<index>
    {
      "settings": {
        "index": {
          "number_of_shards": 3,  
          "number_of_replicas": 2 
        }
      },
      "mappings": {
        "properties": {
          "field1": { "type": "text" }
        }
      }
    }
    
  • 刪除索引:DELETE /<index>, 支援萬用字元

  • 獲取索引資訊:GET /<index>,支援萬用字元

  • 判斷索引是否存在:HEAD /<index>,支援萬用字元

  • 獲取索引對映:GET /<target>/_mapping

  • 更新索引對映

    PUT /<index>/_mapping
    {
      "properties": {
        "field": {
          "type": "keyword"
        }
      }
    }
    
  • 更新或建立別名:PUT /<index>/_alias/<alias>

  • 檢視別名:GET /<index>/_aliasGET /_alias

  • 刪除別名:DELETE /<index>/_aliases/<alias>

  • 更新索引設定:PUT /<index>/_settings,請求體為詳細索引設定

  • 檢視所有設定:GET /<index>/_settings

2.2.3 搜尋API

  • 常規搜尋:POST /<index>/_search,請求體如下

    // 簡單搜尋
    {
      "query": {
        "term": {
          "user.id": "kimchy"
        }
      },
      "explain": true, // 在結果中返回有關分數計算的詳細資訊
      ""
      "scroll" : "1m",	// 滾動搜尋資料量,以及上一次滾動搜尋的結果
      "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU
    1QQ=="
    }
    
    // 複雜查詢
    {
      "query": {
        "bool": {
          "must":     { "match": { "title": "quick" }},
          "must_not": { "match": { "title": "lazy"  }},
          "should": [
                      { "match": { "title": "brown" }},
                      { "match": { "title": "dog"   }}
          ]
        }
      }
    }
    

2.3 配置Elasticsearch

2.3.1 ES配置檔案

cluster.name: my-application	# 叢集名稱,

node.name: node-1	# 節點名稱
node.roles: [master, data, ingest, data_cold, data_warm, data_hot]	# 節點角色配置

path.data: /path/to/data	# 資料儲存路徑
path.logs: /path/to/logs	# 日誌儲存位置

bootstrap.memory_lock: true

network.host: 192.168.0.1	# 節點ip
http.port: 9200	# 節點對外訪問埠
transport.port: 9300	# 節點通訊埠

discovery.zen.minimum_master_nodes: 2	# 最小主節點數,ES7之前的版本必須合理配置,不然可能導致腦裂問題;ES7之後該配置項失效(無論配多少都不會生效)

# ES7之後的配置,initial_master_nodes的值要和node.name完全一致。在叢集啟動的時候,要有半數以上的initial_master_nodes存活才可以成功建立叢集。
discovery.seed_hosts: ["host1", "ip1", "ip2:port2"]	
cluster.initial_master_nodes: ["node-1", "node-2"]

2.3.2 重要的系統配置

1)禁用記憶體交換

大多數作業系統會盡可能將未使用的記憶體空間來作為檔案系統的快取,同時交換應用程式為使用的空間。記憶體交換會導致ES節點不穩定,因為這會影響GC的工作效率,從而導致節點無法響應

  • 作業系統層面

    sudo swapoff -a
    
    • 這會暫時禁用作業系統的記憶體交換。如果需要永久禁用,則需要修改/etc/fstab檔案
  • 應用層面

    bootstrap.memory_lock: true
    
    • 這個選項會讓ES在啟動的時候就鎖定分片的記憶體空間,不過要注意不能鎖定多於當前可用記憶體。

    • 這個選項出於某些原因,仍有可能失效,大多數情況下是ES沒有許可權鎖定記憶體,需要編輯 /etc/security/limits.conf檔案

      # allow user 'elasticsearch' mlockall
      elasticsearch soft memlock unlimited
      elasticsearch hard memlock unlimited
      
2)增加檔案控制程式碼

​ ES在執行的使用會使用大量的檔案控制程式碼,如果沒有足夠的檔案控制程式碼,則可能會造成資料丟失。ES最少需要65535個檔案控制程式碼

  • 系統命令

    ulimit -n 65535
    
  • 系統配置檔案

    # elasticsearch使用者最多可以使用65535個檔案
    elasticsearch  -  nofile  65535
    
3)確保能建立足夠的執行緒

​ ES在不同操作的情況下,使用不同大小的執行緒池。要確保ES在需要的時候能建立足夠多的執行緒,ES最少需要能建立4096個執行緒

  • 系統命令

    ulimit -n 4096
    
  • 系統配置檔案

    # elasticsearch使用者最多可以使用4096個執行緒
    elasticsearch soft nproc 4096
    elasticsearch hard nproc 4096
    

2.3.3 Heap大小設定

預設情況下,ES會自動根據節點的角色和總記憶體空間來自動設定堆空間(至少需要JDK14),但手動更改JVM堆大小也是十分常見的操作,修改 jvm.options 即可,但最大不要超過32GB

如果伺服器記憶體空間非常大的話,可以考慮執行多個ES例項而不是使用大記憶體空間

在單獨部署的情況下,建議給ES分配的堆空間為總記憶體空間的50%,剩餘的記憶體空間留給Lucene例項

  • 為什麼不建議給ES分配過多的記憶體空間?

    ES使用的JVM會使用一種叫壓縮物件指標的技術。其通過使用偏移量來表示記憶體空間地址,從而減少了指標的大小,節約了記憶體空間。

  • 關於壓縮指標,可以參考這篇文章:https://blog.csdn.net/liujianyangbj/article/details/108049482

2.4 原始碼環境構建

編譯和載入過程中可能會出現JDK版本不匹配的問題,手動替換JDK版本即可

  • 從GitHub上獲取ES原始碼:https://github.com/elastic/elasticsearch,同時準備一個對應發行版的ES

  • 用IDEA開啟ES原始碼,在 Preferences -> Build -> Build Tools -> Gradle 中設定好grade要使用的JDK,在 File -> Project Structure -> Project Settings -> Project -> Project SDK 中設定JDK版本

  • idea會自動匯入gradle專案,期間會聯網下載gradle。如果不想等待,可以在 專案資料夾/gradle/wrapper/gradle-wrapper.properties 中手動設定gradle的位置,從而跳過下載

    image-20210702183926159

    • 手動設定的話可能會出現sha256校驗失敗等問題,註釋相關配置即可
  • 等待gradle構建完成

  • 直接執行主啟動類:server/src/main/java/org.elasticsearch.bootstrap.Elasticsearch

  • 常見錯誤

    1. the system property [es.path.conf] must be set
    • 設定jvm,-Des.path.conf,value是發行版es的配置目錄,使用預設配置即可
    1. *path.home is not configured*
    • 設定jvm,-Des.path.home,value是發行版es的根目錄
    1. ERROR Could not register mbeans java.security.AccessControlException: access denied ("javax.management.MBeanTrustPermission" "register")
    • 設定jvm,-Dlog4j2.disable.jmx=true
    1. Unknown codebases [codebase.elasticsearch-plugin-classloader, codebase.elasticsearch, codebase.elasticsearch-secure-sm]
    • 註釋掉相應的檢查報錯異常,需要相應的jar包,而ide生成的class檔案,所以檢測不到
    1. Plugin [xxx] was built for Elasticsearch version xxx but version xxx is running
      • 外掛版本不對應,註釋掉相應檢查邏輯即可
  • 啟動成功後訪問:http://localhost:9200/測試

相關文章