快速检索与存储系统——Elasticsearch

一、什么是Elasticsearch?

在这里插入图片描述

  ELASTICSEARCH(弹性搜索):是一款开源的分布式、RESTful风格的搜索和数据分析引擎,它底层基于Apache Lucene开源库进行封装,其不仅仅提供分布式多用户能力的全文搜索引擎,还可以被准确形容为:

1、一个分布式的实时文档存储,每个字段可以被索引与搜索;

2、一个分布式实时分析搜索引擎;

3、能胜任上百个节点的扩展,并支持PB级别额结构化和非结构化数据。

二、Elasticsearch基本概念

全文搜索(Full-text Search)

  全文检索是指计算机索引程序通过扫描文档中的每一个词,并对每一个词建立一个索引,指明该词在文档中出现的次数和相对位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。

在全文搜索的世界中,目前主流的工具包括:

  Apache Lucene

  Elasticsearch

  Solr

  Ferret

节点&集群

  Elasticsearch 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个Elasticsearch实例。单个Elasticsearch实例称为一个节点(Node),一组节点构成一个集群(Cluster)。

索引&文档&类型&字段

什么是index

  (1)存储在一系列分片中,底层即Lucene index。所以使用新索引应该注意:Lucene索引在磁盘空间内存使用和使用的文件描述符方面有一个小而固定的开销。因此,单个大index效率高于多个小的index,使Lucene index的固定开销均摊在多个文档中。

  (2)其次,数据搜索在每个分片都是独立的,但ES最终需要合并所有搜索分片的结果。例如,搜索10个index,每个index有5个分片,则协调搜索请求执行的节点将需要合并5x10 = 50个分片结果。如果有太多的分片结果要合并,或者运行了一个产生大量分片响应的大量请求,合并这些分片结果的任务会非常消耗CPU和内存资源。因此,尽量复用已有index
什么是type

  在一个index里允许使用type存储多种类型的数据,以此减少index的数量了。在使用时,向每个文档加入_type 字段,在指定type搜索时就会被用于过滤。type的一个好处是:搜索一个index下的多个type,和只搜索一个type相比没有额外的开销 —— 需要合并结果的分片数量是一样的。但是type使用需要注意:

(1)不同type里的相同字段的类型和配置必须一致;

(2)只在某个type里存在的字段,其它没有该字段的 type 也会消耗资源;

(3)得分是由index内的统计数据来决定的,一个 type 中的文档会影响另一个 type 中的文档的得分。

  类比于MySQL如表所示:

Elasticsearch(ES)IndexDocumentTypeField
MySQLDatabaseRowTableColumn

分片

  ES是面向文档的,意味着它存储整个对象或文档,同时索引每个文档内容,使之可以被检索。索引实际上是指向一个或多个物理分片的逻辑命名空间,分片是一个底层的工作单元,他保存了全部数据中的一部分,一个分片是一个Lucene的实例,本身就是一个完整的搜索引擎。ES利用分片来实现分布式。分片是数据的容器,文档保存在分片内,分片又被分配到集群内的各个节点里。 当集群规模扩大或者缩小时, ES会自动在各节点中迁移分片,使得数据仍然均匀分布在集群里。一个分片可以是主分片或者副本分片。 索引内任意一个文档都归属于一个主分片,所以主分片的数目决定着索引能够保存的最大数据量。一个副本分片只是一个主分片的拷贝。 副本分片为保护数据不丢失的冗余备份,并为搜索和返回文档等读操作提供服务。我们如何得知一个文档应该被分片到什么位置呢?则利用如下公式:

  shard = hash (routing) % number_of_primary_shards

   routing是一个可变值,默认为文档的_id,也可以自定义值。routing通过hash函数生成一个数字,然后这个数字在除以number_of_primary_shards(主分片的数量)得到的余数,这个分布到0到number_of_primary_shards-1之间的余数,就是所求文档的分片位置。

三、倒排索引

   Elasticsearch索引表中每一项包含一个属性值和该属性值的各记录地址(非记录确定属性值),即倒排索引原理。1)用户将数据提交到Elasticsearch数据库中,通过分词控制器将对应的语句分词,将权重和分词结果一并存入数据库;2)搜索时,根据权重将结果排名,打分,再将返回结果呈现给用户。索引建立过程需要向主节点发出建立请求,主节点根据负载情况以及分片规则将请求分发给对应主slave,主slave执行完成后,信息同步至所有备份slave。数据写入根据Routing规则可以在任意slave执行。

    建立索引(请求)——>master——>主slave(信息同步)——>备份slave

    数据写入(Routing规则)——>任意slave

ES快速索引策略

如需要存储如下几条数据:

IDNameAgeSex
1Kate24Female
2John24Male
3Bill25Male

则ES根据倒排索引原理为Name,Sex两个Field建立如下索引:

1TermPosting List
2Kate1
3John2
4Bill3
1TermPosting List
2Female1
3Male2,3

  其中,Kate,John,Female,Male叫Term,[1,2,3]为Posting List,存储符合某个Term的文档Id,通过Posting List很快定位到查询文档。(1)但当数据量很大时,为快速找到某个Term,将所有的Term排序,利用二分查找,即通过字典查找,生成Term Dictionary。(2)为进一步加快查找速度,通过内存查找,不读磁盘,减少磁盘寻道次数。但Term太多,无法全部读入内存,建立如字典的索引页,如A开头有哪些Term,通过Term Index快速定位Term Dictionary的位置。(3)最后利用FST,利用字节的方式存储所有的Term Index。

  同样对Posting List也运用相关压缩技术,例如(1)增量编码压缩技术,如:
在这里插入图片描述

(2)位图压缩(Roaring bitmap),其中bitmap是一种数据结构,例如Posting List为[1,3,4,7,10],则利用Roaring bitmap技术压缩后为[1,0,1,1,0,0,1,0,0,1],对应位置0/1表示某值是否存在,压缩前20字节,压缩后10位,2字节。

四、读写原理

写操作(Write):针对文档的CRUD操作

索引新文档(Create)
  当用户向一个节点提交了一个索引新文档的请求,节点会计算新文档应该加入到哪个分片(shard)中。每个节点都存储有每个分片存储在哪个节点的信息,因此协调节点会将请求发送给对应的节点。注意这个请求会发送给主分片,等主分片完成索引,会并行将请求发送到其所有副本分片,保证每个分片都持有最新数据。

  每次写入新文档时,都会先写入内存中,并将这一操作写入一个translog文件(transaction log)中,此时如果执行搜索操作,这个新文档还不能被索引到。

在这里插入图片描述

                图1、新文档被写入内存,操作被写入translog

  ES会每隔1秒时间(这个时间可以修改)进行一次刷新操作(refresh),此时在这1秒时间内写入内存的新文档都会被写入一个文件系统缓存(filesystem cache)中,并构成一个分段(segment)。此时这个segment里的文档可以被搜索到,但是尚未写入硬盘,即如果此时发生断电,则这些文档可能会丢失。

在这里插入图片描述

                图2、在执行刷新后清空内存,新文档写入文件系统缓存

  不断有新的文档写入,则这一过程将不断重复执行。每隔一秒将生成一个新的segment,而translog文件将越来越大。

在这里插入图片描述

                  图3、translog不断加入新文档记录

  每隔30分钟或者translog文件变得很大,则执行一次fsync操作。此时所有在文件系统缓存中的segment将被写入磁盘,而translog将被删除(此后会生成新的translog)。

              图4、执行fsync后segment写入磁盘,清空内存和translog

  由上面的流程可以看出,在两次fsync操作之间,存储在内存和文件系统缓存中的文档是不安全的,一旦出现断电这些文档就会丢失。所以ES引入了translog来记录两次fsync之间所有的操作,这样机器从故障中恢复或者重新启动,ES便可以根据translog进行还原。

  当然,translog本身也是文件,存在于内存当中,如果发生断电一样会丢失。因此,ES会在每隔5秒时间或是一次写入请求完成后将translog写入磁盘。可以认为一个对文档的操作一旦写入磁盘便是安全的可以复原的,因此只有在当前操作记录被写入磁盘,ES才会将操作成功的结果返回发送此操作请求的客户端。

  此外,由于每一秒就会生成一个新的segment,很快将会有大量的segment。对于一个分片进行查询请求,将会轮流查询分片中的所有segment,这将降低搜索效率。因此ES会自动启动合并segment的工作,将一部分相似大小的segment合并成一个新的大segment。合并的过程实际上是创建了一个新的segment,当新segment被写入磁盘,所有被合并的旧segment被清除。

在这里插入图片描述

                      图5、合并segment

在这里插入图片描述

              图6、合并完成后删除旧segment,新segment可供搜索

更新(Update)和删除(Delete)文档

  ES的索引是不能修改的,因此更新和删除操作并不是直接在原索引上直接执行。

  每一个磁盘上的segment都会维护一个del文件,用来记录被删除的文件。每当用户提出一个删除请求,文档并没有被真正删除,索引也没有发生改变,而是在del文件中标记该文档已被删除。因此被删除的文档依然可以被检索到,只是在返回检索结果时被过滤掉了。每次在启动segment合并工作时,那些被标记为删除的文档才会被真正删除。

  更新文档会首先查找原文档,得到该文档的版本号。然后将修改后的文档写入内存,此过程与写入一个新文档相同。同时,旧版本文档被标记为删除,同理,该文档可以被搜索到,只是最终被过滤掉。

读操作(Read):查询过程
  查询的过程大体上分为查询(query)和取回(fetch)两个阶段。这个节点的任务是广播查询请求到所有相关分片,并将它们的响应整合成全局排序后的结果集合,这个结果集合会返回给客户端。

查询阶段

  当一个节点接收到一个搜索请求,则这个节点就变成了协调节点。

在这里插入图片描述

                    图7、查询过程分布式搜索

  第一步是广播请求到索引中每一个节点的分片拷贝。 查询请求可以被某个主分片或某个副本分片处理,协调节点将在之后的请求中轮询所有的分片拷贝来分摊负载。

  每个分片将会在本地构建一个优先级队列。如果客户端要求返回结果排序中从第from名开始的数量为size的结果集,则每个节点都需要生成一个from+size大小的结果集,因此优先级队列的大小也是from+size。分片仅会返回一个轻量级的结果给协调节点,包含结果集中的每一个文档的ID和进行排序所需要的信息。

  协调节点会将所有分片的结果汇总,并进行全局排序,得到最终的查询排序结果。此时查询阶段结束。

取回阶段

  查询过程得到的是一个排序结果,标记出哪些文档是符合搜索要求的,此时仍然需要获取这些文档返回客户端。

  协调节点会确定实际需要返回的文档,并向含有该文档的分片发送get请求;分片获取文档返回给协调节点;协调节点将结果返回给客户端。

在这里插入图片描述

                      图8、分布式搜索的取回阶段

四、Elasticsearch API

  ES的API分为REST Client API(Representational state transfer,表述性状态转移,即http请求形式)以及Java API(即transportClient)两种。相比transportClient API效率更高,通过ES内部的RPC调用形式进行请求,连接可以是一个长连接,相当于是把客户端的请求当成ES 集群的一个节点。ES 7中移除transportClient,主要原因是难以向下兼容,改为High-Level Rest ClientLow-Level Rest Client。

REST Client API命令的格式如下:

curl –X<VERB> ‘<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>’ –d  ‘<BODY>’

部件说明:

VERB适当的HTTP方法(GET、PUT、POST、DELETE、HEAD
PROTOCOLhttp或https
HOSTES集群中任意节点的主机名
PORT运行服务端口,默认9200
PATHAPI的终端路径
QUERY_STRING任意可选的查询字符串参数
BODY一个JSON格式的请求体

对于HTTP方法,具体作用:

HTTP方法说明
GET获取请求对象的当前状态
POST改变对象的当前状态
PUT创建一个对象
DELETE销毁对象
HEAD获取请求对象的当前状态
eg:curl –X PUT ‘localhost:9200/weather’ //新建一个名叫weather的Index

JAVA API操作:

  ES 7.0版本弃用TransportClient对Elasticsearch进行操作,并在8.0版本过后彻底删除,在新版本中通过创建Java Low Level REST ClientJava High Level REST Client,其中High Level基于Low Level进行了更上层的封装,Low Level本身不负责数据的编解码,需用户编解码,而High Level直接调用相应API,两者都基于http协议。

ES常用的API分类如下:

1)文档Document API:提供对文档的增删改查操作;

2)搜索Search API:提供对文档进行某个字段的查询;

3)索引Index API:提供对索引进行操作,查看索引信息等;

4)查看Watcher API:提供更加直观的形式返回数据,适合控制台请求展示;

5)集群Cluster API:对集群进行查看和操作的API。

接下来将对ES 5.x和ES 7.x版本下利用Java API对ES操作讲解:

(1)Client初始化:

ES 5.X:

//on startup
TransportClient client = new PreBuiltTransportClient(Settings.EMPTY)
        .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("host1"), 9300));
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("host2"), 9300));

//on shutdown
client.close();

ES 7.X:

//on startup
RestHighLevelClient client = new RestHighLevelClient(
        RestClient.builder(
                new HttpHost("host1",9200,"http"),
                new HttpHost("host2",9200,"https")
        )
);

//on shutdown
client.close();

  所有操作都提供同步和异步两种操作,同步方法客户端等待结果返回,异步方法后添加Async后缀,该过程客户端直接返回,通过侦听器传递异步结果。其中,侦听器listen:

ActionListener listener = new ActionListener<IndexResponse>() {

    @Override
    public void onResponse(IndexResponse indexResponse) {
        //执行成功
    }

    @Override
    public void onFailure(Exception e) {
        //执行失败
    }
};

(2)Index API
ES 5.X:

IndexResponse response = client.prepareIndex("twitter", "tweet", "1")
        .setSource(builder).get(); // builder可以替换为json
      //.setSource(json,XContentType.JSON).get();      
      //.setSource(jsonMap).get();

ES 7.X:

IndexRequest indexRequest = new IndexRequest().index("posts")
	.id("1")
	.source( "user","kimchy",
       	 "postDate",new Date(),
        "message","trying out Elasticsearch");
//.source(builder)
//.source(jsonMap)

//异步请求
IndexResponse indexResponse = client.indexAsync(indexRequest, RequestOptions.DEFAULT,listener);

//同步请求
IndexResponse indexResponse1 = client.index(indexRequest,RequestOptions.DEFAULT);

五、典型应用场景

  通常日志由服务器生成,输出到不同的文件中,一般会有系统日志、 应用日志、安全日志。这些日志分散地存储在不同的机器上。在没有日志系统的情况下,若系统发生故障,首先需要定位处理请求的服务器,如果这台服务器部署了多个实例,则需要去每个应用实例的日志目录下查找日志文件。每个应用实例还会设置日志滚动策略(如:每天生成一个文件),还有日志压缩归档策略等。整个流程相对比较繁琐,因此,如果我们能把这些日志集中管理,并提供集中检索功能,不仅可以提高诊断的效率,同时对系统情况有个全面的理解。

  针对这些问题,为了提供分布式的实时日志搜集和分析的监控系统,提出ELK,实践之后进而优化为EFK,即ELK(Elasticsearch+Logstash+Kibana)优化—> EFK(Elasticsearch+Beat+Kibana)

  Elasticsearch——>存储数据;Logstash/ Beat——>数据处理引擎/轻量级数据收集引擎(针对 不同的数据有Filebeat,Packetbeat,Functiongbeat etc);Kibana——>可视化平台。

  为解决大并发情况的数据丢失,利用Redis/Kafka,其中Redis保证高实时性,低可靠性(队列容 量取决于内存);Kafka保证高可靠性,有延迟(队列容量取决于磁盘)。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
 ElasticSearch是一个基于ApacheLucene构建的开源搜索服务器。它提供了一个可扩展的搜索解决方案,内置支持近实时搜索和多租户。   《ElasticSearch 可扩展的开源弹性搜索解决方案》将教你如何构建一个快速、灵活、可扩展的搜索解决方案,通过建立自定义集群进入ElasticSearch的世界。通过学习数据索引和分析,你将掌握ElasticSearch的强大功能,包括如何搜索数据、如何扩大搜索范围,以及如何进行集群管理。   从建立自己的ElasticSearch集群开始,然后进行搜索并扩展搜索参数,你就可以创建一个完全自定义的搜索解决方案。在此基础上,你将进一步地学习ElasticSearch的查询API,以及如何使用强大的过滤和统计功能。在ElasticSearch旅程最后总结的一些章节,帮助读者控制和调整集群,学习分片分配、网关模式配置等知识。   《ElasticSearch 可扩展的开源弹性搜索解决方案》是为希望利用ElasticSearch创建一个快速、灵活的搜索解决方案的开发者而撰写的。如果你正试图学习ElasticSearch或者希望变得更加精通,本书也同样适合。开始学习本书时你不需要知道关于ElasticSearch的任何知识,但知道一些数据库和查询的基本知识是必需的。   你将从本书学到   配置和创建一个ElasticSearch索引   使用ElasticSearch查询DSL进行各种查询   在不损失性能的情况下高效和精确地使用过滤器   实现自动补全功能   高亮数据和地理信息搜索以得到更好的结果   理解ElasticSearch如何返回结果及如何验证这些结果   使用统计和相似功能从搜索中获得更多,并提升客户的搜索体验   使用ElasticSearchAPI和第三方监控方案监控集群状态与集群健康状况

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值