空集群
- 如果我们启动一个单独的节点,它还没有数据和索引,这个集群看起来就像图1。
- 图1:只有一个空节点的集群
- 一个节点(node) 就是一个Elasticsearch实例,而一个集群(cluster) 由一个或多个节点组成,它们具有相同的
cluster.name
,它们协同工作,分享数据和负载。当加入新的节点或者删除一个节点时,集群就会感知到并平衡数据。 - 集群中一个节点会被选举为主节点(master) ,它将临时管理集群级别的一些变更,例如新建或删除索引、增加或移除节点等。主节点不参与文档级别的变更或搜索,这意味着在流量增长的时候,该主节点不会成为集群的瓶颈。任何节点都可以成为主节点。我们例子中的集群只有一个节点,所以它会充当主节点的角色。
- 做为用户,我们能够与集群中的任何节点通信,包括主节点。每一个节点都知道文档存在于哪个节点上,它们可以转发请求到相应的节点上。我们访问的节点负责收集各节点返回的数据,最后一起返回给客户端。这一切都由Elasticsearch处理。
集群健康
在Elasticsearch集群中可以监控统计很多信息,但是只有一个是最重要的:集群健康(cluster health) 。集群健康有三种状态:green
、yellow
或red
。
-
GET /_cluster/health
- 在一个没有索引的空集群中运行如上查询,将返回这些信息:
{ "cluster_name": "elasticsearch", "status": "green", <1> "timed_out": false, "number_of_nodes": 1, "number_of_data_nodes": 1, "active_primary_shards": 0, "active_shards": 0, "relocating_shards": 0, "initializing_shards": 0, "unassigned_shards": 0 }
- <1>
status
是我们最感兴趣的字段 status
字段提供一个综合的指标来表示集群的的服务状况。三种颜色各自的含义:- green:所有主要分片和复制分片都可用
- yellow:所有主要分片可用,但不是所有复制分片都可用
- red:不是所有的主要分片都可
添加索引
为了将数据添加到Elasticsearch,我们需要索引(index) ——一个存储关联数据的地方。实际上,索引只是一个用来指向一个或多个分片(shards) 的 “逻辑命名空间(logical namespace)” 。
一个分片(shard) 是一个最小级别 “工作单元(worker unit)” ,它只是保存了索引中所有数据的一部分。在接下来的 深入分片 一章,我们将详细说明分片的工作原理,但是现在我们只要知道分片就是一个Lucene实例,并且它本身就是一个完整的搜索引擎。我们的文档存储在分片中,并且在分片中被索引,但是我们的应用程序不会直接与它们通信,取而代之的是,直接与索引通信。
分片是Elasticsearch在集群中分发数据的关键。把分片想象成数据的容器。文档存储在分片中,然后分片分配到你集群中的节点上。当你的集群扩容或缩小,Elasticsearch将会自动在你的节点间迁移分片,以使集群保持平衡。
分片可以是 主分片(primary shard) 或者是 复制分片(replica shard) 。你索引中的每个文档属于一个单独的主分片,所以主分片的数量决定了索引最多能存储多少数据。
理论上主分片能存储的数据大小是没有限制的,限制取决于你实际的使用情况。分片的最大容量完全取决于你的使用状况:硬件存储的大小、文档的大小和复杂度、如何索引和查询你的文档,以及你期望的响应时间。
复制分片只是主分片的一个副本,它可以防止硬件故障导致的数据丢失,同时可以提供读请求,比如搜索或者从别的shard取回文档。当索引创建完成的时候,主分片的数量就固定了,但是复制分片的数量可以随时调整。
- 让我们在集群中唯一一个空节点上创建一个叫做
blogs
的索引。默认情况下,一个索引被分配5个主分片,但是为了演示的目的,我们只分配3个主分片和一个复制分片(每个主分片都有一个复制分片):PUT /blogs { "settings" : { "number_of_shards" : 3, "number_of_replicas" : 1 } }
- 附带索引的单一节点集群:
- 我们的集群现在看起来就像上图——三个主分片都被分配到
Node 1
。如果我们现在检查集群健康(cluster-health) ,我们将见到以下信息:{ "cluster_name": "elasticsearch", "status": "yellow", <1> "timed_out": false, "number_of_nodes": 1, "number_of_data_nodes": 1, "active_primary_shards": 3, "active_shards": 3, "relocating_shards": 0, "initializing_shards": 0, "unassigned_shards": 3 <2> }
- <1> 集群的状态现在是
yellow
- <2> 我们的三个复制分片还没有被分配到节点上
集群的健康状态yellow
表示所有的主分片(primary shards) 启动并且正常运行了——集群已经可以正常处理任何请求——但是复制分片(replica shards) 还没有全部可用。事实上所有的三个复制分片现在都是unassigned
状态——它们还未被分配给节点。在同一个节点上保存相同的数据副本是没有必要的,如果这个节点故障了,那所有的数据副本也会丢失。
现在我们的集群已经功能完备,但是依旧存在因硬件故障而导致数据丢失的风险。
增加故障转移
- 在单一节点上运行意味着有单点故障的风险——没有数据备份。幸运的是,要防止单点故障,我们唯一需要做的就是启动另一个节点。
启动第二个节点:为了测试在增加第二个节点后发生了什么,你可以使用与第一个节点相同的方式启动第二个节点(《运行Elasticsearch》一章),而且命令行在同一个目录——一个节点可以启动多个Elasticsearch实例。只要第二个节点与第一个节点有相同的
cluster.name
(请看./config/elasticsearch.yml
文件),它就能自动发现并加入第一个节点所在的集群。如果没有,检查日志找出哪里出了问题。这可能是网络广播被禁用,或者防火墙阻止了节点通信。 - 如果我们启动了第二个节点,这个集群看起来就像下图。
- 双节点集群——所有的主分片和复制分片都已分配:
- 第二个节点已经加入集群,三个复制分片(replica shards) 也已经被分配了——分别对应三个主分片,这意味着在丢失任意一个节点的情况下依旧可以保证数据的完整性。文档的索引将首先被存储在主分片中,然后并发复制到对应的复制节点上。这可以确保我们的数据在主节点和复制节点上都可以被检索。
cluster-health
现在的状态是green
,这意味着所有的6个分片(三个主分片和三个复制分片)都已可用:{ "cluster_name": "elasticsearch", "status": "green", <1> "timed_out": false, "number_of_nodes": 2, "number_of_data_nodes": 2, "active_primary_shards": 3, "active_shards": 6, "relocating_shards": 0, "initializing_shards": 0, "unassigned_shards": 0 }
- <1> 集群的状态是
green
. - 我们的集群不仅是功能完备的,而且是高可用的。
横向扩展
- 随着应用需求的增长,我们该如何扩展?如果我们启动第三个节点,我们的集群会重新组织自己,就像图4:
- 图4:包含3个节点的集群——分片已经被重新分配以平衡负载:三节点集群
Node3
包含了分别来自Node 1
和Node 2
的一个分片,这样每个节点就有两个分片,和之前相比少了一个,这意味着每个节点上的分片将获得更多的硬件资源(CPU、RAM、I/O)。- 分片本身就是一个完整的搜索引擎,它可以使用单一节点的所有资源。我们拥有6个分片(3个主分片和三个复制分片),最多可以扩展到6个节点,每个节点上有一个分片,每个分片可以100%使用这个节点的资源。
继续扩展
如果我们要扩展到6个以上的节点,要怎么做?
- 主分片的数量在创建索引时已经确定。实际上,这个数量定义了能存储到索引里数据的最大数量(实际的数量取决于你的数据、硬件和应用场景)。然而,主分片或者复制分片都可以处理读请求——搜索或文档检索,所以数据的冗余越多,我们能处理的搜索吞吐量就越大。
- 复制分片的数量可以在运行中的集群中动态地变更,这允许我们可以根据需求扩大或者缩小规模。让我们把复制分片的数量从原来的
1
增加到2
:PUT /blogs/_settings { "number_of_replicas" : 2 }
- 图5:增加 number_of_replicas 到2:
- 从图中可以看出,
blogs
索引现在有9个分片:3个主分片和6个复制分片。这意味着我们能够扩展到9个节点,再次变成每个节点一个分片。这样使我们的搜索性能相比原始的三节点集群增加三倍 。
当然,在同样数量的节点上增加更多的复制分片并不能提高性能,因为这样做的话平均每个分片的所占有的硬件资源就减少了(译者注:大部分请求都聚集到了分片少的节点,导致一个节点吞吐量太大,反而降低性能),你需要增加硬件来提高吞吐量。
不过这些额外的复制节点使我们有更多的冗余:通过以上对节点的设置,我们能够承受两个节点故障而不丢失数据。
应对故障
- 我们已经说过Elasticsearch可以应对节点失效,所以让我们继续尝试。如果我们杀掉第一个节点的进程(以下简称杀掉节点),我们的集群看起来就像这样:
- 图5:杀掉第一个节点后的集群
- 我们杀掉的节点是一个主节点。一个集群必须要有一个主节点才能使其功能正常,所以集群做的第一件事就是各节点选举了一个新的主节点:
Node 2
。 - 主分片
1
和2
在我们杀掉Node 1
时已经丢失,我们的索引在丢失主分片时不能正常工作。如果此时我们检查集群健康,我们将看到状态red
:不是所有主分片都可用! - 幸运的是丢失的两个主分片的完整拷贝存在于其他节点上,所以新主节点做的第一件事是把这些在
Node 2
和Node 3
上的复制分片升级为主分片,这时集群健康回到yellow
状态。这个提升是瞬间完成的,就好像按了一下开关。 - 为什么集群健康状态是
yellow
而不是green
?我们有三个主分片,但是我们指定了每个主分片对应两个复制分片,当前却只有一个复制分片被分配,这就是集群状态无法达到green
的原因,不过不用太担心这个:当我们杀掉Node 2
,我们的程序依然可以在没有丢失数据的情况下继续运行,因为Node 3
还有每个分片的拷贝。 - 如果我们重启
Node 1
,集群将能够重新分配丢失的复制分片,集群状况与上一节的 图5:增加number_of_replicas到2 类似。如果Node 1
依旧有旧分片的拷贝,它将会尝试再利用它们,它只会从主分片上复制在故障期间有数据变更的那一部分。 - 现在你应该对分片如何使Elasticsearch可以水平扩展并保证数据安全有了一个清晰的认识。接下来我们将会讨论分片生命周期的更多细节。
下一节:Elasticsearch 鼓励你在创建索引的时候就 扁平化(denormalizing) 你的数据,这样做可以获取最好的搜索性能。在每一篇文档里面冗余一些数据可以避免join操作。