Elasticsearch路由详解

ELasticSearch路由

在ELasticSearch 里面,路由功能算是一个高级用法,大多数时候我们用的都是系统默认的路由功能,一个es索引可以分成shard和每个shard又可以有多个replia,那么添加进去的数据,在如何分布在各个shard上面的,而查询的时候文档是如何路由的。

默认情况下,索引数据的分片规则,是下面的公式:

shard = hash(_routing) % num_primary_shards

routing 是用来进行hash计算的路由值,默认是使用文档id值。我们可以在索引文档时通过routing参数指定别的路由值,number_of_primary_shards:创建索引时指定的主分片数

ES默认是基于hash的分片,保证在每个shard上数据量都近似平均,这样就不会出现负载不均衡的情况,然后在检索的时候,ES默认会搜索所有的shard上的数据,然后在master节点上面汇聚处理后,返回最终结果。

有时候,会有另外一种情况,比如说存储一年的数据,如果按照hash去索引,那就是分布非常的均匀,这样的话无论查询什么数据都会去所有的shard上面查询,如果数据量比较大,那么响应速度就比较慢,但这时,我们通过调查发现,一年12个月的数据本身分布并不均匀,有几个月的数据偏多,有几个月的数据偏少,理想情况下,数据偏少的月,查询性能应该更快,但如果是基于hash分片,那么我们并不能实现这种需求,因为hash分片,查询时候必须命中所有的shard之后,查询的结果才是准的,这样一来每次查询都要扫描所有的shard,比如我已经知道数据本身就是1月份的,那其实最好情况下,只查询1月份的数据就行,而不需要把一年的数据都扫描一遍,导致最终结果就是慢的更慢,快的也慢所以我们要针对性的做优化。

那么如何优化,其实思路也比较明确了,那就是按照月份分区,每一个月份的数据都存放在指定的分区中,如果mysql那就是每一个月份一张表,然后查询的时候,直接查询对应月份的数据即可,在es和solr中原理也大致如此,唯一不同的地方在以es 和 solr都比较方便的支持了路由字段的设置而如果数据库,则需要自己通过中间件的方式来搞定,比如说mycat等。

使用路由

es中使用路由字段,看一个官网给的一个例子

1
2
3
4
5
6
7
8
#指定了一个用户的属性作为路由进行分区,然后查询的时候也必须指定路由。

PUT my_index/my_type/1?routing=user1&refresh=true
{
"title": "This is a document"
}

GET my_index/my_type/1?routing=user1

注意,只要在索引的时候加入了路由字段,那么在以后的get、delete、update中都必须指定使用路由字段,否则会出现问题。

路由字段,也是可以被查询的:

1
2
3
4
5
6
7
8
GET my_index/_search
{
"query": {
"terms": {
"_routing": [ "user1" ]
}
}
}

也可以指定多个路由字段:

1
2
3
4
5
6
7
8
GET my_index/_search?routing=user1,user2 
{
"query": {
"match": {
"title": "document"
}
}
}

如果指定了多个用户属性,那么只会查询关联了这两个route属性的shard

如果加入路由字段之后,其他的操作都必须指定字段,为了避免在使用时忘了添加路由字段,导致同类型数据会分布在多个shard上面,这就违反了路由的原则,所以我们可以在mapping中设置路由字段是必需字段,否则会提示错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PUT my_index2
{
"mappings": {
"my_type": {
"_routing": {
"required": true
}
}
}
}

PUT my_index2/my_type/1
{
"text": "No routing value provided"
}

缺失字段会抛出异常:routing_missing_exception

还需要注意的是如果使用了路由字段,那么_id字段只能由用户来保证唯一性,因为同一个id的数据,如果路由字段不一样,他是可以被存在到多个shard中的,而默认情况下是不会出现这种情况的。

集群

集群组成

首先启动的一定是主节点,主节点存储的是集群的元数据信息;

Node2节点启动之前会配置集群的名称Cluster-name:ess,然后配置可以作为主节点的ip地址信息discovery.zen.ping.unicast.hosts: [“10.0.1.11”,“10.0.1.12”],配置自己的ip地址networ.host: 10.0.1.12;

Node2启动的过程中会去找到主节点Node1告诉Node1我要加入到集群里面了,主节点Node1接收到请求以后看Node2是否满足加入集群的条件,如果满足就把node2的ip地址加入的元信息里面,然后广播给集群中的其他节点有新节点加入,并把最新的元信息发送给其他的节点去更新;

集群中的所有节点的元信息都是和主节点一致的,因为一旦有新的节点加入进来,主节点会通知其他的节点同步元信息。

集群有节点出现故障,如主节点挂了,会重新选择主节点。

集群中创建索引的流程

  • 1、首先请求转发到master节点;
  • 2、选择节点 的分片、副本、记录元信息;
  • 3、通知给参与存放索引分片、副本的节点从节点创建分片、副本;
  • 4、参与的节点向主节点反馈结果;
  • 5、等待时间到了,master向一个节点反馈信息,节点响应请求;
  • 6、主节点将元信息广播给所有的从节点。

在集群中索引文档

索引文档的步骤:

  • 1、node2计算文档的路由值得到文档存放的分片(假定路由选定的是分片0)。
  • 2、将文档转发给分片0(P0)的主分片节点 node1。
  • 3、node1索引文档,同步给副本(R0)节点node3索引文档。
  • 4、node1向node2反馈结果
  • 5、node2作出响应

文档是如何路由的

shard = hash(_routing) % num_primary_shards

1
2
3
number_of_primary_shards:创建索引时候指定的主分片数参数值
routing:用来进行hash计算的路由值,默认是使用文档id值。我们可以在索引文档时通过routing参数指定别的路由值
在索引、删除、更新、查询中都可以使用routing参数(可多值)指定操作的分片。

在集群中进行搜索流程

搜索的步骤:如要搜索 索引 s0

  • 1、node2解析查询。
  • 2、node2将查询发给索引s0的分片/副本(R1,R2,R0)节点
  • 3、各节点执行查询,将结果发给Node2
  • 4、Node2合并结果,作出响应。

Master节点的工作任务

    1. 存储集群的元信息,如集群名称、集群中的节点
    1. 转发创建索引和索引文档的请求
    1. 和其他的节点进行通信,告诉其他节点有新的节点加入等