第八章 Elasticsearch集群

我们知道了Elasticsearch能够做什么,接下来我们将见识Elasticsearch另一个很强的-----扩展能力,也就是,Elasticsearch如何能够处理更多的索引和搜索请求,或者是更快地处理索引和搜索请求。在处理百万级甚至数十亿级的文档时,扩展性是一个非常重要的因素。 没有了某种形式的扩展,在单一的 Elasticsearch运行实例或节点( node)上就无法一直支持规模持续增大的流量。Elaticsearch很容易扩展。所以我们来了解 Elasticsearch所拥有的扩展能力,以及如何使用这些特性来给予Elasticsearch更多的性能、更多的可靠性。

一、理解物理设计

1、节点和分片

为了有个全局的理解,我们首先要知道Elasticsearch索引创建的时候,究竟发生了什么,理解数据在物理是如何上组织的?

默认情况下,每个索引由5个主要分片组成,而每份主要分片又有一个副本, 一共10份分片。副本分片对于可靠性和搜索性能很有益处。技术上而言,一份分片是一个目录中的文件,Lucene用这些文件存储索引数据。分片也是Elasticsearch将数据从一个节点迁移到另一个节点的最小单位。

一个节点是一个Elasticsearch的实例。在服务器上启动Elasticsearch之后,你就拥有了一个节点。如果在另一台服务器上启动Elasticsearch,这就是另一个节点。甚至可以通过启动多个Elasticsearch进程,在同一台服务器上拥有多个节点。

 

第八章 Elasticsearch集群

多个节点可以加入同一个集群。在多节点的集群上,同样的数据可以在多台服务器上传播。这有助于性能,因为Elasticsearch有了更多的资源;这同样有助于稳定性,如果每份分片至少有1个副本分片,那么任何一个节点都可以宕机,而Elasticsearch依然可以进行服务,并返回所有数据。

对于使用Elasticsearch的应用程序,集群中有1个还是多个节点都是透明的。默认情况下,可以连接集群中的任一节点并访问完整的数据集,就好像集群只有单独的一个节点。

尽管集群对于性能和稳定性都有好处,但它也有缺点:必须确定节点之间能够足够快速地通信,并且不会产生脑裂。为了解决这个问题,我们后面会来讨论。

一份分片是Lucene的索引:一个包含倒排索引的文件目录。倒排索引的结构使得Elasticsearch在不扫描所有文档的情况下,就能告诉你哪些文档包含特定的词条( 单词)。

1.1、Elasticsearch索引和Lucene索引的对比:

Elasticsearch索引被分解为多块分片,一份分片是一个Lucene的索引,所以一个Elasticsearch的索引由多个Lucene的索引组成。 一个分片是一个Lucene索引(一个倒排索引)。它默认存储原始文档的内容,再加上一些额外的信息,如词条字典和词频,这些都能帮助到搜索。词条字典将每个词条和包含该词条的文档映射起来。搜索的时候,Elasticsearch没有必要为了某个词条扫描所有的文档,而是根据这个字典快速地识别匹配的文档。

词频使得Elasticsearch可以快速地获取某篇文档中某个词条出现的次数。这对于计算结果的相关性得分非常重要。例如,如果搜索“denver",包含多个“denver”的文档通常更为相关。Elasticsearch将给它们更高的得分,让它们出现在结果列表的更前面。

接下来看看主分片和副本分片的细节,以及它们是如何在Elasticsearch集群中分配的。

1.2、附:ES中的节点类型

Master-eligible nodes与Master node每个节点启动后,默认就是一个Master eligible节点,可以通过设置node.master:false来改变,Master-eligible节点可以参加选主流程,成为Master节点,每个节点都保存了集群的状态,但只有Master节点才能修改集群的状态信息,主节点主要负责集群方面的轻量级的动作,比如:创建或删除索引,跟踪集群中的节点,决定分片分配到哪一个节点,在集群再平衡的过程中,如何在节点间移动数据等。

1)Data Node

可以保存数据的节点,叫做Data Node,负责保存分片数据。在数据扩展上起到了至关重要的作用,每个节点启动后,默认就是一个Data Node节点,可以通过设置node. data:false来改变。 

2)Ingest Node

可以在文档建立索引之前设置一些ingest pipeline的预处理逻辑,来丰富和转换文档。每个节点默认启动就是Ingest Node,可用通过node.ingest = false来禁用。

3)Coordinating Node

Coordinating Node负责接收Client的请求,将请求分发到合适的节点,最终把结果汇集到一起,每个节点默认都起到了Coordinating Node的职责,当然如果把master、Data、Ingest全部禁用,那这个节点就仅是Coordinating Node节点了。

4)Machine Learning Node

用于机器学习处理的节点。

2、主分片和副本分片

分片可以是主分片,也可以是副本分片,其中副本分片是主分片的完整副本, 副本分片可以用于搜索,或者是在原有主分片丢失后成为新的主分片。主分片是权威数据,写过程先写主分片,成功后再写副分片。

Elasticsearch 索引由一个或多个主分片以及零个或多个副本分片构成。副本分片可以在运行的时候进行添加和移除,而主分片不可以。

可以在任何时候改变每个分片的副本分片的数量,因为副本分片总是可以被创建和移除。这并不适用于索引划分为主分片的数量,在创建索引之前,你必须决定主分片的数量。请记住,过少的分片将限制可扩展性,但是过多的分片会影响性能。默认设置主分片的数量是5份。

索引主分片的设置:

put test1{

"settings":{

"index.number_of_shards":3,

"index.codec":"best_compression"

}

}

索引副本分片的设置:

put test1/_settings

{

"index.number_of_replicas":2

}

3、在集群中分发分片

最简单的Elasticsearch集群只有一个节点: 一台机器运行着一个Elasticsearch进程。我们安装并启动了Elasticsearch之后,就已经建立了一个拥有单节点的集群。随着越来越多的节点被添加到同一个集群中,现有的分片将在所有的节点中自动进行负载均衡。因此,在那些分片上的索引和搜索请求都可以从额外增加的节点中获益。以这种方式进行扩展(在节点中加入更多节点)被称为水平扩展。此方式增加更多节点,然后请求被分发到这些节点上,工作负载就被分摊了。

水平扩展的另一个替代方案是垂直扩展,这种方式为Elasticsearch的节点增加更多硬件资源,可能是为虚拟机分配更多处理器,或是为物理机增加更多的内存。尽管垂直扩展几乎每次都能提升性能,它并非总是可行的或经济的。

4、分布式索引和搜索

在多个节点的多个分片上是如何进行索引和搜索的呢?

 

第八章 Elasticsearch集群

4.1、当索引一篇文档时

默认情况下,当索引一篇文档的时候,系统首先根据文档ID的散列值选择一个主分片,并将文档发送到该主分片。这份主分片可能位于另一个节点,不过对于应用程序这一点是透明的。

默认地,文档在分片中均匀分布:对于每篇文档,分片是通过其ID字符串的散列决定的。每份分片拥有相同的散列范围,接收新文档的机会均等。

一旦目标主分片确定,接受请求的节点将文档转发到该主分片所在的节点。随后,索引操作在该主分片的所有副本分片中进行。在所有可用副本分片完成文档的索引后,索引命令就会成功返回。这使得副本分片和主分片之间保持数据的同步。数据同步使得副本分片可以服务于搜索请求,并在原有主分片无法访问时自动升级为主分片。

4.2、搜索索引时

当搜索一个索引时,Elasticsearch需要在该索引的完整分片集合中进行查找。 这些分片可以是主分片,也可以是副本分片,原因是对应的主分片和副本分片通常包含一样的文档。Elasticsearch在索引的主分片和副本分片中进行搜索请求的负载均衡,使得副本分片对于搜索性能和容错都有所帮助。

在搜索的时候,接受请求的节点将请求转发到一组包含所有数据的分片。Elasticsearch使用round-robin的轮询机制选择可用的分片(主分片或副本分片),并将搜索请求转发过去,Elasticsearch然后从这些分片收集结果,将其聚集为单一的回复,然后将回复返回给客户端应用程序。在默认情况下,搜索请求通过round-robin轮询机制选中主分片和副本分片。

二、向集群中加入节点

1、单机集群(伪集群)

创建Elasticsearch集群的第一步, 是为单个节点加入另一个节点(或多个节点),组成节点的集群。 没有加入第二个节点前:http://xxxxxx:9200/_cluster/state/master_node,nodes?pretty

1.1、如何加入第二个节点

1)解压缩压缩包elasticsearch-7.7.0-linux-x86_64.tar.gzip到另外一个目录,比如elasticsearch-2,并且保证这个es中不包含任何数据;

2)修改配置文件

将cluster.name改为和第一个节点一样(cluster.name: my-elk);

给第二个节点配置独立的名字(例如:node.name: node-2);

network.host改为本机地址:

vim /etc/elasticsearch/elasticsearch.yml

network.host: 192.168.0.101

将discovery.seed_hosts改为本机地址:

discovery.seed_hosts: ["192.168.0.101"]

如果第一个节点有插件,则也需要安装同样的插件。

然后启动第二个节点即可。

现在有了第二个Elasticsearch节点加入了集群,可以再次访问接口查看:

http://192.168.0.101:9200/_cluster/state/master_node,nodes?pretty

第八章 Elasticsearch集群

 

1.2、新增节点上的分片是如何运作

那么新增节点上的分片是如何运作的呢?看看向集群增加一个节点前后,索引发生了些什么。在左端,test索引的主分片全部分配到节点Node1,而副本分片”分配没有地方分配。在这种状态下,集群是黄色的,因为所有的主分片有了安家之处,但是副本分片还没有。

 

第八章 Elasticsearch集群

一旦第二个节点加入,尚未分配的副本分片就会分配到新的节点Node2,这使得集群变为了绿色的状态。

 

第八章 Elasticsearch集群

当另一个节点加入的时候,Elasticsearch 会自动地尝试将分片在所有节点上 进行均匀分配。

 

第八章 Elasticsearch集群

如果更多的节点加入集群,Elasticsearch将试图在所有的节点上均匀地配置分片数量,这样每个新加入的节点都能够通过部分数据(以分片的形式)来分担负载。

将节点加入Elasticsearch集群带来了大量的好处,主要的收益是高可用性和提升的性能。当副本分片是**状态时(默认情况下是**的),如果无法找到主分片,Elasticsearch会自动地将一个对应的副本分片升级为主分片。这样,即使失去了索引主分片所在的节点,仍然可以访问副本分片上的数据。数据分布在多个节点上同样提升了性能,原因是主分片和副本分片都可以处理搜索和获取结果的请求。如此扩展还为整体集群增加了更多的内存,所以如果过于消耗内存的搜索和聚集运行了太长时间或致使集群耗尽了内存,那么加入更多的节点总是一个处理更多更复杂操作的便捷方式。

2、发现其他ES节点

集群的第二个节点是如何发现第一个节点、并自动地加入集群的,或者在集 群中有更多的节点的情况下,如何知道?

Elasticsearch 7.0中引入的新集群协调子系统来处理这些事,采用的是单播 机制,这种机制需要已知节点的列表来进行连接。

单播发现(unicast discovery )让Elasticsearch连接一系列的主机,并试图发现更多关于集群的信息。使用单播时,我们告诉Elasticsearch集群中其他节点的IP地址以及(可选的)端口或端口范围。

在elasticsearch.yml中通过discovery.seed_hosts配置种子地址列表,这样每个节点在启动时发现和加入集群的步骤就是:

1)去连接种子地址列表中的主机,如果发现某一个Node是Master Eligible Node,那么该Master Eligible Node会共享它知道的Master Eligible Node,这些共享的Master Eligible Node也会作为种子地址的一部分继续去试探;

2)直到找到某一个seed addresss对应的是Master Node为止;

3)如果第二步没有找到任何满足条件的Node,ES会默认每隔1秒后去重新尝试寻找,默认为1秒。

4)重复第三步操作直到找到满足条件为止,也就是直到最终发现集群中的主节点,会发出一个加入请求给主节点。

5)获得整个集群的状态信息。

为什么实例需要知道集群状态信息?例如,搜索必须被路由到所有正确的分 片,以确保搜索结果是准确的。在索引或删除某些文档时,必须更新每个副本。每个客户端请求都必须从接收它的节点转发到能够处理它的节点。每个节点都了 解集群的概况,这样它们就可以执行搜索、索引和其他协调活动。

discovery.seed_hosts中的节点地址列表,可以包括集群中部分或者全部集群节点,但是建议无论怎样都应该包含集群中Master-eligible nodes节点的部分或者全部。

3、选举主节点

一旦集群中的节点发现了彼此,它们会协商谁将成为主节点。一个集群有一个稳定的主节点是非常重要的,主节点是唯一一个能够更新集群状态的节点。主节点一次处理一个群集状态更新,应用所需的更改并将更新的群集状态发布到群集中的所有其他节点。

 Elasticsearch认为所有的节点都有资格成为主节点,除非某个节点的node.master选项设置为false,而node.master在不做配置的情况下,缺省为true。

如果完全使用默认配置启动新安装的Elasticsearch节点,它们会自动查找在同一主机上运行的其他节点,并在几秒钟内形成集群。在生产环境或其他分布式环境中还不够健壮。现在还存在一些风险:节点可能无法及时发现彼此,可能会形成两个或多个独立的集群。从Elasticsearch 7.0开始,如果你想要启动一个 全新的集群,并且集群在多台主机上都有节点,那么你必须指定该集群在第一次选举中应该使用的一组符合主节点条件的节点作为选举配置。这就是所谓的集群引导,也可以称为集群自举,只在首次形成集群时才需要。

cluster.initial_master_nodes这个参数就是用来设置一系列符合主节点条件的节点的主机名或IP地址来进行集群自举。集群形成后,不再需要此设置,并且会忽略它,也就是说,这个属性就只是在集群首次启动时有用。

在向集群添加新的符合主节点条件的节点时不再需要任何特殊的仪式,只需配置新节点,让它们可以发现已有集群,并启动它们。当有新节点加入时,集群将会自动地调整选举配置。

在主节点被选举出来之后,它会建立内部的ping机制来确保每个节点在集群中保持活跃和健康,这被称为错误识别( fault detection),有两个故障检测进程在集群的生命周期中一直运行。一个是主节点的,ping 集群中所有的其他节点,检查他们是否活着。另一种是每个节点都ping主节点,确认主节点是否仍在运行或者是否需要重新启动选举程序。

在Elasticsearch7以前的版本中,为了预防集群产生脑裂( split brain)的问题,Elasticsearch 6.x及之前的版本使用了一个叫作Zen Discovery的集群协调子系统。往往会将discovery.zen.minimum_master_nodes设置为集群节点数除以2再加上1。例如,3个节点的集群discovery.zen.minimum_master_nodes要设置为2,而对于14个节点的集群,最好将其设置为8。

在Elasticsearch 7以后里重新设计并重建了的集群协调子系统,移除minimum_master_nodes参数,转而由集群自主控制。

什么是 Elasticsearch 的脑裂?

脑裂这个词描述了这样的场景:(通常是在重负荷或网络存在问题的情况 下)Elasticsearch 集群中一个或多个节点失去了和主节点的通信,开始选举新的主 节点,并且继续处理请求。这个时候,可能有两个不同的 Elasticsearch 集群相互 独立地运行着,这就是“脑裂”一词的由来,因为单一的集群已经分裂成了两个 不同的部分,和左右大脑类似。为了防止这种情况的发生,Elasticsearch 7 以前 版本你需要根据集群节点的数量来设置 discovery. zen.minimum_master_nodes。 如果节点的数量不变,将其设置为集群节点的总数;否则将节点数除以 2 并加 1 是一个不错的选择,因为这就意味着如果个或多个节点失去了和其他节点的通信, 它们无法选举新的主节点来形成新集群,因为对于它们不能获得所需的节点(可 成为主节点的节点)数量(超过一半)。

三、删除集群中的节点

添加节点是扩展的好方法,但是如果Elasticsearch集群中的一个节点掉线了或者被停机了,那又会发生什么呢?这里以下图中3个节点的集群为例,其中包含了测试的索引,5个主分片和每个主分片对应的1个副本分片都分布在这3个节点上。

我们假设节点1宕机了,那么在节点1的3个分片怎么办?Elasticsearch所做的第1件事情是自动地将节点2上的0和3副本分片转为主分片。

 

第八章 Elasticsearch集群

这是由于索引操作会首先更新主分片,所以Elasticsearch要尽力使索引的主分片正常运作。注意Elasticsearch可以选择任一个副本分片并将其转变为主分片。只是在本例中每个主分片仅有一个副本分片供选择,就是节点Node2上的副本分片。

副本分片变为主分片之后,集群就会变为黄色的状态,这意味着某些副本分片尚未分配到某个节点。Elasticsearch下一步需要创建更多的副本分片来保持索引的高可用性。由于所有的主分片现在都是可用的,节点2上0和3主分片的数据会复制到节点3上作为副本分片,而节点3上1主分片的数据会复制到节点 2。

 

第八章 Elasticsearch集群

一旦副本分片被重新创建,并用于弥补损失的节点,那么集群将重新回归绿 色的状态,全部的主分片及其副本分片都分配到了某个节点。请记住,在这个时间段内,整个集群都是可用于搜索和索引的,因为实际上没有丢失数据。

如果失去的节点多于1个,或者某个没有副本的主分片丢失了,那么集群就会变为红色的状态,这意味着某些数据永远地丢失了,你需要让集群重连拥有丟失数据的节点,或者对丢失的数据重新建立索引。

就副本分片的数量而言,你需要理解自己愿意承担多少风险,这一点非常重要。有1份副本分片意味着集群可以缺失1个节点而不丢失数据。如果有2个副本分片,可以缺失2个节点而不丢失数据,以此类推。所以,确保你选择了合适的副本数量。备份你的索引永远是个不错的主意。

1、停用节点

当节点宕机时,让Elasticsearch自动地创建新副本分片是很棒的选择。可是,当集群进行例行维护的时候,你总是希望关闭某个包含数据的节点,而同时不让集群进人黄色的状态。也许硬件过于老旧,或者处理的请求流量有所下降,总之你不再需要这么多节点了。可以通过杀掉Java进程来停止节点,然后让Elasticsearch将数据恢复到其他节点,但是如果你的索引没有副本分片的时候怎么办?这意味着,如果不预先将数据转移,关闭节点就会让你丢失数据!

值得庆幸的是,Elasticsearch有一种停用节点( decommission)的方式,告诉集群不要再分配任何分片到某个或1组节点上。在3个节点的例子中,假设节点1、节点2和节点3的IP地址分别是192.168.1.101、192.168.1.102和192.168.1.103。如果你想关闭节点1的同时保持集群为绿色状态,可以先停用节点,这个操作会将待停用节点上的所有分片转移到集群中的其他节点。系统通过集群设置的临时修改,来为你实现节点的停用。

PUT _cluster/settings

{

"transient": {

"cluster.routing.allocation.exclude._name": "node-1"

}

}

或者

PUT _cluster/settings

{

"transient": {

"cluster.routing.allocation.exclude._ip": "192.168.1.101"

}

}

一旦运行了这个命令,Elaticsearch将待停用节点上的全部分片开始转移到集群中的其他节点上。

该过程可以重复,每次停止一个你想关闭的节点,或者也可以使用一个通过逗号分隔的IP地址列表,一次停止多个节点。请记住,集群中的其他节点必须有足够的磁盘和内存来处理分片的分配,所以在停止多个节点之前,做出相应的计划来确保你有足够的资源。

2、扩展策略

将节点加入集群以增加性能,看上去很简单,但是稍微做些计划会使得你在获取集群最佳性能的这条道路上走得更远。Elasticsearch的使用方式各有各的不同,所以需要根据如何索引和搜索数据,为集群选择最佳的配置。通常来说,规划生产环境的Elasticsearch集群至少需要考虑过度分片、将数据切分为索引和分片。

2.1、过度分片

让我们从过度分片开始说起。过度分片( over-sharding )是指你有意地为索引 创建大量分片,用于未来增加节点的过程。假设我们已经创建了拥有单一分片、 无副本分片的索引。但是,在增加了另外一个节点之后又会发生什么?

我们将得不到增加集群节点所带来的任何好处了。由于全部的索引和查询负 载仍然是由拥有单一分片的节点所处理,所以即使增加了一个节点你也无法进行 扩展。因为分片是 Elasticsearch 所能移动的最小单位,所以确保你至少拥有和集群 节点一样多的主分片总是个好主意。

如果现在有一个 5 个节点、11 个主分片的 集群,那么当你需要加入更多的节点来处理额外的请求时,就有成长的空间。使 用同样的例子,如果你突然需要多于 11 个的节点,就不能在所有的节点中分发 主分片,因为节点的数量将会超出分片的数量。

怎么办?创建一个有10000个主分片的索引?一开始的时候,这看上去是个好主意,但是Elasticsearch管理每个分片都隐含着额外的开销。每个分片都是完整的Lucene索引,它需要为索引的每个分段创建一些文件描述符,增加相应的内存开销。如果索引有过多的活跃分片,可能会占用了本来支撑性能的内存,或者触及机器文件描述符或内存的极限。对数据的压缩上也会有影响。

值得注意的是,没有对所有案例适用的完美分片索引比例。Elasticsearch选择的默认设置是5个分片,对于普通的用例是不错的主意,但是考虑你的规划在将来是如何增长(或缩减)所建分片的数量,这总是很件重要的事情。

不要忘记: 一旦包含某些数量分片的索引被创建,其主分片的数量永远是不能改变的。

Elasticsearch索引能处理多大的数据单一索引的极限取决于存储索引的机器之类型、你准备如何处理数据以及索引备份了多少副本。

2.2、如何评估ES中的数据量是否合适呢?有几个参考值:

1)ES官方推荐分片的大小是20G - 40G,最大不能超过50G。

2)每个节点上可以存储的分片数量与可用的堆内存大小成正比关系,但是Elasticsearch并未强制规定固定限值。这里有一个很好的经验法则:确保对于节点上已配置的每个GB,将分片数量保持在20以下。如果某个节点拥有3GB的堆内存,那最多可有60个分片,那么有三个机器的集群,ES可用总堆内存是9GB,则最多是180个分片,注意这个数据是包含了主副分片的。但是在此限值范围内,设置的分片数量越少,效果就越好。

3)通常来说,一个Lucene索引(也就是一个Elasticsearch分片)不能处理多于21亿篇文档,或者多于2740亿的唯一词条,但是在达到这个极限之前,你可能就已经没有足够的磁盘空间了。

举例:三个机器的集群,总内存是9GB,准备1主2副,支持的总主分片数量最大不宜超过60个分片。

2.3、将数据切分为索引和分片

现在还没有方法让我们增加或者减少某个索引中的主分片数量,但是你总是可以对数据进行规划,让其横跨多个索引。这是另一种完全合理的切分数据的方式。

比如说以地理位置创建索引和分片,你可以为西藏索引创建2个主分片,而为上海索引创建10个主分片,或者可以将数据以日期来分段,为数据按年份创建索引: 2019、2020和2021等。以这种方式将数据分段,对于搜索同样有所帮助,因为分段将恰当的数据放在恰当的位置。如果顾客只希望搜索2019年和2020年的活动或分组,你只需要搜索相应的索引,而不是整个数据集中检索。

使用索引进行规划的另一种方式是别名。别名( alias )就像指向某个索引或一组索引的指针。别名也允许你随时修改其所指向的索引。对于数据按照语义的方式来切分,这一点非常有用。你可以创建一个别名称为去年,指向2019,当2021年1月1日到来,就可以将这个别名指向2020年的索引。

当索引基于日期的信息时(就像日志文件),这项技术是很常用的,如此一来数据就可以按照每月、每周、每日等日期来分段,而每次分段过时的时候,“当前”的别名永远可用来指向应该被搜索的数据,而无须修改待搜索的索引之名称。此外,别名拥有惊人的灵活性,而且几乎没有额外负载,所以值得尝试。

四、检查集群的健康状态

1、常用

集群的健康 API 接口提供了一个方便但略有粗糙的概览,包括集群、索引和 分片的整体健康状况。这通常是发现和诊断集群中常见问题的第一步。

curl -XGET http://xxxxxx:9200/_cluster/health?pretty 从这个答复的表明信息,我们可以推断出很多关于集群整体健康状态的信息:

 

第八章 Elasticsearch集群

我们一般比较关注的是:"cluster_name" : 集群名称、"status" :

正常情况下,Elasticsearch集群健康状态分为三种:

green最健康得状态,说明所有的分片包括备份都可用;这种情况Elasticsearch集群所有的主分片和副本分片都已分配,Elasticsearch集群是100%可用的。

yellow基本的分片可用,但是备份不可用(或者是没有备份);这种情况Elasticsearch集群所有的主分片已经分片了,但至少还有一个副本是缺失的。不会有数据丢失,所以搜索结果依然是完整的。不过,你的高可用性在某种程度上被弱化。如果更多的分片消失,你就会丢数据了。把yellow想象成一个需要及时调查的警告。

red部分的分片可用,表明分片有一部分损坏。此时执行查询部分数据仍然可以查到,遇到这种情况,还是赶快解决比较好;这种情况Elasticsearch集群至少一个主分片(以及它的全部副本)都在缺失中。这意味着你在缺少数据:搜索只能返回部分数据,而分配到这个分片上的写入请求会返回一个异常。

"timed_out" : 是否有超时

"number_of_nodes" : 集群中的节点数量

"number_of_data_nodes" : 数据节点数

"active_primary_shards" : 指出你集群中的主分片数量

"active_shards" : 所有索引的_所有_分片的汇总值,即包括副本分片。

"relocating_shards" : 大于0表示Elasticsearch正在集群内移动数据的分片,来提升负载均衡和故障转移。这通常发生在添加新节点、重启失效的节点或者删除节点的时候,因此出现了这种临时的现象。

"initializing_shards" : 当用户刚刚创建一个新的索引或者重启一个节点的时候,这个数值会大于0

"unassigned_shards" : 这个值大于0的最常见原因是有尚未分配的副本分片。在开发环境中,这个问题很普遍,因为单节点的集群其索引默认有5个分片和1个副本分片。这种情况下,由于无多余节点来分配副本分片,因此还有5个未分配的副本分片。

"active_shards_percent_as_number" : 集群分片的可用性百分比,如果为0则表示不可用。

集群健康 API 提供了更多的细粒度的操作,允许用户进一步地诊断问题。在 这个例子中,可以通过添加 level 参数,深入了解哪些索引受到了分片未配置的 影响。

比如:

http://xxxxxx:9200/_cluster/health?level=indices&pretty=true

除此之外与集群相关的 API 还有:

查看集群健康状态接口(_cluster/health)

查看集群状况接口(_cluster/state)

查看集群统计信息接口(_cluster/stats)

查看集群挂起的任务接口(_cluster/pending_tasks)

集群重新路由操作(_cluster/reroute)

更新集群设置(_cluster/settings)

节点状态(_nodes/stats)

节点信息(_nodes)

节点的热线程(_nodes/hot_threads)

关闭节点(_nodes/_master/_shutdown)

2、使用_cat API

有的时候可读性很重要,我们就需要更方便的_cat API接口。这个_cat API提供了很有帮助的诊断和调试工具,将数据以更好的可读性打印出来。

_cat API有很多特性,它们对于调试集群的各个方面都是很有帮助的。你可以运行curl 'localhost:9200/_cat '来查看所支持的_ cat API接口的完整清单。

allocation-展示分配到每个节点的分片数量。

count-统计整个集群或索引中文档的数量。

如:GET _cat/count/employees?v

  • health--展示集群的健康状态。
  • indices-展示现有索引的信息。
  • master---显示目前被选为主节点的节点。
  • nodes---显示集群中所有节点的不同信息。
  • recovery--显示集群中正在进行的分片恢复状态。
  • shards-展示集群中分片的数量、大小和名字。
  • plugins---展示已安装插件的信息。

五、路由

 我们已经知道了文档是如何以通过分片形式来定位的。这个过程被称为路由(routing )文档。为了让你更好地回忆,这里重温一下:当Elasticsearch散列文档的ID时就会发生文档的路由,来决定文档应该索引到哪个分片中,这可以由你指定也可以让Elasticsearch生成。

但是这个路由完全可以我们手动指定。索引的时候,Elasticsearch也允许你手动地指定文档的路由,使用父子关系实际上就是这种操作,因为子文档必须要和父文档在同一个分片。

1、为什么使用路由

假设你有一个100个分片的索引。当一个请求在集群上执行时会发生什么呢?

1)这个搜索的请求会被发送到一个节点。

2)接收到这个请求的节点,将这个查询转到这个索引的每个分片上(可能是主分片,也可能是副本分片)。

3)每个分片执行这个搜索查询并返回结果。

4)结果在通道节点上合并、排序并返回给用户。

因为默认情况下,Elasticsearch使用文档的ID(类似于关系数据库中的自增ID),如果插入数据量比较大,文档会平均的分布于所有的分片上,这导致了Elasticsearch不能确定文档的位置,所以它必须将这个请求转到所有的N个分片上去执行这种操作会给集群带来负担,增大了网络的开销;

那么如何确定请求在哪个分片上执行呢?

Elasticsearch提供了一个API接口,告诉我们一个搜索请求在哪些节点和分片上执行。

2、_search_shards API

比如我们创建一个两分片的索引:

put open-soft-shard

{

"settings":{

"index.number_of_shards":2

}

}

并放入数据: 

put /open-soft-shard/_doc/1

{

"name": "Apache Hadoop",

"lang": "Java",

"corp": "Apache",

"stars":200

}

put /open-soft-shard/_doc/2

{

"name": ["Apache Activemq","Activemq Artemis"],

"lang": "Java",

"corp": "Apache",

"stars":[500,200]

}

put /open-soft-shard/_doc/3

{

"name": ["Apache Kafka"],

"lang": "Java",

"corp": "Apache",

"stars":[500,400]

}

put /open-soft-shard/_doc/object

{

"name": ["Apache ShardingSphere"],

"lang": "Java",

"corp": "JingDong",

"stars":400,

"address":{

"city":"BeiJing",

"country":"亦庄"

}

}

使用搜索分片( search shards )的API接口来查看请求将在哪些分片上执行。

GET /open-soft-shard/_search_shards?pretty

可以看到,会搜索全部的两个分片。当我们指定路由值1查询:

GET /open-soft-shard/_search_shards?pretty&routing=1

即使在索引中只有两个分片,当指定路由值1的时候,只有分片shard 0会被搜索。对于搜索需要查找的数据,有效地切除了一半的数据量!所以当处理拥有大量分片的索引时,路由会很有价值,当然对于Elasticsearch的常规使用它并不是必需的

3、配置路由

路由也可以不使用文档的ID,而是定制的数值进行散列。通过指定ULR中的routing查询参数,系统将使用这个值进行散列,而不是ID。

PUT /employees/_doc/2?routing=rountkey 

{

………

}

}

在这个例子中,rountkey这个由我们自己输入的值决定文档属于哪个分片的散列值,而不是文档的ID值2。

由上可知,自定义路由的方式非常简单,只需要在插入数据的时候指定路由的key即可。虽然使用简单,但有细节需要注意。我们来看看:

1)先创建一个名为study_route的索引,该索引有2shard0个副本

PUT study_route/

{

"settings": {

"number_of_shards": 2,

"number_of_replicas": 0

}

}

2)查看shard

GET _cat/shards/study_route?v

 

第八章 Elasticsearch集群

3)插入第1条数据

PUT study_route/_doc/a?refresh

{

"data": "A"

}

4)查看shard

GET _cat/shards/study_route?v

 

第八章 Elasticsearch集群

5)插入第2条数据

PUT study_route/_doc/b?refresh

{

"data": "B"

}

6)查看shard

GET _cat/shards/study_route?v

 

第八章 Elasticsearch集群

7)查看此时索引里面的数据

GET study_route/_search

这个例子比较简单,先创建了一个拥有2个shard,0个副本(为了方便观察)的索引study_route。创建完之后查看两个shard的信息,此时shard为空,里面没有任何文档(docs列为0)。接着我们插入了两条数据,每次插完之后,都检查shard的变化。通过对比可以发现docid=a的第一条数据写入了0号shard,docid=b的第二条数据写入了1号shard。接着,我们指定routing,看看有什么变化。

8)插入第3条数据

PUT study_route/_doc/c?routing=key1&refresh

{

"data": "C"

}

9)查看shard

GET _cat/shards/study_route?v

 

第八章 Elasticsearch集群

10)查看索引数据

GET study_route/_search

我们又插入了1条docid=c的新数据,但这次我们指定了路由,路由的值是一个字符串"key1"。通过查看shard信息,能看出这条数据路由到了0号shard。也就是说用"key1"做路由时,文档会写入到0号shard。

接着我们使用该路由再插入两条数据,但这两条数据的docid分别为之前使用过的"a"和"b",最终结果会是什么样?

11)插入docid=a的数据,并指定routing=key1

PUT study_route/_doc/a?routing=key1&refresh{

"data": "A with routing key1"

}

 

第八章 Elasticsearch集群

es的返回信息表明文档a是updated

12)查看shard

GET _cat/shards/study_route?v

 

第八章 Elasticsearch集群

13)查询索引

GET study_route/_search

 

第八章 Elasticsearch集群

14)插入docid=b的数据,使用key1作为路由字段的值

PUT study_route/_doc/b?routing=key1&refresh

{

"data": "B with routing key1"

}

es返回的信息变成了created

15)查看shard信息

GET _cat/shards/study_route?v

 

第八章 Elasticsearch集群

16)查询索引内容

GET study_route/_search

 

第八章 Elasticsearch集群

两个id为b的文档,其中一个比另一个多了一个字段"_routing"和步骤11插入docid=a的那条数据相比,这次这个有些不同,我们来分析一下。步骤11中插入docid=a时,es返回的是updated,也就是更新了步骤2中插入的docid为a的数据,步骤12和13中查询的结果也能看出,并没有新增数据,route_test中还是只有3条数据。

而步骤14插入docid=b的数据时,es返回的是created,也就是新增了一条数据,而不是updated原来 docid为b的数据,步骤15和16的确也能看出多了一条数据,现在有4条数据。而且从步骤16查询的结果来看,有两条docid为b的数据,但一个有routing,一个没有。而且也能分析出有routing的在0号shard上面,没有的那个在1号shard上。

这个就是我们自定义routing后会导致的一个问题:docid不再全局唯一。ES shard的实质是Lucene的索引,所以其实每个shard都是一个功能完善的倒排索引。ES能保证docid全局唯一是采用docid作为了路由,所以同样的docid肯定会路由到同一个shard上面,如果出现docid重复,就会update或者抛异常,从而保证了集群内docid唯一标识一个doc。但如果我们换用其它值做routing,那这个就保证不了了,如果用户还需要docid的全局唯一性,那只能自己保证了。 因为docid不再全局唯一,所以doc的增删改查API就可能产生问题,比如下面的查询:

GET study_route/_doc/b

GET study_route/_doc/b?routing=key1

上面两个查询,虽然指定的docid 都是b,但返回的结果是不一样的。所以,如果自定义了routing字段的话,一般doc的增删改查接口都要加上routing参数以保证一致性。

为此,ES在mapping中提供了一个选项,可以强制检查doc的增删改查接口是否加了routing参数,如果没有加,就会报错。设置方式如下:

PUT <索引名>/

{

"settings": {

"number_of_shards": 2,

"number_of_replicas": 0

},

"mappings": {

"_routing": {

"required": true

// 设置为 true,则强制检查;false 则不检查,默

认为 false

}

}

}

比如: 

PUT study_route1/

{

"settings": {

"number_of_shards": 2,

"number_of_replicas": 0

},

"mappings": {

"_routing": {

"required": true

}

}

}

// 写入一条数据

PUT study_route1/_doc/b?routing=key1{

"data": "b with routing"

}

// 以下的增删改查都会抱错

GET study_route1/_doc/b

PUT study_route1/_doc/b

{

"data": "B"

}

DELETE study_route1/_doc/b

// 错误信息

 

第八章 Elasticsearch集群

前面我们说过很多时候自定义路由是为了减少查询时扫描shard的个数,从而提高查询效率。默认查询接口会搜索所有的shard,但也可以指定routing字段, 这样就只会查询routing计算出来的shard,提高查询速度。

使用方式也非常简单,只需在查询语句上面指定routing即可,允许指定多个:--查询所有分区

GET study_route/_search

{

"query": {

"match": {

"data": "b"

}

}

}

-- 查询指定分区

GET study_route/_search?routing=key1

{

"query": {

"match": {

"data": "b"

}

}

}