ElasticSearch面试

Posted 价值成长

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ElasticSearch面试相关的知识,希望对你有一定的参考价值。

Welcome to Elastic Docs | Elastic

1. ES的结构和底层实现

ES的整体结构?

  • 一个 ES Index 在集群模式下,有多个 Node (节点)组成。每个节点就是 ES 的Instance (实例)。
  • 每个节点上会有多个 shard (分片), P1 P2 是主分片, R1 R2 是副本分片
  • 每个分片上对应着就是一个 Lucene Index(底层索引文件)
  • Lucene Index 是一个统称
    • 由多个 Segment (段文件,就是倒排索引)组成。每个段文件存储着就是 Doc 文档。
    • commit point记录了所有 segments 的信息
  • 底层和数据文件?
    • 倒排索引(词典+倒排表)
    • doc values - 列式存储
    • 正向文件 - 行式存储

2. 倒排索引:倒排针对的是正排。

1)正排就是我记得我电脑有个文档,讲了 ES 的常见问题总结。那么我就找到文档,从上往下翻
页,找到 ES 的部分。通过文档找文档内容。
2)倒排:一个 txt 文件 ES 的常见问题 -> D:/分布式问题总结.doc。
所以倒排就是文档内容找文档。当然内容不是全部的,否则也不需要找文档了,内容就是几个分词
而已。这里的 txt 就是搜索引擎。
通俗解释:
传统的我们的检索是通过文章,逐个遍历找到对应关键词的位置。
而倒排索引,是通过分词策略,形成了词和文章的映射关系表,这种词典+映射表即为倒排索引。
有了倒排索引,就能实现 o(1)时间复杂度的效率检索文章了,极大的提高了检索效率。

学术解释:

倒排索引,相反于一篇文章包含了哪些词,它从词出发,记载了这个词在哪些文档中出现过,由两部分组成——词典和倒排表。
加分项:倒排索引的底层实现是基于:FST(Finite State Transducer)数据结构。
lucene 从 4+版本后开始大量使用的数据结构是 FST。FST 有两个优点:
1)空间占用小。通过对词典中单词前缀和后缀的重复利用,压缩了存储空间;
2)查询速度快。O(len(str))的查询时间复杂度。

3. ES和数据库的对比

4.  能说说ElasticSearch 写索引的逻辑吗?

ElasticSearch 是集群的 = 主分片 + 副本分片
写索引只能写主分片,然后主分片同步到副本分片上。但主分片不是固定的,可能网络原因,之前
还是 Node1 是主分片,后来就变成了 Node2 经过选举成了主分片了。
客户端如何知道哪个是主分片呢? 看下面过程。
1) 客户端向某个节点 NodeX 发送写请求
2) NodeX 通过文档信息,请求会转发到主分片的节点上
3) 主分片处理完,通知到副本分片同步数据,向 Nodex 发送成功信息
4) Nodex 将处理结果返回给客户端

5. 熟悉ElasticSearch 集群中搜索数据的过程吗?

1)客户端向集群发送请求,集群随机选择一个 NodeX 处理这次请求。
2)Nodex 先计算文档在哪个主分片上,比如是主分片 A,它有三个副本 A1,A2,A3。那么请求
会轮询三个副本中的一个完成请求。
3)如果无法确认分片,比如检索的不是一个文档,就遍历所有分片。

补充一点,一个节点的存储量是有限的,于是有了分片的概念。但是分片可能有丢失,于是有了副
本的概念。比如:ES 集群有 3 个分片,分片 A、分片 B、分片 C,那么分片 A + 分片 B + 分片 C = 所有数据,每个分片只有大概 1/3。分片 A 又有副本 A1 A2 A3,数据都是一样的
 

6. ES底层数据持久化的过程?

通过分步骤看数据持久化过程write -> refresh -> flush -> merge

(1)write

一个新文档过来,会存储在 in-memory buffer 内存缓存区中,顺便会记录 Translog(Elasticsearch 增加了一个 translog ,或者叫事务日志,在每一次对 Elasticsearch 进行操作时均进行了日志记录)。

(2)refresh 

in-memory buffer 中的文档写入到新的 segment 中,清空 in-memory buffer。文档经过 refresh 后, segment 暂时写到文件系统缓存数据只有被 refresh 后,才可以被搜索到。refresh 默认 1 秒钟,一般建议稍微延长这个 refresh 时间间隔,比如 5 s。因此,ES 其实就是准实时,达不到真正的实时。

(3)flush 

文档从文件缓存写入磁盘的过程就是 flush。写入磁盘后,清空 translog。

(4)merge 

Elasticsearch通过在后台进行Merge Segment来解决这个问题。小的段被合并到大的段,然后这些大的段再被合并到更大的段。

7. ES遇到什么性能问题,如何优化的?

  • 硬件配置优化 包括三个因素:CPU、内存和 IO。

    • CPU: 大多数 Elasticsearch 部署往往对 CPU 要求不高; CPUs 和更多的核数之间选择,选择更多的核数更好。多个内核提供的额外并发远胜过稍微快一点点的时钟频率。
    • 内存
      • 配置: 由于 ES 构建基于 lucene,而 lucene 设计强大之处在于 lucene 能够很好的利用操作系统内存来缓存索引数据,以提供快速的查询性能。lucene 的索引文件 segements 是存储在单文件中的,并且不可变,对于 OS 来说,能够很友好地将索引文件保持在 cache 中,以便快速访问;因此,我们很有必要将一半的物理内存留给 lucene;另一半的物理内存留给 ES(JVM heap)。
      • 禁止 swap 禁止 swap,一旦允许内存与磁盘的交换,会引起致命的性能问题。可以通过在 elasticsearch.yml 中 bootstrap.memory_lock: true,以保持 JVM 锁定内存,保证 ES 的性能。
      • 垃圾回收器: 已知JDK 8附带的HotSpot JVM的早期版本存在一些问题,当启用G1GC收集器时,这些问题可能导致索引损坏。受影响的版本早于JDK 8u40随附的HotSpot版本。如果你使用的JDK8较高版本,或者JDK9+,我推荐你使用G1 GC; 因为我们目前的项目使用的就是G1 GC,运行效果良好,对Heap大对象优化尤为明显。
    • 磁盘 在经济压力能承受的范围下,尽量使用固态硬盘(SSD)
  • 索引方面优化

    • 批量提交 当有大量数据提交的时候,建议采用批量提交(Bulk 操作);此外使用 bulk 请求时,每个请求不超过几十M,因为太大会导致内存使用过大。
    • 增加 Refresh 时间间隔 为了提高索引性能,Elasticsearch 在写入数据的时候,采用延迟写入的策略,即数据先写到内存中,当超过默认1秒(index.refresh_interval)会进行一次写入操作,就是将内存中 segment 数据刷新到磁盘中,此时我们才能将数据搜索出来,所以这就是为什么 Elasticsearch 提供的是近实时搜索功能,而不是实时搜索功能。如果我们的系统对数据延迟要求不高的话,我们可以通过延长 refresh 时间间隔,可以有效地减少 segment 合并压力,提高索引速度。比如在做全链路跟踪的过程中,我们就将 index.refresh_interval 设置为30s,减少 refresh 次数。再如,在进行全量索引时,可以将 refresh 次数临时关闭,即 index.refresh_interval 设置为-1,数据导入成功后再打开到正常模式,比如30s。
    • 索引缓冲的设置可以控制多少内存分配 indices.memory.index_buffer_size 接受一个百分比或者一个表示字节大小的值。默认是10%
    • translog 相关的设置 控制数据从内存到硬盘的操作频率,以减少硬盘 IO。可将 sync_interval 的时间设置大一些。默认为5s。也可以控制 tranlog 数据块的大小,达到 threshold 大小时,才会 flush 到 lucene 索引文件。默认为512m。
    • _id 字段的使用 _id 字段的使用,应尽可能避免自定义 _id,以避免针对 ID 的版本管理;建议使用 ES 的默认 ID 生成策略或使用数字类型 ID 做为主键。
    • _all 字段及 _source 字段的使用 _all 字段及 _source 字段的使用,应该注意场景和需要,_all 字段包含了所有的索引字段,方便做全文检索,如果无此需求,可以禁用;_source 存储了原始的 document 内容,如果没有获取原始文档数据的需求,可通过设置 includes、excludes 属性来定义放入 _source 的字段。
    • 合理的配置使用 index 属性 合理的配置使用 index 属性,analyzed 和 not_analyzed,根据业务需求来控制字段是否分词或不分词。只有 groupby 需求的字段,配置时就设置成 not_analyzed,以提高查询或聚类的效率。
  • 查询方面优化

    • Filter VS Query
    • 深度翻页 使用 Elasticsearch scroll 和 scroll-scan 高效滚动的方式来解决这样的问题。也可以结合实际业务特点,文档 id 大小如果和文档创建时间是一致有序的,可以以文档 id 作为分页的偏移量,并将其作为分页查询的一个条件。
    • 避免层级过深的聚合查询, 层级过深的aggregation , 会导致内存、CPU消耗,建议在服务层通过程序来组装业务,也可以通过pipeline的方式来优化。
    • 通过开启慢查询配置定位慢查询

8. elasticsearch 是如何实现 master 选举的?

9. 在并发情况下,Elasticsearch 如果保证读写一致?

1)可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖,由应用层来处理具体的冲突;
2)另外对于写操作,一致性级别支持 quorum/one/all,默认为 quorum,即只有当大多数分片可用时才允许写操作。但即使大多数可用,也可能存在因为网络等原因导致写入副本失败,这样该副本被认为故障,分片将会在一个不同的节点上重建。
3)对于读操作,可以设置 replication 为 sync(默认),这使得操作在主分片和副本分片都完成后才会返回;如果设置 replication 为 async 时,也可以通过设置搜索请求参数_preference 为 primary 来查询主分片,确保文档是最新版本。

10. ElasticSearch中的副本是什么?

一个索引被分解成碎片以便于分发和扩展。副本是分片的副本。一个节点是一个属于一个集群的
ElasticSearch的运行实例。一个集群由一个或多个共享相同集群名称的节点组成。

11. 介绍下你们电商搜索的整体技术架构

12. Elasticsearch 对于大数据量(上亿量级)的聚合如何实现?

Elasticsearch 提供的首个近似聚合是 cardinality 度量。它提供一个字段的基数,即该字段的 distinct 或者 unique 值的数目。它是基于 HLL 算法的。HLL 会先对我们的输入作哈希运算,然后根据哈希运算的结果中的 bits 做概率估算从而得到基数。其特点是:可配置的精度,用来控制内存的使用(更精确 = 更多内存);小的数据集精度是非常高的;我们可以通过配置参数,来设置去重需要的固定内存使用量。无论数千还是数十亿的唯一值,内存使用量只与你配置的精确度相关。

13. ES查询和聚合的有哪些方式?

  • DSL
    • 基于文本 - match, query string
    • 基于词项 - term
    • 复合查询 - 5种
  • EQL Elastic Query Language
    • bucket
    • metric
    • pipline
  • SQL

14. ES查询中query和filter的区别?

query 是查询+score, 而filter仅包含查询, 比如在复合查询中constant_score查询无需计算score,所以对应查询是filter而不是query。

15. ES查询中match和term的区别?

term是基于索引的词项,而match基于文本。

16. ES查询中should和must的区别?

should是任意匹配,must是同时匹配。

17. ES查询中match,match_phrase和match_phrase_prefix有什么区别?

match本质上是对term组合,match_phrase本质是连续的term的查询(and关系),match_phrase_prefix在match_phrase基础上提供了一种可以查最后一个词项是前缀的方法

18. ES聚合中的管道聚合有哪些?如何理解?

简单而言:让上一步的聚合结果成为下一个聚合的输入,这就是管道。

如何理解?

第一个维度:管道聚合有很多不同类型,每种类型都与其他聚合计算不同的信息,但是可以将这些类型分为两类:

  • 父级 父级聚合的输出提供了一组管道聚合,它可以计算新的存储桶或新的聚合以添加到现有存储桶中。
  • 兄弟 同级聚合的输出提供的管道聚合,并且能够计算与该同级聚合处于同一级别的新聚合。

第二个维度:根据功能设计的意图

比如前置聚合可能是Bucket聚合,后置的可能是基于Metric聚合,那么它就可以成为一类管道

进而引出了:xxx bucket

  • Bucket聚合 -> Metric聚合: bucket聚合的结果,成为下一步metric聚合的输入
    • Average bucket
    • Min bucket
    • Max bucket
    • Sum bucket
    • Stats bucket
    • Extended stats bucket

19. ES聚合中的Bucket聚合有哪些?如何理解?

 20. ES聚合中的Metric聚合有哪些?如何理解?

  1. 从分类看:Metric聚合分析分为单值分析多值分析两类
  2. 从功能看:根据具体的应用场景设计了一些分析api, 比如地理位置,百分数等等
  • 单值分析: 只输出一个分析结果
    • 标准stat型
      • avg 平均值
      • max 最大值
      • min 最小值
      • sum
      • value_count 数量
    • 其它类型
      • cardinality 基数(distinct去重)
      • weighted_avg 带权重的avg
      • median_absolute_deviation 中位值
  • 多值分析: 单值之外的
    • stats型
      • stats 包含avg,max,min,sum和count
      • matrix_stats 针对矩阵模型
      • extended_stats
      • string_stats 针对字符串
    • 百分数型
      • percentiles 百分数范围
      • percentile_ranks 百分数排行
    • 地理位置型
      • geo_bounds Geo bounds
      • geo_centroid Geo-centroid
      • geo_line Geo-Line
    • Top型
      • top_hits 分桶后的top hits
      • top_metrics

21.ES性能优化之缓存

Elasticsearch 应用时会使用各种缓存,而缓存是加快数据检索速度的王道。接下来,我们将着重介绍以下三种缓存:
➢ 页缓存:操作系统级别的缓存
➢ 分片级请求缓存:节点级别的缓存
➢ 查询缓存:功能缓存

其他提高检索速度的方法:

1)从操作系统的角度提高IO效率

2)从jvm的角度提高IO效率

22. mysql-elasticsearch数据同步方案

(1) 程序同步
直接在代码里写逻辑,数据在增删改查进数据库的同时,也往es里同步一份。
优点:方便,无需集成其他的技术;
缺点:代码耦合性太高,增加接口的处理时间;
(2) logstash
定时查询数据库,查询到数据有变化就发送到es中
优点:解耦,官方推荐。
缺点:
1)不支持同步删除,当然可以在数据库用逻辑删除代替物理删除,对于logstash来说就是更新操作了;
2)大数据量有性能问题,在对数据库的压力上,logstash的原理是定时扫描变动的表,所以对数据库有一定压力,并且如果有其他程序在进行某条语句更新,锁住了这条行数据,那logstash读取数据时,就会被“卡住”,如果这个时间过长,可能会影响服务器卡死。
3)无法做到实时同步,只能秒级同步。

如果实时性要求不高,并且定时时间内数据变化量不大,推荐使用这个,学习维护成本比较低,毕竟是官方推荐,ELK全家桶。
(3) canal
利用数据库的binlog同步变化数据,然后将数据发送给es,当然也可以通过java代码监听拿到数据,再发送到es或做其他处理。
优点:解耦,实时同步,没有大数据性能压力
缺点:学习和维护成本高,要求对数据库有创建用户的权限,毕竟要用到数据库同步功能,开启binlog数据库的压力也会增加。
(4) MQ中间件
有数据变化的时候,就通知mq,然后监听mq实现数据同步到mq。
优点:解耦,适合高并发
缺点:如何保证消息可靠性,需要在业务代码中加入发送消息到MQ的代码。

mysql-elasticsearch数据同步方案_码里法的博客-CSDN博客

以上是关于ElasticSearch面试的主要内容,如果未能解决你的问题,请参考以下文章

基数排序再预习——解答一道面试题

拜托,面试别再问我基数排序了!!!

面试官:给我手撕一下基数排序,再考虑一下如何进行改进呢?

根据 Elasticsearch 的交互式反馈更新分数

程序员笔试面试必会——排序②Python实现 计数排序基数排序

当结果具有相同分数时在 Elasticsearch 中分页