面试集中营—ElasticSearch架构篇

2024-04-27 06:44

本文主要是介绍面试集中营—ElasticSearch架构篇,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、为什么用ElasticSearch?

       1、支持多种数据类型。它可以处理非结构化、数值和地理信息等多种类型的数据;

       2、简单的RESTful API。ES提供了一个简单易用的RESTful API,使得它可以从任何编程语言中调用,降低了学习的曲线。

       3、近实时搜索。ES每隔1秒将数据存储至系统缓存中,使用倒排索引提高检索效率,使得搜索数据变得快速且高效。

       4、支持相关性搜索。它可以根据条件对搜索结果进行打分,提供了基于文档的全文检索能力。

       5、天然分布式存储。ES是分布式的,使用分片支持处理PB级的数据量,易于扩展,可部署在数百台服务器的集群中。

      6、降低全文检索的学习曲线。它可以被任何编程语言调用,使得开发变得更加容易。

      7、高可用性。由于其分布式特性,ES可以通过添加更多节点来分担负载,增加可靠性,无需对应用进行任何改动。

二、ES基本概念名词

Cluster

       代表一个集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的。ES的一个概念就是去中心化,字面上理解就是无中心节点,这是对于集群外部来说的,因为从外部来看es集群,在逻辑上是个整体,你与任何一个节点的通信和与整个es集群通信是等价的。

Shards

        代表索引分片,es可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上。构成分布式搜索。分片的数量只能在索引创建前指定,并且索引创建后不能更改

replicas

        代表索引副本,es可以设置多个索引的副本。

        副本的作用:

        一是提高系统的容错性,当某个节点某个分片损坏或丢失时可以从副本中恢复。

        二是提高es的查询效率,es会自动对搜索请求进行负载均衡。

Recovery

        代表数据恢复或叫数据重新分布,es在有节点加入或退出时会根据机器的负载对索引分片进行重新分配;挂掉的节点重新启动时也会进行数据恢复。

三、ES基础架构

     角色

       五大角色:

       Master Node

        主节点,该节点不和应用创建连接,每个节点都保存了集群状态,master节点控制整个集群的元数据。只有master节点可以修改节点状态信息及元数据的处理,比如索引的新增、删除、分片路由分配、所有索引和相关Mapping、Setting配置等等。

       Master eligible nodes

       合格主节点。合格节点,每个节点部署后不修改配置信息,默认就是一个 eligible 节点。有资格成为Master节点但暂时并不是Master的节点被称为 eligible 节点,该节点可以参加选主流程,成为Mastere节点。该节点只是与集群保持心跳,判断Master是否存活,如果Master故障则参加新一轮的Master选举。

       Data Node

        数据节点,用于简历文档索引,接受应用创建连接,接受索引请求,接受用户的搜索请求。是真实春初数据的节点,正常情况下节点数量越多,集群的性能就越强大

       Coordinating Node

        协调节点(/路由节点/client节点) 协调节点,该节点专用与接收应用的查询连接、接受搜索请求,但其本身不负责存储数据。

       协调节点接受客户端搜索请求后将请求转发到与查询条件相关的多个data节点的分片上,然后多个data节点的分片执行查询语句或者查询结果再返回给协调节点,协调节点把各个data节点的返回结果进行整合、排序等一系列操作后再将最终结果返回给用户请求。

        搜索请求在两个阶段中执行(query 和 fetch),这两个阶段由接收客户端请求的节点 - 协调节点协调。在请求query 阶段,协调节点将请求转发到保存数据的数据节点。 每个数据节点在本地执行请求并将其结果返回给协调节点。在收集fetch阶段,协调节点将每个数据节点的结果汇集为单个全局结果集。

       Ingest Node

        ingest 节点可以看作是数据前置处理转换的节点,支持 pipeline管道设置,可以使用 ingest 对数据进行过滤、转换等操作,类似于 logstash 中 filter 的作用,功能相当强大。

        Ingest节点处理时机:在数据被索引之前,通过预定义好的处理管道对数据进行预处理。默认情况下,所有节点都启用Ingest,因此任何节点都可以处理Ingest任务。

        ingest的详细可以参考elasticsearch高可用 原理 (图解+秒懂+史上最全)-CSDN博客

        存储结构

           Elasticsearch -> Index-> Types -> Documents -> Fields

           1、ES的index代表Mysql中的数据库

           2、ES的types代表Mysql中的Tables,新版本中弱化types概念

           3、ES的Document代表Mysql中的行

           4、ES的Fields代表Mysql中的列

           5、ES的mapping代表Mysql中的表结构,

         由于Elasticsearch底层使用了lucene的原因,不支持对mapping的修改,可使用索引重建的方式。而且不能更改类型,为什么不能修改一个字段的type?原因是一个字段的类型修改以后,那么该字段的所有数据都需要重新索引。Elasticsearch底层使用的是lucene库,字段类型修改以后索引和搜索要涉及分词方式等操作,不允许修改类型在我看来是符合lucene机制的。

          存储就是集群,分片,副本。如下图所示

      Lucene

        Lucene是使用Java语言开发的开源的,高性能的查询库。Apache Solr,Apache Nutch,OpenSearch和Elasticsearch都是在Lucene的基础上创建的。Lucene已经有超过20年的历史,是Apache基金会管理的成熟项目。

        Lucene的核心是倒排索引(inverted search index),这是Lucene有快速查询能力的核心。倒排索引提供了关键词与包含关键词的文档的对应关系。在查询的过程中,从排序的列表中快速找到关键词并且找到关键词对应的文档列表。Lucene支持的信息类型包含数字、字符串以及文本类型。Lucene包含丰富的搜索接口,支持自然语言搜索,通配符搜索,模糊搜索和邻近搜索。

      倒排索引

        所谓倒排索引,就是相对于正排索引,正排索引典型例子就是mysql的B+树主键索引,一个索引字段对应一列数据,key是主键ID,value是内容。而倒排索引,key是内容,value是关联的主键列表。如下图所示。

      倒排索引有什么优点呢?

       1、快速查找包含特定单词的文档,这使得它特别适合用于搜索引擎和文本搜索应用。

       2、倒排索引可以快速查找文档集合中包含多个单词的文档,这在文本分类和关键词提取中也很有用、

       缺点:

       1、额外的存储空间来存储索引;

       2、倒排索引在建立和更新时需要一定的时间;

     Lucene中的倒排索引

        lucene就是影响最为广泛的一款倒排索引及搜索工具,lucene把关键字按照字典顺序的存储在磁盘上,这样可以充分的发挥二分查找的优势,加快查找的速度。

      存储内容      

term indexterm dictionaryPosting List
小米1,2...4
华为1,4,6
手机1,2,3...6

        FST

       新增一条数据的过程

        1、分词

        lucene使用字典文件记录所有的关键字,每个关键字不会重复。这个关键字是通过分词来完成的,如果是英文很简单就是按照英文单词来分割,但是中文就需要一个中文的分词器。

        2、分析整理

        下一步就是对分割出来的词进行分析和整理,比如时态统一,去除不需要的词比如“是”,“的”等等

       3、建立对应关系

      最后就是把关键词和对应的文件id关联上,一个关键词可以关联多个文件id。

      对索引的压缩算法

       针对海量的数据,会产生大量的索引文件,如果不对索引进行压缩,可以想见光索引文件就需要大量的硬件资源,同时对于索引的检索也会更加费时。此时压缩算法就非常必要了。

       1、大量的对数字的压缩

       索引中的Posting List不管主键Id是什么数据类型的,统一都是int类型,最大能够表示的正整数是2的31次方减1,当id很大的时候,比如存储两个连续的大整数,比如[1000000000,1000000001]这两个数字压缩之后可能会成为[100000000,1]类似的情况,后面的1表示和前一个数字的差值。因为 Posting List的id不一定是连续的但是肯定是有序的。所以使用差值列表来存储[Posting List]。

      2、关键词压缩

      比如有很多关键词都有相同的文字,比如中国,中国人,中国话,都有中国,那么就会把中国作为一个编码,变成中国,<1,人>,<1,话>

四、Elasticsearch数据备份与恢复

快照和还原机制

        Snapshot: 快照,是Elasticsearch中用于备份数据的核心概念。Snapshot是一个时间点上的数据的完整拷贝,可以用于恢复数据或迁移到其他集群。
        Restore: 还原,是从Snapshot中恢复数据的过程。Restore可以用于恢复单个索引或整个集群。 

        创建快照:curl -X PUT "http://localhost:9200/_snapshot/my_snapshot/snapshot_1?wait_for_completion=true" -H 'Content-Type: application/json' -d' { "indices": "my_index", "ignore_unavailable": true, "include_global_state": false }' 
        恢复快照:curl -X POST "http://localhost:9200/_snapshot/my_snapshot/snapshot_1/_restore" -H 'Content-Type: application/json' -d' { "indices": "my_index", "ignore_unavailable": true }' 或者使用elasticsearch-snapshot工具恢复快照: bin/elasticsearch-snapshot restore my_snapshot snapshot_1

分页查询与插入

        可以使用scroll分页查询的方式,按照时间周期,从源ES中导入到目标ES中。代码如下:

// 设定滚动时间间隔final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L));SearchRequest searchRequest = new SearchRequest(formIndices);searchRequest.scroll(scroll);SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// 设定每次返回多少条数据int number = size + new Random().nextInt(size / 2);searchSourceBuilder.size(number);searchRequest.source(searchSourceBuilder);SearchResponse searchResponse;try {CountRequest countRequest = new CountRequest(formIndices);CountResponse countResponse = client.count(countRequest, getNewOptions());total = countResponse.getCount();searchResponse = client.search(searchRequest, getNewOptions());} catch (IOException e) {LOGGER.error("formIndices:" + formIndices, e);return;}count = count + searchResponse.getHits().getHits().length;String scrollId = searchResponse.getScrollId();SearchHit[] searchHits = searchResponse.getHits().getHits();LOGGER.info("-----首页-----" + System.currentTimeMillis());request = new BulkRequest();for (SearchHit documentFields : searchHits) {Map<String, Object> sourceAsMap = documentFields.getSourceAsMap();String timestamp = (String) sourceAsMap.get("@timestamp");// 过滤pathString path = (String) sourceAsMap.get("path");if(checkDiscardPath(path)){continue;}if (!StringUtils.isEmpty(timestamp)) {try {sourceAsMap.put("@timestamp", toIndices + timestamp.substring(10));} catch (Exception e) {e.printStackTrace();}}Map<String, Object> httpMap = (Map<String, Object>) sourceAsMap.get("http");if (httpMap != null && httpMap.size() > 0) {Map<String, Object> requestMap = (Map<String, Object>) httpMap.get("request");if (requestMap != null && requestMap.size() > 0) {requestMap.remove("body");requestMap.remove("params");httpMap.put("request", requestMap);}Map<String, Object> responseMap = (Map<String, Object>) httpMap.get("response");if (responseMap != null && responseMap.size() > 0) {responseMap.remove("body");httpMap.put("response", responseMap);}sourceAsMap.put("http", httpMap);}indexRequest = new IndexRequest("ai-cloud-gateway-prod-packet-" + toIndices).source(sourceAsMap);request.add(indexRequest);}// 写BulkResponse bulk = clientNew.bulk(request, getNewOptions());LOGGER.info(bulk.status().getStatus() + ":" + System.currentTimeMillis());SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);// 遍历搜索命中的数据,直到没有数据while (searchHits.length > 0) {try {scrollRequest.scroll(scroll);try {searchResponse = client.scroll(scrollRequest, getNewOptions());} catch (IOException e) {e.printStackTrace();errorTimes++;continue;}scrollId = searchResponse.getScrollId();searchHits = searchResponse.getHits().getHits();if (searchHits.length == 0) {LOGGER.info(" searchHits is null -----  end");break;} else {LOGGER.info(" searchHits is  not null ----- " + searchHits.length);}count = count + searchHits.length;LOGGER.info("-----下一页-----");request = new BulkRequest();LOGGER.info("-----过滤前----- {}",searchHits.length);SearchHit[] finalSearchHits = checkDiscardHits(searchHits);LOGGER.info("-----过滤后----- {}",finalSearchHits.length);IndexRequest[] requests = new IndexRequest[finalSearchHits.length];CountDownLatch countDownLatch = new CountDownLatch(finalSearchHits.length);try {for (int i = 0; i < finalSearchHits.length; i++) {int finalI = i;// 清除 request  response 中的body 减少数据量executorServiceTwo.execute(() -> {try {Map<String, Object> sourceAsMap = finalSearchHits[finalI].getSourceAsMap();String timestamp = (String) sourceAsMap.get("@timestamp");sourceAsMap.put("@timestamp", toIndices + timestamp.substring(10));Map<String, Object> httpMap = (Map<String, Object>) sourceAsMap.get("http");if (httpMap != null && httpMap.size() > 0) {Map<String, Object> requestMap = (Map<String, Object>) httpMap.get("request");if (requestMap != null && requestMap.size() > 0) {requestMap.remove("body");requestMap.remove("params");httpMap.put("request", requestMap);}Map<String, Object> responseMap = (Map<String, Object>) httpMap.get("response");if (responseMap != null && responseMap.size() > 0) {responseMap.remove("body");httpMap.put("response", responseMap);}sourceAsMap.put("http", httpMap);}requests[finalI] = new IndexRequest("ai-cloud-gateway-prod-packet-" + toIndices).source(sourceAsMap);countDownLatch.countDown();} catch (Exception e) {LOGGER.error("", e);}});}} catch (Exception e) {LOGGER.error("", e);Map<String, Object> sourceAsMap;for (SearchHit documentFields : searchHits) {sourceAsMap = documentFields.getSourceAsMap();// 过滤pathString path = (String) sourceAsMap.get("path");if(checkDiscardPath(path)){continue;}String timestamp = (String) sourceAsMap.get("@timestamp");if (!StringUtils.isEmpty(timestamp)) {try {sourceAsMap.put("@timestamp", toIndices + timestamp.substring(10));} catch (Exception ee) {LOGGER.error("", ee);}}Map<String, Object> httpMap = (Map<String, Object>) sourceAsMap.get("http");if (httpMap != null && httpMap.size() > 0) {Map<String, Object> requestMap = (Map<String, Object>) httpMap.get("request");if (requestMap != null && requestMap.size() > 0) {requestMap.remove("body");requestMap.remove("params");httpMap.put("request", requestMap);}Map<String, Object> responseMap = (Map<String, Object>) httpMap.get("response");if (responseMap != null && responseMap.size() > 0) {responseMap.remove("body");httpMap.put("response", responseMap);}sourceAsMap.put("http", httpMap);}indexRequest = new IndexRequest("ai-cloud-gateway-prod-packet-" + toIndices).source(sourceAsMap);request.add(indexRequest);countDownLatch.countDown();}}try {countDownLatch.await(60, TimeUnit.SECONDS);} catch (InterruptedException e) {LOGGER.warn("InterruptedException", e);}LOGGER.info("request {}",requests.length);request.add(requests);// 写LOGGER.info("begin writing to " + toIndices + ",from" + formIndices);bulk = clientNew.bulk(request, getNewOptions());LOGGER.info(bulk.status().getStatus() + ":" + System.currentTimeMillis());if (count >= total - 5000) {break;}Thread.sleep(100);} catch (Exception e) {......}}

  完整代码:GitHub - EricLoveMia/elasticsearch-import

五、性能调优

1、不要返回数据量非常大的结果集

2、避免出现大文档,即单条索引记录的体积不要过大。

3、有条件的话尽量使用大的内存,SSD硬盘,分配给ES的内存为最大内存的50%;

4、尽量批请求bulk;

5、大量写入的情况下,增加索引刷新时间大小index.refresh_interval,默认1s刷新一次,设置为-1表示关闭索引刷新;

6、初始加载数据时禁用副本,将index.number_of_replicas 设置为0;

7、尽量避免使用深度分页,实在不能避免可以采用Scroll 遍历查询或Search After 查询;

8、Data too large 的报错。下图可以看到有两条界限:驱逐线 和 断路器。当缓存数据到达驱逐线时,会自动驱逐掉部分数据,把缓存保持在安全的范围内。当用户准备执行某个查询操作时,断路器就起作用了,缓存数据+当前查询需要缓存的数据量到达断路器限制时,会返回Data too large错误,阻止用户进行这个查询操作。

        此时就要根据情况来修改一些参数的值。

        indices.fielddata.cache.size :配置fieldData的Cache大小,可以配百分比也可以配一个准确的数值。cache到达约定的内存大小时会自动清理,驱逐一部分FieldData数据以便容纳新数据。默认值为unbounded无限。
        indices.fielddata.cache.expire:用于约定多久没有访问到的数据会被驱逐,默认值为-1,即无限。expire配置不推荐使用,按时间驱逐数据会大量消耗性能。而且这个设置在不久之后的版本中将会废弃。

        indices.breaker.fielddata.limit:这个 fielddata 断路器限制fielddata的大小,默认情况下为堆大小的60%。
        indices.breaker.request.limit:这个 request 断路器估算完成查询的其他部分要求的结构的大小, 默认情况下限制它们到堆大小的40%。
        indices.breaker.total.limit:这个 total 断路器封装了 request 和 fielddata 断路器去确保默认情况下这2个部分使用的总内存不超过堆大小的70%。

参考:

elasticsearch高可用 原理 (图解+秒懂+史上最全)-CSDN博客

elasticsearch常识:存储结构、优化_es的存储结构-CSDN博客

Elasticsearch数据备份与恢复-CSDN博客

ES性能调优详解-CSDN博客

ElasticSearch:从[FIELDDATA]Data too large错误看FieldData配置_indices.fielddata.cache.size-CSDN博客ES 性能调优,这可能是全网最详细的 Elasticsearch 性能调优指南_es性能优化-CSDN博客

这篇关于面试集中营—ElasticSearch架构篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/939778

相关文章

mybatis的整体架构

mybatis的整体架构分为三层: 1.基础支持层 该层包括:数据源模块、事务管理模块、缓存模块、Binding模块、反射模块、类型转换模块、日志模块、资源加载模块、解析器模块 2.核心处理层 该层包括:配置解析、参数映射、SQL解析、SQL执行、结果集映射、插件 3.接口层 该层包括:SqlSession 基础支持层 该层保护mybatis的基础模块,它们为核心处理层提供了良好的支撑。

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

百度/小米/滴滴/京东,中台架构比较

小米中台建设实践 01 小米的三大中台建设:业务+数据+技术 业务中台--从业务说起 在中台建设中,需要规范化的服务接口、一致整合化的数据、容器化的技术组件以及弹性的基础设施。并结合业务情况,判定是否真的需要中台。 小米参考了业界优秀的案例包括移动中台、数据中台、业务中台、技术中台等,再结合其业务发展历程及业务现状,整理了中台架构的核心方法论,一是企业如何共享服务,二是如何为业务提供便利。

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

系统架构设计师: 信息安全技术

简简单单 Online zuozuo: 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo :本心、输入输出、结果 简简单单 Online zuozuo : 文章目录 系统架构设计师: 信息安全技术前言信息安全的基本要素:信息安全的范围:安全措施的目标:访问控制技术要素:访问控制包括:等保

利用命令模式构建高效的手游后端架构

在现代手游开发中,后端架构的设计对于支持高并发、快速迭代和复杂游戏逻辑至关重要。命令模式作为一种行为设计模式,可以有效地解耦请求的发起者与接收者,提升系统的可维护性和扩展性。本文将深入探讨如何利用命令模式构建一个强大且灵活的手游后端架构。 1. 命令模式的概念与优势 命令模式通过将请求封装为对象,使得请求的发起者和接收者之间的耦合度降低。这种模式的主要优势包括: 解耦请求发起者与处理者

java面试常见问题之Hibernate总结

1  Hibernate的检索方式 Ø  导航对象图检索(根据已经加载的对象,导航到其他对象。) Ø  OID检索(按照对象的OID来检索对象。) Ø  HQL检索(使用面向对象的HQL查询语言。) Ø  QBC检索(使用QBC(Qurey By Criteria)API来检索对象。 QBC/QBE离线/在线) Ø  本地SQL检索(使用本地数据库的SQL查询语句。) 包括Hibern

创业者该如何设计公司的股权架构

本文来自七八点联合IT橘子和车库咖啡的一系列关于设计公司股权结构的讲座。 主讲人何德文: 在公司发展的不同阶段,创业者都会面临公司股权架构设计问题: 1.合伙人合伙创业第一天,就会面临股权架构设计问题(合伙人股权设计); 2.公司早期要引入天使资金,会面临股权架构设计问题(天使融资); 3.公司有三五十号人,要激励中层管理与重要技术人员和公司长期走下去,会面临股权架构设计问题(员工股权激

贝壳面试:什么是回表?什么是索引下推?

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题: 1.谈谈你对MySQL 索引下推 的认识? 2.在MySQL中,索引下推 是如何实现的?请简述其工作原理。 3、说说什么是 回表,什么是 索引下推 ? 最近有小伙伴在面试 贝壳、soul,又遇到了相关的