本文主要对Elasticsearch的分布式相关特性进行总结
1. 分片与副本
1.1 分片&副本
索引分片机制用来存储超过单个节点存储容量的数据,分片副本用来应对不断攀升的吞吐量以及确保数据的安全性。
当一个节点的主分片丢失,ElasticSearch可以把任意一个可用的分片副本推举为主分片。在默认情况下,ElasticSearch会创建一个分片副本。然而分片副本的数量可以通过设置相关的API随时更新,这一点与分片数是不同的。
分片副本的动态更新功能使得创建应用程序时十分方便,查询吞吐量可以随着分片副本数量的增加而增长,与此同时,使用分片副本还可以处理增加查询的发并量。
1.2 合适的分片与副本数
上节可以看出分片主要用来解决单节点存储容量的问题,多副本主要用来解决查询性能以及高可用的问题,Elasticsearch默认的情况下会为每个索引创建5个分片1个副本,该配置很多场景下往往不能满足需求,分片与副本的数量设置情况可参考下列方式
- 如果数据集的大小有限制而且严格定义好的,可以只使用一个分片,一个副本
- 除了上述情况,可以采用公式 “所需最大节点数=分片数*(副本数+1)”的计算方式,例如你计划用10个分片和2个分片副本,那么最大的节点数是30
1.3 分片&副本分配过度
分片分配过度可能会产生以下影响:
1. 增加分发搜索命令到每个分片以及分片结果合并的开销
副本分配过度可能会产生以下影响:
1. 额外的存储空间开销
2. 从主分片复制数据到分片副本时的开销
1.4 调整集群分片分配
主节点的主要功能之一是决定将哪些分片分配给哪些节点,以及何时移动节点之间的分片以集群的数据平衡。
目前Elasticsearch有多种策略可以控制分片的分配:
- 群集级别的分片分配策略:分片分配是将分片分配给节点的过程。这可能发生在初始恢复,副本分配,rebalancing或添加/删除节点时。
- 基于磁盘的的分片分配策略:Elasticsearch可以基于磁盘的使用情况配置分配策略来决定是将新分片分配给该节点还是主动将分片从该节点迁出.
- 基于机架/区域感知的分片分配策略:和大多数分布式集群的分配策略一样,ES也支持基于机架/区域感知的分片分配策略,该策略针对大面积基础设施出现问题时的保证集群高可用的策略。
- 基于过滤的分片分配策略:可以在索引或是集群级别来配置分片的分配,例如那些节点可以分配集群,那些节点不可以分配某个索引等。
上述策略除了可以通过配置文件配置外,还可以通过API在运行时进行配置。详细配置可参考:https://www.elastic.co/guide/en/elasticsearch/reference/5.2/modules-cluster.html
2. 路由
2.1 路由和分片
在分布式环境下一个索引可以分多个shard,在索引数据的时候,索引数据采用以下算法写到对应的shard上:
shard_num = hash(_routing) % num_primary_shards
默认情况下,Elasticsearch使用文档ID值作为依据将其哈希到相应的主分片上,这种算法基本上会保持所有数据在所有分片上的一个平均分布,而不会产生数据热点。
而我们为什么会需要自定义的Routing模式呢?首先默认的Routing模式在很多情况下都是能满足我们的需求的——平均的数据分布、对我们来说是透明的、多数时候性能也不是问题。但是在我们更深入地理解我们的数据的特征之后,使用自定义的Routing模式可能会给我们带来更好的性能。
假设你有一个100个分片的索引。当一个请求在集群上执行时会发生什么呢?
- 这个搜索的请求会被发送到一个节点
- 接收到这个请求的节点,将这个查询广播到这个索引的每个分片上(可能是主分片,也可能是复制分片)
- 每个分片执行这个搜索查询并返回结果
- 结果在通道节点上合并、排序并返回给用户
因为默认情况下,Elasticsearch使用文档的ID(类似于关系数据库中的自增ID,当然,如果不指定ID的话,Elasticsearch使用的是随机值)将文档平均的分布于所有的分片上,这导致了Elasticsearch不能确定文档的位置,所以它必须将这个请求广播到所有的100个分片上去执行。这同时也解释了为什么主分片的数量在索引创建的时候是固定下来的,并且永远不能改变。因为如果分片的数量改变了,所有先前的路由值就会变成非法了,文档相当于丢失了。
而自定义的Routing模式,可以使我们的查询更具目的性。我们不必盲目地去广播查询请求,取而代之的是:我们要告诉Elasticsearch我们的数据在哪个分片上。
2.2 通过路由控制索引数据
所有的文档API(get,index,delete,update和mget)都能接收一个routing参数,可以用来形成个性化文档分片映射。一个个性化的routing值可以确保相关的文档存储到同样的分片上——比如,所有属于同一个用户的文档。
指定方法1:
curl -XPOST 'http://localhost:9200/store/order?routing=user123' -d '
{
"productName": "sample",
"customerID": "user123"
}'
指定方法2:
curl -XPUT 'http://localhost:9200/store/order/_mapping' -d '
{
"order": {
"_routing": {
"required": true,
"path": "customerID"
}
}
}'
2.3 通过路由控制检索数据
利用路由机制的查询也是非常简单明了的,只需要在查询中指定对应的路由值即可:
curl -XGET 'http://localhost:9200/store/order/_search?routing=user123' -d '
{
"query": {
"filtered": {
"query": {
"match_all": {}
},
"filter": {
"term": {
"userID": "user123"
}
}
}
}
}'
通过指定的路由值,我们就可以直接定位到user123的文档所在的分片,而不用一股脑的向索引的所有节点都发送请求。这样的话,会大大减少系统资源的浪费。
2.4 多个路由联合
也可以同时指定多个路由值,方法也是显而易见的,只需要在查询参数中指定多个路由值即可:
curl -XGET 'http://localhost:9200/forum/posts/?routing=Admin,Moderator' -d '{}'
3. 查询执行偏好
在Elasticsearch中,会将相关操作随机分发到分片或者分片副本上。如果往集群中发送大量的查询命令,最终每个分片和分片副本上执行的查询命令数量会大致相同,但是可以通过preference参数来配置查询的执行地点。
为了便于说明,借助下表对参数进行说明:
节点名称 | 节点编号 | 包含索引 | 分片与副本分配 |
node1 | 6GVd-ktcS2um4uM4AAJQhQ | masting | primary shard 0 |
node2 | iw76-abdcdefdsdsdsdfsd | masting | primary shard 1 |
node3 | wJq0kPSHTHCovjuCsVK0-A | masting | replica shard 1,replica shard 0 |
- _primary: 使用该属性值,发送到集群的相关操作请求只会在主分片上执行。
- _primary_first:该属性值与_primary属性值导致相似的集群行为,但是具有容错机制。如果发送查询命令到mastering索引时附带了值为_primary_first的preference参数,该命令将在名称为node1和node2的节点上执行,但是如果有一个(或者更多)的主分片失效,查询命令将转到其它的分片上执行,在本例中会转到node3上执行。
- _local: ElasticSearch会优先在本地的节点上执行相关操作。比如,如果我们向node3发送附带一条preference参数值为_local的查询命令,最终该查询命令会在node3上执行。但是,如果我们把相同的命令发送到node2节点,那么最终该命令不仅会在编号为1的分片(节点为node2)上执行,同时也会分发到node1或者node3上执行,这两个节点上有编号为0的分片。该属性值在减小网络传输时间上特别有用。只要用到了_local preference参数值,我们就能确保查询命令会尽可能地在本地的节点上执行。
- _only_node:wJq0kPSHTHCovjuCsVK0-A:这类的操作只会在指定标识(本例中是wJq0kPSHTHCovjuCsVK0-A)的节点上执行。所以在本例中,查询命令只会在node3节点上的两个分片副本上执行。需要注意的是,如果指定节点中的分片不足以覆盖到整个索引的数据,那么命令就只会在指定节点的相关分片上执行。比如,如果我们将查询命令的preference参数值设置为 _only_node:6GVd-ktcS2um4uM4AAJQhQ,我们就只会获取到一个分片的数据。这个属性值在如下的应用场景中非常有用:用户已经知道某个节点所在的服务器处理能力强大,希望一些特定的查询命令只在该节点上执行。
- _prefer_node:wJq0kPSHTHCovjuCsVK0-A:这个选项用来把preference参数值设置成_prefer_node:,后面附带的值是一个节点Id(本例中就是wJq0kPSHTHCovjuCsVK0-A) ,这会导致ElasticSearch优先选择指定的节点来执行查询命令,但是如果该节点上缺少索引数据的一些分片,那么查询命令会发到含有欠缺分片的节点上。这与_only_node选项是类似的,_prefer_node也可以用来选择特定的节点,但是具备容错机制。
- _shards:0,1:这个preference值用来指定相关操作执行的某类分片(在本例中就是所有的分片,因为整个mastering索引只有id为0和1的分片)。这是唯一的一个可以结合其它属性使用的preference值。比如,如果希望命令只执行在本地节点的id为0或者1的分片上,我们可以把0,1两个值和_local值用“;”连接起来,最终得到的preference参数值就是这样了:0,1:_local。允许用户发出的命令只在某个分片上执行这一特性用于诊断集群问题是非常有帮助的。
- 自定义字符串值:这个自定义值会确保附带相同custom值的查询命令会在同样的分片上执行。比如,如果我们的查询命令附带preference参数值为mastering_Elasticsearch,那么命令会在node1和node2的主分片上执行。如果我们又发送了另一个附带同样preference参数值的查询命令,该命令也只会在node1和node2的分片上执行。该功能用于应对以下应用场景:假如集群中的各个节点刷新速率不一样,我们不希望用户在重复同一个命令时看到不同的结果,就应该使用该功能。
4. 别名
别名可以理解为一个虚拟的索引,这个虚拟索引对真在存在的索引进行一些特殊包装,对于使用者而言可以像使用正常索引那样来使用别名。大致可以完成如下功能
1. 用来将某个路由值与某个索引绑定
2. 一个别名可以对应多个真实索引
3. 隐藏索引配置的细节