一、Cluster:集群
Elastic Search可以作为一个独立的单个搜索服务器。不过,为了处理大型数据集,实现容错和高可用性,Elastic Search可以运行在许多互相合作的服务器上,这些服务器形成的整体称为集群。
二、Node:节点
形成集群的每个服务器称为节点
三、Shard:分片
分片,类似于数据库的分库操作。当文档的数量过大时,由于内存的限制、磁盘处理能力不足、无法够快的响应客户端的请求等,一个节点已经不能满足需求了,这种情况下,数据可以分为较小的分片,每个分片放到不同的节点上(说句废话,一般情况下,一个节点上应该只有一个主分片,但是一个节点上不仅只有一个分片,还可以有很多副本)。
当索引一个文档的时候,文档会被存储到一个主分片中。 那么,Elasticsearch是 如何知道一个文档应该存放到哪个分片中呢?当我们创建文档时,它如何决定这个文档应当被存储在分片 1
还是分片 2
中呢?
首先这肯定不会是随机的,否则将来要获取文档的时候我们就不知道从何处寻找了。实际上,这个过程是根据下面这个公式决定的:
shard = hash(routing) % number_of_primary_shards
routing
是一个可变值,默认是文档的 _id
,也可以设置成一个自定义的值。 routing
通过 hash 函数生成一个数字,然后这个数字再除以 number_of_primary_shards
(主分片的数量)后得到 余数 。这个分布在 0
到 number_of_primary_shards-1
之间的余数,就是我们所寻求的文档所在分片的位置。
这就解释了为什么我们要在创建索引的时候就确定好主分片的数量 并且永远不会改变这个数量:因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了。
当查询的索引分布在多个分片上时,ES会把查询发送给每个相关的分片,并将结果组合在一起,而应用程序并不知道分片的存在。
分片与路由
因为分布式索引会进行类似于数据库分库分表的操作。假如现在有2个分片,有4篇文档id分别为:1,2,3,4,根据取模算法,将他们分配到对应的表中。Elastic Search采取的也是这种思路。数据1和3 对2取模,等于 1,那么就会把1,3两个数据放到第2个分片上。2和4对2取模,等于0,那么就会把2,4两条数据放到第1个分片上。
查询数据的时候同理,查询1,3,根据取模运算结果,会去第2个分片上查找。查询2,4,会去第1个分片上查找。这个时候,修改了分片数目,取模运算结果与之前不一致,比如改成3个分片,那么查询3的时候,取模运算结果变成了0,就会去第1个分片上查找3,那么肯定查不到的。因此分片数不能随意修改,一旦修改,需要全部重建索引。
四、Replica:副本
Elastic Search中可以有许多相同的分片,其中之一可以进行编辑操作(如增删改),这种特殊的分片称为主分片。而副本是一个分片的精确复制,每个分片可以有零个或多个副本。
当主分片丢失时,如:该分片所在的数据不可用时,集群将副本提升为新的主分片。
number_of_replicas
副本数,用于备份分片的,和分片里面的数据保持一致,主要响应读操作,副本越多读取就越快。此外,副本数是可以修改的。但是在集群环境中,副本数应小于等于节点数减1,否则会降低集群的健康值。
我们前面说,一个节点应该只有一个主分片。但是实际中,我们需要预估一下将来的分片数,并且提前做规划。在《Elastic Search权威指南》中,提到了扩容设计。其中指出,在初期,我们可以只有一个节点,并且可以在该节点中建立多个主分片。当访问量增加,节点负荷过大时,我们可以增加新的节点,更加奇妙的是Elasticsearch 会自动将其中部分分片移动至到新的节点。就如下图所示
引自:https://www.elastic.co/guide/cn/elasticsearch/guide/current/overallocation.html
此外,新的版本的副本复制策略同6.6不太一样,具体可以参考https://www.jianshu.com/p/a164b86445b6
五、文档,元数据
1,文档
在大多数应用中,多数实体或对象可以被序列化为包含键值对的 JSON 对象。 一个 键 可以是一个字段或字段的名称,一个 值 可以是一个字符串,一个数字,一个布尔值, 另一个对象,一些数组值,或一些其它特殊类型诸如表示日期的字符串,或代表一个地理位置的对象
2,文档元数据
一个文档不仅仅包含它的数据 ,也包含 元数据 —— 有关 文档的信息。 三个必须的元数据元素如下:
-
_index
文档在哪存放
一个 索引 应该是因共同的特性被分组到一起的文档集合。 例如,你可能存储所有的产品在索引
products
中,而存储所有销售的交易到索引sales
中。 虽然也允许存储不相关的数据到一个索引中,但这通常看作是一个反模式的做法。实际上,在 Elasticsearch 中,我们的数据是被存储和索引在 分片 中,而一个索引仅仅是逻辑上的命名空间, 这个命名空间由一个或者多个分片组合在一起。 然而,这是一个内部细节,我们的应用程序根本不应该关心分片,对于应用程序而言,只需知道文档位于一个 索引 内。 Elasticsearch 会处理所有的细节。
-
_type
文档表示的对象类别
数据可能在索引中只是松散的组合在一起,但是通常明确定义一些数据中的子分区是很有用的。 例如,所有的产品都放在一个索引中,但是你有许多不同的产品类别,比如 "electronics" 、 "kitchen" 和 "lawn-care"。
需要注意的是,Elastic Search 6之后,_type 只有一个,而到了Elastic Search 7 之后,取消了 _type。
为什么 _type 被移除了?
起初,我们说"索引"和关系数据库的“库”是相似的,“类型”和“表”是对等的。这是一个不正确的对比,导致了不正确的假设。在关系型数据库里,"表"是相互独立的,一个“表”里的列和另外一个“表”的同名列没有关系,互不影响。但在类型里字段不是这样的。在一个Elasticsearch索引里,所有不同类型的同名字段内部使用的是同一个lucene字段存储。也就是说,上面例子中,user类型的user_name字段和tweet类型的user_name字段是存储在一个字段里的,两个类型里的user_name必须有一样的字段定义。这可能导致一些问题,例如你希望同一个索引中"deleted"字段在一个类型里是存储日期值,在另外一个类型里存储布尔值。最后,在同一个索引中,存储仅有小部分字段相同或者全部字段都不相同的文档,会导致数据稀疏,影响Lucene有效压缩数据的能力。因为这些原因,我们决定从Elasticsearch中移除类型的概念。
-
_id
文档唯一标识
ID 是一个字符串,当它和
_index
以及_type
组合就可以唯一确定 Elasticsearch 中的一个文档。 当你创建一个新的文档,要么提供自己的_id
,要么让 Elasticsearch 帮你生成。当Elastic Search7 取消
_type
之后,及_index
和_id
即可确定唯一的文档。
五、文档,元数据
1,文档
在大多数应用中,多数实体或对象可以被序列化为包含键值对的 JSON 对象。 一个 键 可以是一个字段或字段的名称,一个 值 可以是一个字符串,一个数字,一个布尔值, 另一个对象,一些数组值,或一些其它特殊类型诸如表示日期的字符串,或代表一个地理位置的对象
2,文档元数据
一个文档不仅仅包含它的数据 ,也包含 元数据 —— 有关 文档的信息。 三个必须的元数据元素如下:
-
_index
文档在哪存放
一个 索引 应该是因共同的特性被分组到一起的文档集合。 例如,你可能存储所有的产品在索引
products
中,而存储所有销售的交易到索引sales
中。 虽然也允许存储不相关的数据到一个索引中,但这通常看作是一个反模式的做法。实际上,在 Elasticsearch 中,我们的数据是被存储和索引在 分片 中,而一个索引仅仅是逻辑上的命名空间, 这个命名空间由一个或者多个分片组合在一起。 然而,这是一个内部细节,我们的应用程序根本不应该关心分片,对于应用程序而言,只需知道文档位于一个 索引 内。 Elasticsearch 会处理所有的细节。
-
_type
文档表示的对象类别
数据可能在索引中只是松散的组合在一起,但是通常明确定义一些数据中的子分区是很有用的。 例如,所有的产品都放在一个索引中,但是你有许多不同的产品类别,比如 "electronics" 、 "kitchen" 和 "lawn-care"。
需要注意的是,Elastic Search 6之后,_type 只有一个,而到了Elastic Search 7 之后,取消了 _type。
为什么 _type 被移除了?
起初,我们说"索引"和关系数据库的“库”是相似的,“类型”和“表”是对等的。这是一个不正确的对比,导致了不正确的假设。在关系型数据库里,"表"是相互独立的,一个“表”里的列和另外一个“表”的同名列没有关系,互不影响。但在类型里字段不是这样的。在一个Elasticsearch索引里,所有不同类型的同名字段内部使用的是同一个lucene字段存储。也就是说,上面例子中,user类型的user_name字段和tweet类型的user_name字段是存储在一个字段里的,两个类型里的user_name必须有一样的字段定义。这可能导致一些问题,例如你希望同一个索引中"deleted"字段在一个类型里是存储日期值,在另外一个类型里存储布尔值。最后,在同一个索引中,存储仅有小部分字段相同或者全部字段都不相同的文档,会导致数据稀疏,影响Lucene有效压缩数据的能力。因为这些原因,我们决定从Elasticsearch中移除类型的概念。
-
_id
文档唯一标识
ID 是一个字符串,当它和
_index
以及_type
组合就可以唯一确定 Elasticsearch 中的一个文档。 当你创建一个新的文档,要么提供自己的_id
,要么让 Elasticsearch 帮你生成。当Elastic Search7 取消
_type
之后,及_index
和_id
即可确定唯一的文档。
六、新建,索引,删除操作的基本逻辑
假设我们如上面所示,上面每个节点都可以接收写请求,那么新增数据的一个流程如下所示:
步骤顺序如下:
(1) 客户端向Node1发送新建请求(如果客户端向Node2或者Node3等其他节点发送请求,请求会被转发给Master节点,本图中为Node1,更新,删除同理)。
(2) master根据_id确定文档属于分片0,那么请求会被转发到Node3,因为分片 0 的主分片目前被分配在 Node3上。
(3) Node 3在主分片上面执行请求。如果成功了,它将请求并行转发到 Node 1和 Node 2 的副本分片上。一旦所有的副本分片都报告成功, Node3 将向Master节点报告成功,Master节点向客户端报告成功(ES新版本是可用shard写入成功过半,Node3会发送消息给master,并由Master节点转发给请求来源节点)。
在客户端收到成功响应时,文档变更已经在主分片和所有副本分片执行完成,变更是安全的。
七、查询的基本逻辑
以下是从主分片或者副本分片检索文档的步骤顺序:
1、客户端向 Node 1
发送获取请求。
2、节点使用文档的 _id
来确定文档属于分片 0
。分片 0
的副本分片存在于所有的三个节点上。 在这种情况下,它将请求转发到 Node 2
。在处理读取请求时,Master节点在每次请求的时候都会通过轮询所有的副本分片来达到负载均衡,因此会转发请求到Node 2
。
3、Node 2
将文档返回给 Node 1
,然后将文档返回给客户端。
在文档被检索时,已经被索引的文档可能已经存在于主分片上但是还没有复制到副本分片。 在这种情况下,副本分片可能会报告文档不存在,但是主分片可能成功返回文档。 一旦索引请求成功返回给用户,文档在主分片和副本分片都是可用的。