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

相关文章
相关标签/搜索