作者:劉曉國
當我們開始使用 Elasticsearch 時,我們必須理解其中的一些重要的概念。這些概念的理解對於以後我們使用 Elastic Stack 是非常重要的。在今天的這篇文章裡,我們先來介紹一下在 Elastic Stack 中最重要的一些概念。
首先,我們來看下一下如下的這個圖:
Cluster
Cluster 也就是集群的意思。
Elasticsearch 集群由一個或多個節點組成,可通過其集群名稱進行標識。通常這個 Cluster 的名字是可以在 Elasticsearch 裡的配置文件中設置的。在默認的情況下,如我們的 Elasticsearch 已經開始運行,那麼它會自動生成一個叫做 “Elasticsearch” 的集群。我們可以在 config/elasticsearch.yml 裡定製我們的集群的名字:
一個 Elasticsearch 的集群就像是下面的一個佈局:
帶有 NginX 代理及 Balancer 的架構圖是這樣的:
我們可以通過:
GET _cluster/state
來獲取整個 cluster 的狀態。這個狀態只能被 master node 所改變。上面的接口返回的結果是:
{
"cluster_name": "elasticsearch",
"compressed_size_in_bytes": 1920,
"version": 10,
"state_uuid": "rPiUZXbURICvkPl8GxQXUA",
"master_node": "O4cNlHDuTyWdDhq7vhJE7g",
"blocks": {},
"nodes": {...},
"metadata": {...},
"routing_table": {...},
"routing_nodes": {...},
"snapshots": {...},
"restore": {...},
"snapshot_deletions": {...}
}
Node
單個 Elasticsearch 實例。
在大多數環境中,每個節點都在單獨的盒子或虛擬機上運行。一個集群由一個或多個 node 組成。在測試的環境中,我可以把多個 node 運行在一個 server 上。在實際的部署中,大多數情況還是需要一個 server 上運行一個 node。
根據 node 的作用,可以分為如下的幾種:
- master-eligible:可以作為主 node。一旦成為主 node,它可以管理整個 cluster 的設置及變化:創建,更新,刪除 index;添加或刪除 node;為 node 分配 shard
- data:數據 node
- ingest: 數據接入(比如 pipepline)
- machine learning (Gold/Platinum License)
一般來說,一個 node 可以具有上面的一種或幾種功能。我們可以在命令行或者 Elasticsearch 的配置文件(Elasticsearch.yml)來定義:
Node類型 | 配置參數 | 默認值 |
---|---|---|
master-eligible | node.master | true |
data | node.data | true |
ingest | node.ingest | true |
machine learning | node.ml | true (除了OSS發佈版) |
你也可以讓一個 node 做專有的功能及角色。如果上面 node 配置參數沒有任何配置,那麼我們可以認為這個 node 是作為一個 coordination node。在這種情況下,它可以接受外部的請求,並轉發到相應的節點來處理。針對 master node,有時我們需要設置 cluster.remote.connect: false。
在實際的使用中,我們可以把請求發送給 data 節點,而不能發送給 master 節點。
我們可以通過對 config/elasticsearch.yml 文件中配置來定義一個 node 在集群中的角色:
在有些情況中,我們可以通過設置 node.voting_only 為 true 從而使得一個 node 在 node.master 為真的情況下,只作為參加 voting 的功能,而不當選為 master node。這種情況為了避免腦裂情況發生。它通常可以使用一個 CPU 性能較低的 node 來擔當。
在一個集群中,我們可以使用如下的一個命令來獲取當前可以進行 vote 的所有 master-eligible 節點:
GET /_cluster/state?filter_path=metadata.cluster_coordination.last_committed_config
你可能獲得類似如下列表的結果:
{
"metadata" : {
"cluster_coordination" : {
"last_committed_config" : [
"Xe6KFUYCTA6AWRpbw84qaQ",
"OvD79L1lQme1hi06Ouiu7Q",
"e6KF9L1lQUYbw84CTAemQl"
]
}
}
}
在整個 Elastic 的架構中,Data Node 和 Cluster 的關係表述如下:
上面的定義適用於 Elastic Stack 7.9 發佈版以前。在 Elastic Stack 7.9 之後,有了新的改進。
請詳細閱讀文章 “Elasticsearch:Node roles 介紹 - 7.9 之後版本”:https://elasticstack.blog.csdn.net/article/details/110947372
Document
Elasticsearch 是面向文檔的,這意味著您索引或搜索的最小數據單元是文檔。
文檔在 Elasticsearch 中有一些重要的屬性:
- 它是獨立的。文檔包含字段(名稱)及其值。
- 它可以是分層的。可以將其視為文檔中的文檔。字段的值可以很簡單,就像位置字段的值可以是字符串一樣。它還可以包含其他字段和值。例如,位置字段可能包含城市和街道地址。
- 結構靈活。您的文檔不依賴於預定義的架構。例如,並非所有事件都需要描述值,因此可以完全省略該字段。但它可能需要新的字段,例如位置的緯度和經度。
文檔通常是數據的 JSON 表示形式。JSON over HTTP 是與 Elasticsearch 進行通信的最廣泛使用的方式,它是我們在本書中使用的方法。
例如,您的聚會網站中的事件可以在以下文檔中表示:
{
"name": "Elasticsearch Denver",
"organizer": "Lee",
"location": "Denver, Colorado, USA"
}
很多人認為 Document 相比較於關係數據庫,它相應於其中每個 record。
Type
類型是文檔的邏輯容器,類似於表是行的容器。
您將具有不同結構(模式)的文檔放在不同類型中。 例如,你可以使用一種類型來定義聚合組,並在人們聚集時為事件定義另一種類型。
每種類型的字段定義稱為映射。 例如,name 將映射為字符串,但 location 下的 geolocation 字段將映射為特殊的 geo_point 類型。 (我們探討如何使用附錄A中的地理空間數據。)每種字段的處理方式都不同。 例如,你在名稱字段中搜索單詞,然後按位置搜索組以查找位於您居住地附近的組。
很多人認為 Elasticsearch 是 schema-less 的。大家都甚至認為 Elasticsearch 中的數據庫是不需要 mapping 的。其實這是一個錯誤的概念。schema-less 在 Elasticsearch 中正確的理解是,我們不需要事先定義一個類型關係數據庫中的 table 才使用數據庫。
在 Elasticsearch 中,我們開始可以不定義一個 mapping,而直接寫入到我們指定的 index 中。這個 index 的 mapping 是動態生成的 (當然我們也可以禁止這種行為)。其中的數據項的每一個數據類型是動態識別的。比如時間,字符串等,雖然有些數據類型,還是需要我們手動調整,比如 geo_point 等地理位置數據。
另外,它還有一個含義,同一個 type,我們在以後的數據輸入中,可能增加新的數據項,從而生產新的 mapping。這個也是動態調整的。
Elasticsearch 具有 schema-less 的能力,這意味著無需顯式指定如何處理文檔中可能出現的每個不同字段,即可對文檔建立索引。 啟用動態映射後,Elasticsearch 自動檢測並向索引添加新字段。 這種默認行為使索引和瀏覽數據變得容易-只需開始建立索引文檔,Elasticsearch 就會檢測布爾值,浮點數和整數值,日期和字符串,並將其映射到適當的 Elasticsearch 數據類型。
由於一些原因,在 Elasticsearch 6.0 以後,一個 Index 只能含有一個 type。這其中的原因是:相同 index 的不同映射 type 中具有相同名稱的字段是相同; 在 Elasticsearch 索引中,不同映射 type 中具有相同名稱的字段在 Lucene 中被同一個字段支持。在默認的情況下是 _doc。在未來8.0的版本中,type 將被徹底刪除。
Index
在 Elasticsearch 中,索引是文檔的集合。
每個 Index 一個或許多的 documents 組成,並且這些 document 可以分佈於不同的 shard 之中。
很多人認為 index 類似於關係數據庫中的 database。這中說法是有些道理,但是並不完全相同。其中很重要的一個原因是,在Elasticsearch 中的文檔可以有 object 及 nested 結構。一個 index 是一個邏輯命名空間,它映射到一個或多個主分片,並且可以具有零個或多個副本分片。
每當一個文檔進來後,根據文檔的 id 會自動進行 hash 計算,並存放於計算出來的 shard 實例中,這樣的結果可以使得所有的 shard 都比較有均衡的存儲,而不至於有的 shard 很忙。
shard_num = hash(_routing) % num_primary_shards
在默認的情況下,上面的 _routing 既是文檔的 _id。如果有 routing 的參與,那麼這些文檔可能只存放於一個特定的 shard,這樣的好處是對於一些情況,我們可以很快地綜合我們所需要的結果而不需要跨 node 去得到請求。比如針對 join 的數據類型。
從上面的公式我們也可以看出來,我們的 shard 數目是不可以動態修改的,否則之後也找不到相應的 shard 號碼了。必須指出的是,replica 的數目是可以動態修改的。
Shard
由於 Elasticsearch 是一個分佈式搜索引擎,因此索引通常會拆分為分佈在多個節點上的稱為分片的元素。 Elasticsearch 自動管理這些分片的排列。 它還根據需要重新平衡分片,因此用戶無需擔心細節。
一個索引可以存儲超出單個結點硬件限制的大量數據。比如,一個具有 10 億文檔的索引佔據1TB 的磁盤空間,而任一節點都沒有這樣大的磁盤空間;或者單個節點處理搜索請求,響應太慢。
為了解決這個問題,Elasticsearch 提供了將索引劃分成多份的能力,這些份就叫做分片(shard)。當你創建一個索引的時候,你可以指定你想要的分片 (shard) 的數量。每個分片本身也是一個功能完善並且獨立的“索引”,這個“索引”可以被放置到集群中的任何節點上。
分片之所以重要,主要有兩方面的原因:
- 允許你水平分割/擴展你的內容容量
- 允許你在分片(潛在地,位於多個節點上)之上進行分佈式的、並行的操作,進而提高性能/吞吐量
有兩種類型的分片:Primary shard 和 Replica shard。
- Primary shard: 每個文檔都存儲在一個Primary shard。 索引文檔時,它首先在 Primary shard上編制索引,然後在此分片的所有副本上(replica)編制索引。索引可以包含一個或多個主分片。 此數字確定索引相對於索引數據大小的可伸縮性。 創建索引後,無法更改索引中的主分片數。
-
Replica shard: 每個主分片可以具有零個或多個副本。 副本是主分片的副本,有兩個目的:
- 增加故障轉移:如果主要故障,可以將副本分片提升為主分片
- 提高性能:get 和 search 請求可以由主 shard 或副本 shard 處理。
默認情況下,每個主分片都有一個副本,但可以在現有索引上動態更改副本數。 永遠不會在與其主分片相同的節點上啟動副本分片。
下面的圖表示的是一個 index 有5個 shard 及1個 replica
這些 Shard 分佈於不同的物理機器上:
我們可以為每個 Index 設置相應的 Shard 數值:
curl -XPUT http://localhost:9200/another_user?pretty -H 'Content-Type: application/json' -d '
{
"settings" : {
"index.number_of_shards" : 2,
"index.number_of_replicas" : 1
}
}
比如在上面的 REST 接口中,我們為 another_user 這個 index 設置了2個 shards,並且有一個 replica。一旦設置好 primary shard 的數量,我們就不可以修改了。這是因為 Elasticsearch 會依據每個 document 的 id 及 primary shard 的數量來把相應的 document 分配到相應的 shard 中。如果這個數量以後修改的話,那麼每次搜索的時候,可能會找不到相應的 shard。
我們可以通過如下的接口來查看我們的 index 中的設置:
curl -XGET http://localhost:9200/twitter/_settings?pretty
上面我們可以得到 twitter index 的設置信息:
{
"twitter" : {
"settings" : {
"index" : {
"creation_date" : "1565618906830",
"number_of_shards" : "1",
"number_of_replicas" : "1",
"uuid" : "rwgT8ppWR3aiXKsMHaSx-w",
"version" : {
"created" : "7030099"
},
"provided_name" : "twitter"
}
}
}
}
Replica
默認情況下,Elasticsearch 為每個索引創建一個主分片和一個副本。這意味著每個索引將包含一個主分片,每個分片將具有一個副本。
分配多個分片和副本是分佈式搜索功能設計的本質,提供高可用性和快速訪問索引中的文檔。主副本和副本分片之間的主要區別在於,只有主分片可以接受索引請求。副本和主分片都可以提供查詢請求。
在上圖中,我們有一個 Elasticsearch 集群,由默認分片配置中的兩個節點組成。 Elasticsearch 自動排列分割在兩個節點上的一個主分片。有一個副本分片對應於每個主分片,但這些副本分片的排列與主分片的排列完全不同。
請允許我們澄清一下:請記住,number_of_shards 值與索引有關,而不是與整個群集有關。此值指定每個索引的分片數(不是群集中的主分片總數)。
我們可以通過如下的接口來獲得一個 index 的健康情況:
http://localhost:9200/_cat/indices/twitter
上面的接口可以返回如下的信息:
更進一步的查詢,我們可以看出:
如果一個 index 顯示的是紅色,表面這個 index 至少有一個 primary shard 沒有被正確分配,並且有的 shard 及其相應的 replica 已經不能正常訪問。 如果是綠色,表明 index 的每一個 shard 都有備份 (replica),並且其備份也成功複製在相應的 replica shard 之中。如果其中的一個 node 壞了,相應的另外一個 node 的 replica 將起作用,從而不會造成數據的丟失。
shard 健康
- 紅色:集群中未分配至少一個主分片
- 黃色:已分配所有主副本,但未分配至少一個副本
- 綠色:分配所有分片