ElasticSearch索引过程

概念图

在这里插入图片描述

协调节点流程

客户端发送索引请求

客户端向ES节点发送索引请求

参数检查

对请求中的参数进行检查,检查参数是否合法,不合法的参数直接返回失败给客户端。

数据预处理

如果请求指定了pipeline参数,则对数据进行预处理,数据预处理的节点为Ingest Node,如果接受请求的节点不具有数据处理能力,则转发给其他能处理的节点。

在Ingest Node上有定义好的处理数据的Pipeline,Pipeline中有一组定义好的Processor,每个Processor分别具有不同的处理功能,ES提供了一些内置的Processor,如:split、join、set 、script等,同时也支持通过插件的方式,实现自定义的Processor。数据经过Pipeline处理完毕后继续进行下一步操作。

自动创建索引

创建索引请求被发送到Master节点,由Master节点负责进行索引的创建,索引创建成功后,Master节点会更新集群状态clusterstate,更新完毕后将索引创建的情况返回给Coordinate节点,收到Master节点返回的所有创建索引的响应后,进入下一流程。

请求预处理
  • 检查参数、自动生成ID、处理routing等
  • 获取集群状态信息,遍历所有请求,从集群状态中获取对应索引的元信息,检查mapping、routing、id信息,如果请求没有指定文档的id,则会生成一个UUID作为文档的id。
检测集群状态

协调节点在开始处理时会检查集群状态,若集群异常则取消写入,如果Master节点不存在。

路由算法及构建shard请求
  • 路由算法

路由算法即根据请求的routing和文档id信息计算文档应该被索引到那个分片ID的过程。
计算公式如下:

shard_num = hash(_routing) % num_primary_shards

默认情况下,_routing就是文档id,num_primary_shards是主分片个数,所以从算法中即可以看出索引的主分片个数一旦指定便无法修改,因为文档利用主分片的个数来进行定位。

当使用自定义_routing或者id时,按照上面的公式计算,数据可能会大量聚集于某些分片,造成数据分布不均衡,所以ES提供了routing_partition_size参数,routing_partition_size越大,数据的分布越均匀。
分片的计算公式变为:

shard_num = (hash(_routing) + hash(_id) % routing_partition_size) % num_primary_shards

也就是说,_routing字段用于计算索引中的一组分片,然后使用_id来选择该组内的分片。index.routing_partition_size取值应具有大于1且小于index.number_of_shards的值。

  • 构建shard请求

将用户的 bulkRequest 重新组织为基于 shard 的请求列表。

例如,原始用户请求可能有10个写操作,如果这些文档的主分片都属于同一个,则写请求被合并为1个。根据路由算法计算某文档属于哪个分片。遍历所有的用户请求,重新封装后添加到上述map结构。

Map<ShardId, List<BulkItemRequest>> requestsByShard = new HashMap<>();
转发请求并等待响应

根据集群状态中的内容路由表确定主分片所在节点,转发请求并等待响应。

遍历所有需要写的 shard,将位于某个 shard 的请求封装为 BulkShardRequest类,调用TransportShardBulkAction#execute执行发送,在listener中等待响应,每个响应也是以shard为单位的。如果某个shard的响应中部分doc写失败了,则将异常信息填充到Response中,整体请求做成功处理。待收到所有响应后(无论成功还是失败的),回复给客户端。

主分片节点流程

主分片索引文档

在这里插入图片描述
当主分片所在节点接受到请求后,节点开始进行本节点的文档写入,文档写入过程如下:

  • 文档写入时,不会直接写入到磁盘中,而是先将文档写入到Index Buffer内存空间中,到一定的时间,Index Buffer会Refresh把内存中的文档写入Segment中。当文档在Index Buffer中时,是无法被查询到的,这就是ES不是实时搜索,而是近实时搜索的原因。
    • 因为文档写入时,先写入到内存中,当文档落盘之前,节点出现故障重启、宕机等,会造成内存中的数据丢失,所以索引写入的同时会同步向Transaction Log写入操作内容。
    • 每隔固定的时间间隔ES会将Index Buffer中的文档写入到Segment中,这个写入的过程叫做Refresh,Refresh的时间可以通过index.refresh_interval,默认情况下为1秒。
    • 写入到Segment中并不代表文档已经落盘,因为Segment写入磁盘的过程相对耗时,Refresh时会先将Segment写入缓存,开放查询,也就是说当文档写入Segment后就可以被查询到。
    • 每次refresh的时候都会生成一个新的segment,太多的Segment会占用过多的资源,而且每个搜索请求都会遍历所有的Segment,Segment过多会导致搜索变慢,所以ES会定期合并Segment,减少Segment的个数,并将Segment和并为一个大的Segment;
    • 在操作Segment时,会维护一个Commit Point文件,其中记录了所有Segment的信息;同时维护.del文件用于记录所有删除的Segment信息。单个倒排索引文件被称为Segment。多个Segment汇总在一起,就是Lucene的索引,对应的就是ES中的shard。
    • Lucene倒排索引由单词词典及倒排列表组成:单词词典:记录所有文档的单词,记录单词到倒排列表的关系,数据量比较大,一般采用B+树,哈希拉链法实现。倒排列表:记录单词对应的文档集合,由倒排索引项组成。倒排索引项结构如表所示:其中,文档ID:记录单词所在文档的ID;词频:记录单词在文档中出现的次数;位置:记录单词在文档中的位置;偏移:记录单词的开始位置,结束位置。
  • 每隔一定的时间(默认30分钟),ES会调用Flush操作,Flush操作会调用Refresh将Index Buffer清空;然后调用fsync将缓存中的Segments写入磁盘;随后清空Transaction Log。同时当Transaction Log空间(默认512M)后也会触发Flush操作。

副本分片索引文档

当主分片完成索引操作后,会循环处理要写的所有副本分片,向副本分片所在的节点发送请求。副本分片执行和主分片一样的文档写入流程,然后返回写入结果给主分片节点。

请求返回

主分片收到副本分片的响应后,会执行finish()操作,将收到的响应信息返回给Coordinate节点,告知Coordinate节点文档写入分片成功、失败的情况;coordinate节点收到响应后,将索引执行情况返回给客户端。当文档写入失败时,主分片节点会向Master节点返送shardFieled请求,因为主副本分片未同步,Master会更新集群的状态,将写失败的副本分片从in-sync-allocation中去除;同时在路由表中将该分片的状态改为unassigned,即未分配状态。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值