共计 4219 个字符,预计需要花费 11 分钟才能阅读完成。
起源:8rr.co/GsAa
面试题
ES 写入数据的工作原理是什么啊?ES 查问数据的工作原理是什么啊?底层的 Lucene 介绍一下呗?倒排索引理解吗?
面试官心理剖析
问这个,其实面试官就是要看看你理解不理解 es 的一些基本原理,因为用 es 无非就是写入数据,搜寻数据。你要是不明确你发动一个写入和搜寻申请的时候,es 在干什么,那你真的是 ……
对 es 根本就是个黑盒,你还无能啥?你惟一无能的就是用 es 的 api 读写数据了。要是出点什么问题,你啥都不晓得,那还能指望你什么呢?
面试题分析
es 写数据过程
- 客户端抉择一个 node 发送申请过来,这个 node 就是
coordinating node
(协调节点)。 coordinating node
对 document 进行 路由,将申请转发给对应的 node(有 primary shard)。- 理论的 node 上的
primary shard
解决申请,而后将数据同步到replica node
。 coordinating node
如果发现primary node
和所有replica node
都搞定之后,就返回响应后果给客户端。
es-write
es 读数据过程
能够通过 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 给客户端。
es 搜寻数据过程
es 最弱小的是做全文检索,就是比方你有三条数据:
java 真好玩儿啊
java 好难学啊
j2ee 特地牛 Copy to clipboardErrorCopied
你依据 java
关键词来搜寻,将蕴含 java
的 document
给搜寻进去。es 就会给你返回:java 真好玩儿啊,java 好难学啊。
- 客户端发送申请到一个
coordinate node
。 - 协调节点将搜寻申请转发到 所有 的 shard 对应的
primary shard
或replica shard
,都能够。 - query phase:每个 shard 将本人的搜寻后果(其实就是一些
doc id
)返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终后果。 - fetch phase:接着由协调节点依据
doc id
去各个节点上 拉取理论 的document
数据,最终返回给客户端。
写申请是写入 primary shard,而后同步给所有的 replica shard;读申请能够从 primary shard 或 replica shard 读取,采纳的是随机轮询算法。
写数据底层原理
es-write-detail
先写入内存 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
中,这个数据就能够被搜寻到了。
为什么叫 es 是 准实时 的?NRT
,全称 near real-time
。默认是每隔 1 秒 refresh 一次的,所以 es 是准实时的,因为写入的数据 1 秒之后能力被看到。能够通过 es 的 restful api
或者 java api
,手动 执行一次 refresh 操作,就是手动将 buffer 中的数据刷入 os cache
中,让数据立马就能够被搜寻到。只有数据被输出 os cache
中,buffer 就会被清空了,因为不须要保留 buffer 了,数据在 translog 外面曾经长久化到磁盘去一份了。
反复下面的步骤,新的数据一直进入 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 强刷到磁盘下来。
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
到磁盘,然而性能会差很多。
实际上你在这里,如果面试官没有问你 es 丢数据的问题,你能够在这里给面试官炫一把,你说,其实 es 第一是准实时的,数据写入 1 秒后能够搜寻到;可能会失落数据的。有 5 秒的数据,停留在 buffer、translog os cache、segment file os cache 中,而不在磁盘上,此时如果宕机,会导致 5 秒的 数据失落。
总结一下,数据先写入内存 buffer,而后每隔 1s,将数据 refresh 到 os cache,到了 os cache 数据就能被搜寻到(所以咱们才说 es 从写入到能被搜寻到,两头有 1s 的提早)。每隔 5s,将数据写入 translog 文件(这样如果机器宕机,内存数据全没,最多会有 5s 的数据失落),translog 大到肯定水平,或者默认每隔 30mins,会触发 commit 操作,将缓冲区的数据都 flush 到 segment file 磁盘文件中。
数据写入 segment file 之后,同时就建设好了倒排索引。
删除 / 更新数据底层原理
如果是删除操作,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
。
底层 lucene
简略来说,lucene 就是一个 jar 包,外面蕴含了封装好的各种建设倒排索引的算法代码。咱们用 Java 开发的时候,引入 lucene jar,而后基于 lucene 的 api 去开发就能够了。
通过 lucene,咱们能够将已有的数据建设索引,lucene 会在本地磁盘下面,给咱们组织索引的数据结构。
倒排索引
在搜索引擎中,每个文档都有一个对应的文档 ID,文档内容被示意为一系列关键词的汇合。例如,文档 1 通过分词,提取了 20 个关键词,每个关键词都会记录它在文档中呈现的次数和呈现地位。
那么,倒排索引就是 关键词到文档 ID 的映射,每个关键词都对应着一系列的文件,这些文件中都呈现了关键词。
举个栗子。
有以下文档:
另外,实用的倒排索引还能够记录更多的信息,比方文档频率信息,示意在文档汇合中有多少个文档蕴含某个单词。
那么,有了倒排索引,搜索引擎能够很不便地响应用户的查问。比方用户输出查问 Facebook
,搜寻零碎查找倒排索引,从中读出蕴含这个单词的文档,这些文档就是提供给用户的搜寻后果。
要留神倒排索引的两个重要细节:
- 倒排索引中的所有词项对应一个或多个文档;
- 倒排索引中的词项 依据字典程序升序排列
下面只是一个简略的例子,并没有严格依照字典程序升序排列。