Elasticsearch Document

1.  基本概念回顧html

1.1.  Nodenode

節點是一個服務器,它是集羣的一部分,存儲數據,並參與集羣的索引和搜索功能算法

節點有一個名稱標識,該名稱在缺省狀況下是在啓動時分配給節點的隨機全局唯一標識符(UUID)json

這個名稱對於管理很是重要,由於你但願識別網絡中的哪些服務器與Elasticsearch集羣中的哪些節點相對應api

默認狀況下,每一個節點都被設置爲鏈接一個名爲elasticsearch的集羣,這意味着若是您在網絡上啓動多個節點,而且假設它們能夠發現彼此,那麼它們都會自動造成並鏈接一個名爲elasticsearch的集羣。數組

1.2.  Index服務器

文檔(document)的集合就是索引(Index)網絡

1.3.  Type併發

當你想要在同一個index中存儲不一樣類型的documents時,type用做這個index的一個邏輯分類/分區。好比,在一個索引中,用戶數據是一個type,帖子是另外一個type。在後續的版本中,一個index將再也不容許建立多個types,並且整個types這個概念都將被刪除。app

(PS:type是index的一個邏輯分類(或者叫分區),在當前的版本中,它仍然用於在一個索引下區分不一樣類型的數據。可是,不建議這樣作,由於在後續的版本中type這個概念將會被移除,也不容許一個索引中有多個類型。)

1.4.  Document

一個document就是index中的一條記錄,它是JSON格式的

1.5.  Shards & Replicas (分片與副本)

索引可能存儲大量數據,這些數據可能超過單個節點的硬件限制。例如,一個包含10億個文檔、佔用1TB磁盤空間的索引可能不適用於單個節點的磁盤,或者速度太慢,沒法單獨處理來自單個節點的搜索請求。爲了解決這個問題,Elasticsearch提供了將索引細分爲多個碎片的功能,每一個碎片稱之爲 shard。

建立索引時,您能夠簡單地定義所需的分片數量。每一個分片自己就是一個功能完整且獨立的「index」,能夠駐留在集羣中的任何節點上。

分片之因此重要,主要有兩個緣由:

  • 容許水平擴容
  • 容許分佈式存儲和並行操做,從而提升性能/吞吐量

shard如何分佈以及如何將其文檔聚合回搜索請求的機制徹底由Elasticsearch管理,對於用戶來講是透明的。

副本指的是分片的副本,是shard的複製

複製之因此重要,主要有兩個緣由:

  • 在shard/node失敗的時候,它提供高可用性。正由於如此,複製的shard(簡稱shard的副本)毫不會跟原始shard在同一個節點上
  • 它容許擴展搜索量/吞吐量,由於搜索能夠並行地在全部副本上執行

(It provides high availability in case a shard/node fails. For this reason, it is important to note that a replica shard is never allocated on the same node as the original/primary shard that it was copied from.)

總而言之,每一個索引能夠被分紅多個碎片

索引還能夠被複制0次(即沒有副本)或更屢次

複製以後,每一個索引都將擁有主分片(原始分片)和 副本分片(主分片的副本)

1.6.  小結 & 回顧:

  1. node是一臺服務器,表示集羣中的節點
  2. document表示索引記錄
  3. 一個index中不建議定義多個type
  4. 一個index能夠有多個shard,每一個shard能夠有0個或多個副本
  5. original shard (原始shard,或者叫 primary shard)的複製成爲副本shard,簡稱shard
  6. 主分片和副本決不會在同一個節點上
  7. 分片的好處主要有兩個:第一,突破單臺服務器的硬件限制;第二,能夠並行操做,從而提升性能和吞吐量;(PS:其實跟kafka差很少)
  8. 副本的好處主要在於:第一,提供高可用;第二,並行提高性能和吞吐量
  9. 一個索引包含一個或多個分片,索引記錄(即文檔)數據存儲在這些shard中,且一個文檔只會存在於一個分片中
  10. 每一個shard都是一個獨立的功能完善的「index」,意思是它能夠獨立處理索引/搜索請求

(注意:本文中提到的分片指的是主分片(primary shard),而不是副本(replica shard))

2.  讀寫文檔

在Elasticsearch中,每一個索引都被劃分爲分片,每一個分片能夠有多個副本。這些副本稱爲複製組,在添加或刪除文檔時必須保持同步。若是咱們作不到這一點,從一個副本中讀取的結果將與從另外一個副本中讀取的結果很是不一樣。保持碎片副本同步並提供從中讀取的服務的過程稱爲數據複製模型。

Elasticsearch的數據複製模型基於主備份模型,該模型中有一個主分片,以及從主分片那裏複製的複製組,這些稱之爲複製分片。主(服務器)節點做爲全部索引操做的主要入口點,它負責驗證並確保操做是正確的。一旦主服務器接受了索引操做,主服務器還負責將該操做複製到其餘副本。

2.1.  Basic write model (基本的寫模型)

在Elasticsearch中,每一個索引操做首先會經過路由(一般是基於 document ID)解析到一個複製組(PS:這裏解析到複製組的意思是定位到索引的哪一個分片)。一旦肯定了複製組,該操做將在內部轉發到該組的當前主分片。主分片負責驗證操做並將其轉發到其餘副本。因爲副本能夠是下線狀態(PS:不在線),所以主分片不須要將該操做複製到全部副本。代替的,Elasticsearch維護應該接收操做的分片副本列表,這個列表稱爲同步副本,由主節點(PS:master node)維護。顧名思義,這些是一組「良好的」分片副本,它們保證處理了用戶已確認的全部索引和刪除操做。主服務器負責維護這個不變量(PS:指的是同步副本列表),所以必須將全部操做複製到這個集合中的每一個副本。

主分片的基本流程以下:

  1. 校驗輸入操做,而且若是結構無效(PS:好比字段類型錯誤等等)時拒絕該操做
  2. 在本地執行操做,即索引或刪除相關文檔。這也將驗證字段的內容,並在須要時拒絕
  3. 將操做轉發到當前同步副本集中的每一個副本。若是有多個副本,則並行執行此操做
  4. 一旦全部副本成功地執行了操做並響應了主副本(PS:只的主分片),主副本就向客戶端確認請求已成功完成

2.1.1.  失敗處理

若是主分片失敗的話,那麼它所在服務器節點將想主服務器節點(master node)發送一條關於該主分片的消息。索引操做將等待(默認狀況下最多1分鐘,具體看 Dynamic index settings)主節點(master)將其中一個副本提高爲新的主副本。而後將操做轉發到新的主分片進行處理。注意,主節點(master)還負責監視節點的健康情況,並可能決定主動降級主副本。當持有主副本的節點因網絡問題與集羣隔離時,一般會發生這種狀況。

在主分片上成功執行操做以後,主分片在副本分片上執行操做時必須處理潛在的故障。這多是因爲副本上的實際故障,或者因爲網絡問題致使操做沒法到達副本(或阻止副本響應)。全部這些最終結果是:做爲同步複製集的一部分的副本會丟失即將被確認的操做。爲了不違反不變量,主分片的宿主服務器向主服務器發送一條消息,請求從同步副本集中刪除有問題的分片。(PS:跟Kafka的副本同步有點兒像)一旦主服務器確認分片副本的移除後,主分片纔會確認這個寫請求操做。

在將操做轉發到副本時,主分片將使用副本驗證它仍然是活的主分片。若是主分片因爲網絡分區(或長時間GC)而被隔離,它可能會在乎識到它已經降級以前繼續處理輸入的索引操做,而後將操做路由到新的主服務器。

2.2.  Basic read model (基本的讀模型)

Elasticsearch中的讀取能夠是很是輕量級的ID查找,也能夠是具備複雜聚合(佔用大量CPU資源)的大型搜索請求。主備份模型的優勢之一是它保持全部分片副本相同,所以,一個同步副本就足以知足讀取請求。

當一個節點接收到讀請求時,該節點負責將其轉發到持有相關分片的節點、整理響應並響應客戶端。咱們將該節點稱爲該請求的協調節點

基本流程以下:

  1. 解析這個讀請求到相關的分片
  2. 從這個分片的複製組中選擇一個活的分片,這個活的分片能夠是主分片,也能夠是複製分片(副本)。默認狀況下,Elasticsearch只是簡單地在副本之間進行輪詢。
  3. 發送分片級別的讀請求給選中的副本
  4. 聚合結果並對客戶端做出響應

2.2.1.  失敗處理

當分片未能響應讀取請求時,協調節點將從相同的複製組中選擇另外一個副本,並將分片級搜索請求發送到該副本。重複失敗可能致使沒有分片副本可用。在某些狀況下,例如_search, Elasticsearch更喜歡快速響應,儘管會獲得部分結果,而不是等待問題獲得解決(部分結果在響應的_shards頭中表示)。

2.2.2.  高效地讀

在正常操做下,對每一個相關複製組執行一次讀操做。只有在失敗的條件下,相同分片的多個副本才能執行相同的搜索。

2.2.3.  讀未確認

因爲先在主分片上寫,而後複製請求,所以併發讀取可能在確認更改以前就已經看到更改。

2.2.4.  默認兩個副本

當只維護數據的兩個副本時(number_of_replicas默認是1),該模型能夠容錯。

2.3.  小結 & 回顧

一、一個索引(index)有多個分片(shard),每一個分片(primary shard)有多個副本(replica shard),主分片和它的副本稱組成該分片的複製組

二、數據複製模型基於主備份模型

一、首先,計算數據在哪一個分片上,這個過程稱之爲路由,一般是根據文檔ID來計算的

二、將請求轉到相應的節點上,主分片校驗請求,而後在本地執行,隨後將請求轉發到同步副本集中的每一個副本上,多個副本是並行執行的,當全部副本都執行完成之後,主副本向客戶端做出響應

三、每一個分片都有一個同步副本集,它由master節點維護

四、若是主分片操做失敗,則該分片的節點當即向master節點發送一條關於該分片的消息,而後master節點將從它的副本中選出一個做爲主分片,而且將請求轉到新的主分片上執行

五、若是主分片操做成功,副本操做失敗時,則改分片的節點會向master節點請求將改副本從同步副本集中移除,待master確認之後主分片就能夠想要客戶端了

六、若是主分片操做成功,可是因爲網絡分割,致使主分片與集羣的鏈接斷開了,那麼在選出新的主分片以前元主分片繼續處理請求,一旦選出新的分片後,原主分片再也不試主分片,就不能再接收請求了

一、解析讀請求到相應的分片(PS:路由,即計算數據在哪一個分片上)

二、從分片的複製組中選擇一個分片(PS:默認選擇的算法是在副本之間輪詢)

三、給選中的分片發請求

四、獲取分片完成請求後的響應結果,並響應客戶端

五、接收客戶端請求的那個結點負責將請求轉發到相應分片結點上,聚合各節點的響應結果,並響應客戶端,該結點成爲本次請求的協調節點

六、若是分片未能正常響應,協調節點將從相同的複製組中選擇另外一個副本,並將分片級搜索請求發送到該副本

七、一個讀請求只會在一個副本上執行,只有當執行失敗的時候,纔會換另一個副本上執行

八、併發讀寫可能會讀到髒數據

3.  Index API

下面這個例子,向"twitter"索引中插入一條id爲1的文檔,而且是在"_doc"類型下

curl -X PUT "localhost:9200/twitter/_doc/1" -H 'Content-Type: application/json' -d'
{
    "user" : "kimchy",
    "post_date" : "2009-11-15T14:12:12",
    "message" : "trying out Elasticsearch"
}
'

響應結果多是這樣的:

{
    "_shards" : {
        "total" : 2,
        "failed" : 0,
        "successful" : 2
    },
    "_index" : "twitter",
    "_type" : "_doc",
    "_id" : "1",
    "_version" : 1,
    "_seq_no" : 0,
    "_primary_term" : 1,
    "result" : "created"
}

_shards字段中提供了關於索引操做的複製過程的信息

  • total    代表索引操做應在多少分片(主分片和副本分片)上執行
  • successful    表示成功執行的複製數量(PS:successful至少是1)
  • failed    在索引操做在副本分片上執行失敗的狀況下,包含複製相關錯誤的數組

(PS:主分片 primary shard ;副本分片 replica shard )

3.1.  自動建立索引

若是在執行index api以前沒有建立索引的話,那麼該操做會自動建立索引,並自動建立一個動態類型映射(mapping)。

映射自己很是靈活,並且沒有模式。新的字段和對象將自動添加到指定類型的映射定義中。

能夠經過將 action.auto_create_index 設置爲false來禁用自動建立索引,將 index.mapper.dynamic 設置爲false來禁用自動映射。

3.2.  版本控制

每一個被索引的文檔都有一個版本號。版本號(version)在index API請求的響應中返回。版本號主要用於併發控制。

curl -X PUT "localhost:9200/twitter/_doc/1?version=2" -H 'Content-Type: application/json' -d'
{
    "message" : "elasticsearch now has versioning support, double cool!"
}
'

上面這個例子,若是ID爲1的文檔的版本號是2,則更新其message自動爲指定的內容,若是版本不是2,則不會執行更新操做,反而會報錯。

如今執行失敗,是由於當前ID爲1的文檔版本號是1,所以執行這個請求會失敗。若是咱們將該請求後面的版本號改爲1,則會成功。

(PS:這其實就是CAS,比較並交換)

(PS:樂觀鎖)

默認狀況下,內部版本號從1開始,而且在每次更新和刪除的時候版本號都會遞增。固然,也能夠手動指定。爲了能夠手動指定版本號,應該將version_type指定爲external。這個值必須是一個長整型的數值或者爲0。

3.2.1.  版本類型

下面是一些不一樣的版本類型:

  • internal    只索引給定版本號與文檔存儲的版本號相同的文檔。(PS:換言之,只有當給定的版本號與文檔存儲的版本號相同時,纔會索引該文檔,這裏索引操做指的是更新、刪除)
  • external 或者 external_gt    只索引那些文檔存儲的版本號比給定版本號小或者不存在的文檔,同時給定的版本號會做爲文檔的新版本號。(PS:換言之,只有當給定的版本號比文檔存儲的版本號大或者文檔不存在時,纔會執行)
  • external_gte    只索引給定版本號大於或等於存儲文檔的版本號的那些文件,若是文檔不存在的話這個操做也會成功。(PS:換言之,只有當給定版本號大於或等於存儲文檔的版本號時,纔會執行)

(PS:其實很好理解,無非就是給定的版本號與文檔當前版本號的一個比較,internal是相等的時候才執行,external是大於的時候才執行,external_get是大於或等於的時候才執行)

3.3.  操做類型

索引操做也能夠接受一個 op_type 參數用於強制建立操做。例如:

curl -X PUT "localhost:9200/twitter/_doc/1?op_type=create" -H 'Content-Type: application/json' -d'
{
    "user" : "kimchy",
    "post_date" : "2009-11-15T14:12:12",
    "message" : "trying out Elasticsearch"
}
'

或者,另外一種寫法也能夠

curl -X PUT "localhost:9200/twitter/_doc/1/_create" -H 'Content-Type: application/json' -d'
{
    "user" : "kimchy",
    "post_date" : "2009-11-15T14:12:12",
    "message" : "trying out Elasticsearch"
}
'

上面的例子,若是文檔已經存在,則操做失敗

3.4.  自動ID生成

索引操做能夠在不指定id的狀況下執行。在這種狀況下,將自動生成id。此外,op_type將自動設置爲create。下面是一個例子(注意這裏用POST代替PUT):

curl -X POST "localhost:9200/twitter/_doc/" -H 'Content-Type: application/json' -d'
{
    "user" : "kimchy",
    "post_date" : "2009-11-15T14:12:12",
    "message" : "trying out Elasticsearch"
}
'

返回結果以下:

{
    "_shards":{
        "total":2,
        "failed":0,
        "successful":2
    },
    "_index":"twitter",
    "_type":"_doc",
    "_id":"W0tpsmIBdwcYyG50zbta",
    "_version":1,
    "_seq_no":0,
    "_primary_term":1,
    "result":"created"
}

3.5.  路由

默認狀況下,路由(routing)控制是經過文檔ID的哈希值來作的。對於顯式的控制,可使用路由參數直接在每一個操做的基礎上指定輸入到路由器使用的哈希函數中的值。例如:

curl -X POST "localhost:9200/twitter/_doc?routing=kimchy" -H 'Content-Type: application/json' -d'
{
    "user" : "kimchy",
    "post_date" : "2009-11-15T14:12:12",
    "message" : "trying out Elasticsearch"
}
'

上面的例子中,_doc類型下的文檔究竟被路由到哪一個分片(shard)上是基於路由參數提供的值kimchy

3.6.  分佈

索引操做根據它的路由定向到主分片,並在包含該分片的實際節點上執行。在主分片完成操做以後,若是須要,更新將被分發到適用的副本。

3.7.  超時

默認狀況下,索引操做在主分片上最多等待1分鐘,而後失敗並以錯誤進行響應。可使用timeout參數顯式指定它等待的時間。

下面這個例子,設置超時時間是5分鐘:

curl -X PUT "localhost:9200/twitter/_doc/1?timeout=5m" -H 'Content-Type: application/json' -d'
{
    "user" : "kimchy",
    "post_date" : "2009-11-15T14:12:12",
    "message" : "trying out Elasticsearch"
}
'

4.  Get API

下面這個例子從一個名字叫「twitter」的索引中,類型爲「_doc」之下,獲取id爲0的JSON文檔:

curl -X GET "localhost:9200/twitter/_doc/0"

其返回的結果多是這樣的:

{
    "_index" : "twitter",
    "_type" : "_doc",
    "_id" : "0",
    "_version" : 1,
    "found": true,
    "_source" : {
        "user" : "kimchy",
        "date" : "2009-11-15T14:12:12",
        "likes": 0,
        "message" : "trying out Elasticsearch"
    }
}

你還能夠用HEAD操做單純的只是檢查某個文檔是否存在,例如:

curl -X HEAD "localhost:9200/twitter/_doc/0"

默認狀況下,Get 操做是實時的。也就是說,若是文檔已經被更新,可是索引還沒刷新,那麼get操做會調用刷新

4.1.  Source字段過濾

默認狀況下,get操做的返回中包含 _source 字段,你能夠手動關閉它。例如:

curl -X GET "localhost:9200/twitter/_doc/0?_source=false"

若是隻想顯示_source中的某些字段,能夠這樣簡短的表示:

curl -X GET "localhost:9200/twitter/_doc/0?_source=*.id,retweeted"

4.2.  路由

curl -X GET "localhost:9200/twitter/_doc/2?routing=user1"

注意,若是你帶了routing參數,並且還路由值還帶錯了,那麼將找不到文檔

5.  ?refresh

IndexUpdateDelete等操做支持設置 refresh 參數來控制何時改變對搜索可見。(PS:意思了,對文檔作了更新之後,何時這個更新能夠被檢索的時候看到)

refresh參數的值能夠是下列之一:

  • 空字符串 或者 true    :  操做發生後當即刷新相關的主分片和副本分片(不是整個索引),以便更新後的文檔當即出如今搜索結果中。
  • wait_for    :  在回覆以前,等待請求所作的更改被刷新。這並不強制當即刷新,而是等待刷新發生。Elasticsearch自動刷新已更改每一個索引的分片。refresh_interval,默認爲1秒。
  • false(默認)    :  不要執行刷新相關操做。此請求所作的更改將在請求返回後的某個時點可見。

下面是一些例子:

curl -X PUT "localhost:9200/test/_doc/1?refresh" -H 'Content-Type: application/json' -d'
{"test": "test"}
'
curl -X PUT "localhost:9200/test/_doc/2?refresh=true" -H 'Content-Type: application/json' -d'
{"test": "test"}
'

建立一個文檔,並當即刷新

curl -X PUT "localhost:9200/test/_doc/3" -H 'Content-Type: application/json' -d'
{"test": "test"}
'
curl -X PUT "localhost:9200/test/_doc/4?refresh=false" -H 'Content-Type: application/json' -d'
{"test": "test"}
'

只是建立一個文檔,其它的什麼也不作

curl -X PUT "localhost:9200/test/_doc/4?refresh=wait_for" -H 'Content-Type: application/json' -d'
{"test": "test"}
'

建立一個文檔,並等待刷新

6.  參考

https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-replication.html

相關文章
相關標籤/搜索