「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」
前言
- 关于作者:励志不秃头的一个CURD的Java农民工
- 关于文章:这是秃头系列的第二个系列——Elasticsearch,在这个系列,不会有es的api的curd,关于es的简单使用,现在网上的博客或者官网都可以帮助到大家,所有这里就不再赘述了,这里只有一些es的原理描述以及我在es使用上的一些心得,同时会总结es面试中常见的问题,希望对大家有一定的帮助
正文
Elasticsearch 是一个开源的基于Apache Lucene的分布式的RESTful 风格的搜索和数据分析引擎
关键慨念
-
节点(Node) :物理概念,一个运行的Elasticearch实例。
-
索引(Index):逻辑概念,相当于关系型数据库中的一张表。
-
分片(Shard):为了支持更大量的数据,索引一般会按某个维度分成多个部分,每个部分就是一个分片,分片有两种,主分片和副本分片,一个分片就是一个lucene索引,一个lucene索引由多个segment组成。
-
副本(Replica):同一个分片(Shard)的备份数据,一个分片可能会有0个或多个副本。
-
倒排索引:
在搜索引擎中,每个文档都有一个对应的文档 ID,文档内容被表示为一系列关键词的集合。例如,文档 1 经过分词,提取了 20 个关键词,每个关键词都会记录它在文档中出现的次数和出现位置。
那么,倒排索引就是关键词到文档 ID 的映射,每个关键词都对应着一系列的文件,这些文件中都出现了关键词。举个例子:
有以下文档:DocId Doc 1 谷歌地图之父跳槽 Facebook 2 谷歌地图之父加盟 Facebook 3 谷歌地图创始人拉斯离开谷歌加盟 Facebook 4 谷歌地图之父跳槽 Facebook 与 Wave 项目取消有关 5 谷歌地图之父拉斯加盟社交网站 Facebook 对文档进行分词之后,得到以下**倒排索引**。WordId Word DocIds 1 谷歌 1,2,3,4,5 2 地图 1,2,3,4,5 3 之父 1,2,4,5 4 跳槽 1,4 5 Facebook 1,2,3,4,5 6 加盟 2,3,5 7 创始人 3 8 拉斯 3,5 9 离开 3 10 与 4 .. .. .. - 另外,实用的倒排索引还可以记录更多的信息,比如文档频率信息,表示在文档集合中有多少个文档包含某个单词。 - 那么,有了倒排索引,搜索引擎可以很方便地响应用户的查询。比如用户输入查询 Facebook,搜索系统查找倒排索引,从中读出包含这个单词的文档,这些文档就是提供给用户的搜索结果。
集群部署
- 节点角色 : master、data、data_content、data_hot、data_warm、data_cold、data_frozen、ingest、ml、remote_cluster_client、transform
- 部署方式 :
- 混合式部署
- 简单快速易上手
- 微小集群
- 分层部署
- 各角色节点互不影响
- 不同角色节点分配不同性能的机器
- 水平扩展
- 大集群
- 混合式部署
分布式架构原理**
-
一个索引可以拆分成多个 shard ,每个shard存储部分数据
- 支持横向扩展
- 提高性能,数据分布在多个shard上,即多台服务器上,所有的操作,都会在多台机器上并行分布执行,提高了吞吐量和性能
- 这个 shard 的数据实际是有多个备份,就是说每个 shard 都有一个 primary shard,负责写入数据,但是还有几个 replica shard。primary shard 写入数据之后,会将数据同步到其他几个 replica shard 上去
-
通过这个 replica 的方案,每个 shard 的数据都有多个备份,如果某个机器宕机了,还有别的数据副本在别的机器上。实现了高可用
-
es 集群多个节点,会自动选举一个节点为 master 节点,这个 master 节点其实就是干一些管理的工作的,比如维护索引元数据、负责切换 primary shard 和 replica shard 身份等。要是 master 节点宕机了,那么会重新选举一个节点为 master 节点。
-
如果是非 master节点宕机了,那么会由 master 节点,让那个宕机节点上的 primary shard 的身份转移到其他机器上的 replica shard。接着你要是修复了那个宕机机器,重启了之后,master 节点会控制将缺失的 replica shard 分配过去,同步后续修改的数据之类的,让集群恢复正常。
- 说得更简单一点,就是说如果某个非 master 节点宕机了。那么此节点上的 primary shard 不就没了。那好,master 会让 primary shard 对应的 replica shard(在其他机器上)切换为 primary shard。如果宕机的机器修复了,修复后的节点也不再是 primary shard,而是 replica shard。
写入数据的过程原理
写入数据过程
- 客户端选择一个 node 发送请求过去,这个 node 就是 coordinating node(协调节点)。
- coordinating node 对 document 进行路由,将请求转发给对应的 node(有 primary shard)。
- 实际的 node 上的 primary shard 处理请求,然后将数据同步到 replica node。
- coordinating node 如果发现 primary node 和所有 replica node 都搞定之后,就返回响应结果给客户端。
写入数据原理
- 先写入内存buffer,在buffer里的时候数据不能被搜索到,同时将数据写入到 translog 日志文件
- 如果buffer快满了,或者到一定的时间,就会将内存buffer数据 refresh 到 一个新的 segment file,但是此时数据不是直接进入 segment file 磁盘文件,而是先进入 os cache 。这个过程就是 refresh。
- 每隔 1 秒钟,es 将 buffer 中的数据写入一个新的 segment file,每秒钟会产生一个新的磁盘文件 segment file,这个 segment file 中就存储最近 1 秒内 buffer 中写入的数据。
- 但是如果 buffer 里面此时没有数据,那当然不会执行 refresh 操作,如果 buffer 里面有数据,默认 1 秒钟执行一次 refresh 操作,刷入一个新的 segment file 中。
- 操作系统里面,磁盘文件其实都有一个东西,叫做 os cache,即操作系统缓存,就是说数据写入磁盘文件之前,会先进入 os cache,先进入操作系统级别的一个内存缓存中去。只要 buffer 中的数据被 refresh 操作刷入 os cache中,这个数据就可以被搜索到了。
- 重复上面的步骤,新的数据不断进入 buffer 和 translog,不断将 buffer 数据写入一个又一个新的 segment file 中去,每次 refresh 完 buffer 清空,translog 保留。随着这个过程推进,translog 会变得越来越大。当 translog 达到一定长度的时候,就会触发 commit 操作。
commit操作
- 将 buffer 中现有数据 refresh 到 os cache 中去,清空 buffer
- 将一个 commit point 写入磁盘文件,里面标识着这个 commit point 对应的所有 segment file,同时强行将 os cache 中目前所有的数据都 fsync 到磁盘文件中去
- 最后清空 现有 translog 日志文件,重启一个 translog,此时 commit 操作完成。 commit 操作叫做 flush。默认 30 分钟自动执行一次 flush,但如果 translog 过大,也会触发 flush。flush 操作就对应着 commit 的全过程,可以通过 es api,手动执行 flush 操作,手动将 os cache 中的数据 fsync 强刷到磁盘上去。
面试题:为什么叫ES是准实时的?**
因为es是默认每隔1秒refresh一次的,所以es是准实时的,因为写入的数据1秒之后才能被看到;或者通过 es 的 restful api 或者 java api,手动执行一次 refresh 操作,就是手动将 buffer 中的数据刷入 os cache中,让数据立马就可以被搜索到。
只要数据被输入 os cache 中,buffer 就会被清空了,因为不需要保留 buffer 了,数据在 translog 里面已经持久化到磁盘去一份了。
translog 日志文件的作用
- 在执行 commit 操作之前,数据要么是停留在 buffer 中,要么是停留在 os cache 中,无论是 buffer 还是 os cache 都是内存,一旦这台机器死了,内存中的数据就全丢了。所以需要将数据对应的操作写入一个专门的日志文件 translog 中,一旦此时机器宕机,再次重启的时候,es 会自动读取 translog 日志文件中的数据,恢复到内存 buffer 和 os cache 中去。
- translog 其实也是先写入 os cache 的,默认每隔 5 秒刷一次到磁盘中去,所以默认情况下,可能有 5 秒的数据会仅仅停留在 buffer 或者 translog 文件的 os cache 中,如果此时机器挂了,会丢失 5 秒钟的数据。但是这样性能比较好,最多丢 5 秒的数据。也可以将 translog 设置成每次写操作必须是直接 fsync 到磁盘,但是性能会差很多。
总结
数据先写入内存 buffer,然后每隔 1s,将数据 refresh 到 os cache,到了 os cache 数据就能被搜索到(所以我们才说 es 从写入到能被搜索到,中间有 1s 的延迟)。每隔 5s,将数据写入 translog 文件(这样如果机器宕机,内存数据全没,最多会有 5s 的数据丢失),translog 大到一定程度,或者默认每隔 30mins,会触发 commit 操作,将缓冲区的数据都 flush 到 segment file 磁盘文件中。数据写入 segment file 之后,同时就建立好了倒排索引。
读取数据过程
可以通过 doc id 来查询,会根据 doc id 进行 hash,判断出来当时把 doc id 分配到了哪个 shard 上面去,从那个 shard 去查询。
- 客户端发送请求到任意一个 node,成为 coordinate node。
- coordinate node 对 doc id 进行哈希路由,将请求转发到对应的 node,此时会使用 round-robin 随机轮询算法,在 primary shard 以及其所有 replica 中随机选择一个,让读请求负载均衡。
- 接收请求的 node 返回 document 给 coordinate node。
- coordinate node 返回 document 给客户端。
写请求是写入 primary shard,然后同步给所有的 replica shard;读请求可以从 primary shard 或 replica shard 读取,采用的是随机轮询算法。
搜索数据过程
es 最强大的是做全文检索
- 客户端发送请求到一个 coordinate node。
- 协调节点将搜索请求转发到所有的 shard 对应的 primary shard 或 replica shard,都可以。
- query phase:每个 shard 将自己的搜索结果(其实就是一些 doc id)返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果。
- fetch phase:接着由协调节点根据 doc id 去各个节点上拉取实际的 document 数据,最终返回给客户端。
假设你有三条数据:
Hello World
Hello Java
Hello JavaEE
你根据Java关键词来搜索,将包含 Java的 document 给搜索出来。es 就会给你返回:Hello Java、Hello JavaEE
删除/更新数据原理
删除:
- commit 的时候会生成一个 .del 文件,里面将某个 doc 标识为 deleted 状态,那么搜索的时候根据 .del 文件就知道这个 doc 是否被删除了。 更新:
- 将原来的 doc 标识为 deleted 状态,然后新写入一条数据。
- buffer 每 refresh 一次,就会产生一个 segment file,所以默认情况下是 1 秒钟一个 segment file,这样下来 segment file 会越来越多,此时会定期执行 merge。每次 merge 的时候,会将多个 segment file 合并成一个,同时这里会将标识为 deleted 的 doc 给物理删除掉,然后将新的 segment file 写入磁盘,这里会写一个 commit point,标识所有新的 segment file,然后打开 segment file 供搜索使用,同时删除旧的 segment file。
后言
这是秃头系列关于Elasticsearch的第一篇文章,主要讲了一些理论知识,这些知识可以让我们更加了解es,对我们以后在es的调优也有一定的帮助。我是新生代农民工L_Denny,我们下篇文章再见。