ES

来自智得网
跳转至: 导航、​ 搜索
ES-logo

简介

ES是基于Lucene搭建的一个分布式存储。lucene是apache旗下著名的开源搜索框架,但是lucene存在以下问题:

lucene是单机版本的搜索引擎,无法用于大规模搜索系统的实现。

lucene的文件写入索引之后不能立即生效,需要等待lucene生成segment才能被检索,过程中如果服务器宕机,会出现数据丢失的问题。

lucene的updateDocuments方法只支持文档全量更新,不支持字段级别的更新。

lucene只提供简单的API用于创建索引以及检索,不是一个完整的搜索引擎。

ES通过分布式架构解决了上述问题,成为了最为流行的搜索引擎之一。

名词解释

名词 解释
index ES中的index类似关系型数据库的database概念,作为存储的基本逻辑单元,一个index可以包含若干个存储节点。
type 类似关系型数据库中表的概念,但是ES7之后的版本已经弱化该字段。
mapping 相当于数据库中的schema,描述了索引中各个字段的类型。
doc 一个doc 相当于关系型数据库的一行数据,一条记录
字段 field 和数据库中的字段概念基本一致,数据类型包括字符串,text,数字以及复合数据类型等。

原理

实时性

当一个文档写入Lucene不能被立即查询,Elasticsearch提供了refresh的操作,该操作定时地调用(默认1秒)lucene的reopen(新版本为openIfChanged)在内存中为新写入的数据生成一个新的segment,写入segment的文档均可以被检索到。

refresh操作的时间间隔由refresh_interval参数控制,默认为1s, 当然还可以在写入请求中带上refresh表示写入后立即refresh,另外还可以调用refresh API显式refresh。

由于refresh的间隔较短,因此会产生大量的小segment,为此ES会运行一个任务检测当前磁盘中的segment,对符合条件的segment进行合并操作,减少lucene中的segment个数,提高查询速度,降低负载。不仅如此,merge过程也是文档删除和更新操作后,旧的doc真正被删除的时候。用户还可以手动调用_forcemerge API来主动触发merge,以减少集群的segment个数和清理已删除或更新的文档。

可靠性

当一个文档写入Lucence后是存储在内存中的,即使执行了refresh操作仍然是在文件系统缓存中,如果此时服务器宕机,那么这部分数据将会丢失。为此ES增加了translog, 当进行文档写操作时会先将文档写入Lucene,然后写入一份到translog,写入translog是落盘的(如果对可靠性要求不是很高,也可以设置异步落盘,可以提高性能,由配置index.translog.durability和index.translog.sync_interval控制),这样就可以防止服务器宕机后数据的丢失。由于translog是追加写入,因此性能比较好。与传统的分布式系统不同,这里是先写入Lucene再写入translog,原因是写入Lucene可能会失败,为了减少写入失败回滚的复杂度,因此先写入Lucene。

每30分钟或当translog达到一定大小(由index.translog.flush_threshold_size控制,默认512mb), ES会触发一次flush操作,此时ES会先执行refresh操作将buffer中的数据生成segment,然后调用lucene的commit方法将所有内存中的segment fsync到磁盘。此时lucene中的数据就完成了持久化,会清空translog中的数据(6.x版本为了实现sequenceIDs,不删除translog)

ES 读写流程图

集群

ES对lucene最大的优化是完善了集群架构。

lucene集群的节点分为三类:

  • 主节点, Primary,主节点承担索引的同步写请求,同时也可以承担部分读请求。
  • 从节点,从节点的数据通过复制主节点而来,主要是承担集群的读操作。
  • 协调节点,协调节点主要用于承接读请求,之后将请求路由到Primary或者Replication节点进行查询,并且在协调节点进行聚合。
  • ingest节点,查看该请求是否符合某个ingest pipeline的pattern, 如果符合则执行pipeline中的逻辑,一般是对文档进行各种预处理,如格式调整,增加字段等。如果当前节点没有ingest角色,则需要将请求转发给有ingest角色的节点执行。

主从架构将数据复制为多份,可以有效减少读请求的压力,为了减少写操作的压力,ES同样支持分片架构,根据路由规则可以将数据分发到不同的节点。

ES的路由规则如下:

shard_num = hash(_routing) % num_primary_shards

其中routing是分片键,num_primary_shards的含义是分片个数。