前言

最近面试过程中遇到问Elasticsearch的问题不少,这次总结一下,然后顺便也了解一下Elasticsearch内部是一个什么样的结构,毕竟总不能就只了解个倒排索引吧。本文标题就是我遇到过的两个问题,所以此次基本上只是围绕着这两个问题来总结。

ES写入数据

在介绍写入数据的过程时,先明确一下ES中的一些关键性的概念:

  • Clouster:集群,由一到N个Elasticsearch服务节点组成。
  • Node:节点,组成Elasticsearch集群的基本单元,单个集群内节点名称唯一。通常一个节点中分配一道多个分片。
  • Shards:分片,当ES的索引数据过大时,会进行水平拆分,拆分出来的每一个单元都称为分片。在进行写入数据的时候,会通过路由来确定具体写到哪个分片上,所以在创建索引的时候就要确定好分片数量,并且一旦确定不可更改。索引数据在经过分片后,在数据管理和性能上都有很大提升,并且每一个分片都是一个Lucende的索引,每个分片都必须有一个主分片和零到多个副分片
  • Replicas:副本或备份,副本是指对主分片的备份分片,无论是主分片还是副本分片都可以对外提供查询服务。但是写入操作时是先写入主分片,然后再分发到副本上。
    当主分片不可用时会在副本分片上选举一个作为主分片,因此副本不仅可以保证系统的高可用性,还可以提升搜索时的并发性能(主副分片都可以提供查询)。但并不是副本越多越好,副本数量过多会导致数据同步的负担过大。
  • Index:索引,由一个和多个分片组成,单个集群内索引名字是唯一的。
  • Type:类型,指索引内部的逻辑分区,一般是通过Type的名字来进行分区,若是查询条件中没有该值,则说明在整个索引中执行查询。
  • Document:文档,ES索引中的每一条数据都称为一个Document,基本上和关系型数据库中的一个记录意思相同,通过_id,在Type内进行唯一标识。
  • Settings:对集群中索引的设定,例如默认的分片数量,副本数等信息。
  • Mapping:这里的Mapping类似于,关系型数据库的表结构信息,这里面包含了索引中字段的存储类型,分词方式,是否分词等信息。
    Elasticsearch中的Mapping是可以动态识别的,Elasticsearch字段的数据格式识别它的类型,但是若是需要对Filed字段进行特殊设置时,就需要手动创建Mapping了。注意:一个Mapping一旦创建成功后,若是已经存储了数据了,就不可以修改了。
  • Analyzer:字段的分词方式的定义,一个Analyzer,通常由一个Tokenizer,零到多个Filter组成。例如默认的标准Analyzer包含一个标准的Analyzer和三个Filter(Standard Token Filter、Lower Case Token Filter、Stop Token Filter)。

Elasticsearch的节点分类

  • Master Node(主节点):主节点主要负责创建索引,删除索引,分配分片,追踪集群中的节点状态等工作。在配置文件中设置node.master=true 来将该节点设置成候选主节点,在集群中只有候选主节点才有选举权和被选举权。
  • Data Node(数据节点):数据节点负责数据的存储和相关具体操作,例如索引数据的创建,更新,搜索,聚合等操作。因此,数据节点对机器的要求比较高无论是在磁盘空间还是CPU、内存、I/O性能等。
    集群在扩大后,需要增加更多的数据节点来提高可用性,在配置文件中通过node.data=true 来设置当前节点为数据节点。
  • Client Node(客户端节点):客户端节点是既不做候选主节点也不做数据节点的节点,只负责请求的分发、汇总等。若是单独增加这种节点主要是更多地为了提高并发性。
  • Tribe Node(部落节点):部落节点可以跨越多个集群,它可以接收每个集群的状态,然后合并成一个全局集群状态。
  • Coordinating Node(协调节点):协调节点只是一个角色,并不是指具体的某个集群节点,也没法通过配置来指定某个节点为协调节点,这也就说明集群中任何一个节点都可以是协调节点。
    例如:节点A接收到用户的查询请求,会把查询语句分发到其他节点,然后合并各个节点返回查询结果,最后将完成的聚合结果返回给用户。这个请求中节点A的扮演的就是协调节点的角色。

还有就是集群节点有三个颜色状态:绿色、黄色、红色

  • 绿色:代表健康状态,所有的主副分片都可正常工作,集群100%健康。
  • 黄色:预警,所有的主分片都可以正常工作,但是至少有一个副分片是不能正常工作的。虽然集群能正常工作,但是高可用性已经有所降低。
  • 红色:异常,集群不可正常使用。集群中至少有一个分片的主分片和全部副分片不可用。此时虽然查询操作可以返回数据,但也只是返回可用分片的那部分数据,并非全部的正确数据。若此时写请求打到异常分片会造成数据丢失。

写入过程

Elasticsearch写入数据到索引的过程大致是这样的:
Elasticsearch写入数据的过程是什么样的?以及是如何快速更新索引数据的?-LMLPHP

  • 首先客户端会根据配置的连接节点,通过轮询的方式选择一个coordinate节点。
  • coordinate节点通过路由函数(shard = hash(routing)%number_of_primary_shards),计算出数据应该落到那个shard中,根据coordinate节点上维护的shard信息,将请求发送到Node1上。
  • Node1先校验索引数据,然后在主分片上执行请求,执行成功后,将请求并行转发到副本集存在Node2、Node3。
  • Node2、Node3写入成功数据成功后,发送ack信息给主分片所在的Node1节点。
  • Node1节点再将ack信息发送给coordinate节点。
  • coordinate 节点 发送ack节点给客户端。

在主分片上执行写入请求的过程如下:

Elasticsearch写入数据的过程是什么样的?以及是如何快速更新索引数据的?-LMLPHP

  1. 当有数据写入时,为了提升写速度,也是先将数据写入到内存(Memory Buffer)中的;
  2. 因为先写入了内存所以为了保证内存中的数据不丢失,也会同时写入Translog到内存中。
  3. 每隔一定时间(可配置)将数据从Memory Buffer中refreshFileSystemCache中,生成segment文件,一旦生成segment文件,就能通过索引查询到了。
  4. refresh完,memory buffer就清空了。Translog 也是从buffer flush到磁盘中。
  5. 定期/定量从FileSystemCache中,结合Translog内容flush index到磁盘中。做增量flush的。

因为Elasticsearch的这个刷盘机制,也说明并非是一个实时的搜索引擎。

更新数据

在早期的全文检索中为整个文档建立了很大的倒排索引,并将其写入到磁盘。如果索引有更新,就需要重新全量创建一个索引来替换原来的索引。

这种方式在数据量很大时效率很低,并且由于创建一次索引的成本很高,所以对数据的更新不能过于频繁,也就不能保证实效性。

分段存储

在搜索中引入了段(segment)的概念(将一个索引文件拆分为多个子文件,则每个子文件叫做段),每个段都是一个独立的可被搜索的数据集,并且段具有不变性,一旦索引的数据被写入硬盘,就不可修改

由于Elasticsearch中的每个分片包含多个segment(段),每一个segment都是一个倒排索引;因此在在查询的时,会把所有的segment查询结果汇总归并为最终的分片查询结果返回。

那么在这种分段存储的模式下Elasticsearch是如何进行数据操作的呢?
  • 新增: 当有新的数据需要插入索引时,由于段的不可变性,会新建一个段来存储新增数据。
  • 删除: 也是由于段的不可变,所以删除的时候会新增一个.del文件,专门用来存储被删除的数据id。这样虽然查询的时候还是能查到,但是在进程查询结果汇总的时候会将已删除的数据id过滤掉。
  • 更新: 更新操作其实就是删除和新增的组合操作,先在.del文件中积累旧数据,然后在新段中添加一条更新后的数据。
分段存储的优点和缺点
segment优点
11-19 17:38