在众多的节点中,其中会有一个Master Node
,它主要负责维护索引元数据、负责切换主分片和副本分片身份等工作(后面会讲到分片的概念),如果主节点挂了,会选举出一个新的主节点。
从上面我们也已经得知,Elasticsearch最外层的是Index(相当于数据库 表的概念);一个Index的数据我们可以分发到不同的Node上进行存储,这个操作就叫做分片。
比如现在我集群里边有4个节点,我现在有一个Index,想将这个Index在4个节点上存储,那我们可以设置为4个分片。这4个分片的数据合起来就是Index的数据
为什么要分片?原因也很简单:
- 如果一个Index的数据量太大,只有一个分片,那只会在一个节点上存储,随着数据量的增长,一个节点未必能把一个Index存储下来。
- 多个分片,在写入或查询的时候就可以并行操作(从各个节点中读写数据,提高吞吐量)
现在问题来了,如果某个节点挂了,那部分数据就丢了吗?显然Elasticsearch也会想到这个问题,所以分片会有主分片和副本分片之分(为了实现高可用)
数据写入的时候是写到主分片,副本分片会复制主分片的数据,读取的时候主分片和副本分片都可以读。
Index需要分为多少个主分片和副本分片都是可以通过配置设置的
如果某个节点挂了,前面所提高的Master Node
就会把对应的副本分片提拔为主分片,这样即便节点挂了,数据就不会丢。
到这里我们可以简单总结一下Elasticsearch的架构了:
Elasticsearch 写入的流程
上面我们已经知道当我们向Elasticsearch写入数据的时候,是写到主分片上的,我们可以了解更多的细节。
客户端写入一条数据,到Elasticsearch集群里边就是由节点来处理这次请求:
集群上的每个节点都是coordinating node
(协调节点),协调节点表明这个节点可以做路由。比如节点1接收到了请求,但发现这个请求的数据应该是由节点2处理(因为主分片在节点2上),所以会把请求转发到节点2上。
- coodinate(协调)节点通过hash算法可以计算出是在哪个主分片上,然后路由到对应的节点
shard = hash(document_id) % (num_of_primary_shards)
路由到对应的节点以及对应的主分片时,会做以下的事:
- 将数据写到内存缓存区
- 然后将数据写到translog缓存区
- 每隔1s数据从buffer中refresh到FileSystemCache中,生成segment文件,一旦生成segment文件,就能通过索引查询到了
- refresh完,memory buffer就清空了。
- 每隔5s中,translog 从buffer flush到磁盘中
- 定期/定量从FileSystemCache中,结合translog内容
flush index
到磁盘中。
解释一下:
- Elasticsearch会把数据先写入内存缓冲区,然后每隔1s刷新到文件系统缓存区(当数据被刷新到文件系统缓冲区以后,数据才可以被检索到)。所以:Elasticsearch写入的数据需要1s才能查询到
- 为了防止节点宕机,内存中的数据丢失,Elasticsearch会另写一份数据到日志文件上,但最开始的还是写到内存缓冲区,每隔5s才会将缓冲区的刷到磁盘中。所以:Elasticsearch某个节点如果挂了,可能会造成有5s的数据丢失。
- 等到磁盘上的translog文件大到一定程度或者超过了30分钟,会触发commit操作,将内存中的segement文件异步刷到磁盘中,完成持久化操作。
说白了就是:写内存缓冲区(定时去生成segement,生成translog),能够让数据能被索引、被持久化。最后通过commit完成一次的持久化。
等主分片写完了以后,会将数据并行发送到副本集节点上,等到所有的节点写入成功就返回ack给协调节点,协调节点返回ack给客户端,完成一次的写入。
Elasticsearch更新和删除
Elasticsearch的更新和删除操作流程:
- 给对应的
doc
记录打上.del
标识,如果是删除操作就打上delete
状态,如果是更新操作就把原来的doc
标志为delete
,然后重新新写入一条数据
前面提到了,每隔1s会生成一个segement 文件,那segement文件会越来越多越来越多。Elasticsearch会有一个merge任务,会将多个segement文件合并成一个segement文件。
在合并的过程中,会把带有delete
状态的doc
给物理删除掉。
Elasticsearch查询
查询我们最简单的方式可以分为两种:
- 根据ID查询doc
- 根据query(搜索词)去查询匹配的doc
public TopDocs search(Query query, int n); public Document doc(int docID);
根据ID去查询具体的doc的流程是:
- 检索内存的Translog文件
- 检索硬盘的Translog文件
- 检索硬盘的Segement文件
根据query去匹配doc的流程是:
- 同时去查询内存和硬盘的Segement文件
从上面所讲的写入流程,我们就可以知道:Get(通过ID去查Doc是实时的),Query(通过query去匹配Doc是近实时的)
- 因为segement文件是每隔一秒才生成一次的
Elasticsearch查询又分可以为三个阶段:
- QUERY_AND_FETCH(查询完就返回整个Doc内容)
- QUERY_THEN_FETCH(先查询出对应的Doc id ,然后再根据Doc id 匹配去对应的文档)
- DFS_QUERY_THEN_FETCH(先算分,再查询)
- 「这里的分指的是 词频率和文档的频率(Term Frequency、Document Frequency)众所周知,出现频率越高,相关性就更强」
一般我们用得最多的就是QUERY_THEN_FETCH,第一种查询完就返回整个Doc内容(QUERY_AND_FETCH)只适合于只需要查一个分片的请求。
QUERY_THEN_FETCH总体的流程流程大概是:
- 客户端请求发送到集群的某个节点上。集群上的每个节点都是coordinate node(协调节点)
- 然后协调节点将搜索的请求转发到所有分片上(主分片和副本分片都行)
- 每个分片将自己搜索出的结果
(doc id)
返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果。 - 接着由协调节点根据
doc id
去各个节点上拉取实际的document
数据,最终返回给客户端。
Query Phase阶段时节点做的事:
- 协调节点向目标分片发送查询的命令(转发请求到主分片或者副本分片上)
- 数据节点(在每个分片内做过滤、排序等等操作),返回
doc id
给协调节点
Fetch Phase阶段时节点做的是:
- 协调节点得到数据节点返回的
doc id
,对这些doc id
做聚合,然后将目标数据分片发送抓取命令(希望拿到整个Doc记录) - 数据节点按协调节点发送的
doc id
,拉取实际需要的数据返回给协调节点
主流程我相信大家也不会太难理解,说白了就是:由于Elasticsearch是分布式的,所以需要从各个节点都拉取对应的数据,然后最终统一合成给客户端
只是Elasticsearch把这些活都干了,我们在使用的时候无感知而已。
最后
这篇文章主要对Elasticsearch简单入了个门,实际使用肯定还会遇到很多坑,但我目前就到这里就结束了。