ES
简介
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对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的含义是分片个数。