Elasticsearch

一.什么是 Elasticsearch

ElasticSearch 是一个基于 Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch 是用 Java开发的,并作为 Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。

二.Elasticsearch应用场景

(1).大型分布式日志分析系统ELK  elasticsearch(存储日志)+logstash(收集日志)+kibana(展示数据)

(2).大型电商商品搜索系统、网站站内搜索、网盘搜索引擎等

三.Elasticsearch基本概念

(1)Near Realtime(NRT):近实时,两个意思,从写入数据到数据可以被搜索到有一个小延迟(大概1秒);基于es执行搜索和分析可以达到秒级

(2)Cluster:集群,包含多个节点,每个节点属于哪个集群是通过一个配置(集群名称,默认是elasticsearch)来决定的,对于中小型应用来说,刚开始一个集群就一个节点很正常

(3)Node:节点(简单理解为集群中的一个服务器),集群中的一个节点,节点也有一个名称(默认是随机分配的),节点名称很重要(在执行运维管理操作的时候),默认节点会去加入一个名称为“elasticsearch”的集群,如果直接启动一堆节点,那么它们会自动组成一个elasticsearch集群,当然一个节点也可以组成一个elasticsearch集群

(4)Index:索引(简单理解就是一个数据库),包含一堆有相似结构的文档数据,比如可以有一个客户索引,商品分类索引,订单索引,索引有一个名称。一个index包含很多document,一个index就代表了一类类似的或者相同的document。比如说建立一个product index,商品索引,里面可能就存放了所有的商品数据,所有的商品document。

(5)Type:类型(简单理解就是一张表),每个索引里都可以有一个或多个type,type是index中的一个逻辑数据分类,一个type下的document,都有相同的field,比如博客系统,有一个索引,可以定义用户数据type,博客数据type,评论数据type。

(6)Document&field:文档(就是一行数据),es中的最小数据单元,一个document可以是一条客户数据,一条商品分类数据,一条订单数据,通常用JSON数据结构表示,每个index下的type中,都可以去存储多个document。一个document里面有多个field,每个field就是一个数据字段。

(7)shard:单台机器无法存储大量数据,es可以将一个索引中的数据切分为多个shard,分布在多台服务器上存储。有了shard就可以横向扩展,存储更多数据,让搜索和分析等操作分布到多台服务器上去执行,提升吞吐量和性能。每个shard都是一个lucene index。

(8)replica:任何一个服务器随时可能故障或宕机,此时shard可能就会丢失,因此可以为每个shard创建多个replica副本。replica可以在shard故障时提供备用服务,保证数据不丢失,多个replica还可以提升搜索操作的吞吐量和性能。primary shard(建立索引时一次设置,不能修改,默认5个),replica shard(随时修改数量,默认1个),默认每个索引10个shard,5个primary shard,5个replica shard,最小的高可用配置,是2台服务器。

(9)index -> type -> mapping -> document -> field

        index:mysql数据库
  type:就像一张表。ES5.X中一个index可以有多个type、
             ES6.X中一个type只能有一个type、
             ES7.X中移除了type这个概念,此时的index就像一张表了。
  mapping:定义了每个字段的类型等信息。相当于关系型数据库中的表结构。
  document:一条document就代表了mysql表里的一条记录。
  field:每个field就代表了这个document中的一个字段的值。

四.倒排索引

查询有个算法叫倒排序:简单的说就是:通过分词把词语出现的id进行记录下来,再查询的时候先去查到哪些id包含这个数据,然后再根据id把数据查出来。

五.路由算法
  首先这肯定不会是随机的,否则将来要获取文档的时候我们就不知道从何处寻找了。实际上,这个过程是根据下面这个公式决定的:
  shard = hash(routing) % number_of_primary_shards
  routing默认是文档的id,也可以自定义,hash(routing)后得到一个数字,再对主分片个数取余,这个数字肯定是0~number_of_primary_shards-1 之间的余数,就是我们文档所在的位置。所以我们在创建索引的时候就要确定好主分片个数,并永远不修改,否则就找不到之前的数据了。

六.Elasticsearch分布式架构原理

(1)一个index包含多个shard,也就是一个index存在多个服务器上

(2)每个shard都是一个最小工作单元,承载部分数据,比如有三台服务器,现在有三条数据,这三条数据在三台服务器上各方一条.

(3)增减节点时,shard会自动在nodes中负载均衡

(4)primary shard和replica shard,每个document肯定只存在于某一个primary shard以及其对应的replica shard中,不可能存在于多个primary shard

(5)replica shard是primary shard的副本,负责容错,以及承担读请求负载

(6)primary shard的数量在创建索引的时候就固定了,replica shard的数量可以随时修改

(7)primary shard的默认数量是5,replica默认是1,默认有10个shard,5个primary shard,5个replica shard

(8)primary shard不能和自己的replica shard放在同一个节点上(否则节点宕机,primary shard和副本都丢失,起不到容错的作用),但是可以和其他primary shard的replica shard放在同一个节点上

Elasticsearch_第1张图片

七.Elasticsearch写入

(1)简单流程

        1.客户端选择一个node发送请求过去,这个node就是coordinating node (协调节点)
        2.coordinating node,对document进行路由,将请求转发给对应的node
        3.实际上的node上的primary shard处理请求,然后将数据同步到replica node
        4.coordinating node,如果发现primary node和所有的replica node都搞定之后,就会返回请求到客户端
               这个路由简单的说就是取模算法,比如说现在有3台服务器,这个时候传过来的id是5,那么5%3=2,就放在第2台服务器

(2)文档写入流程

新文档创建或者更新时,进行如下流程:

更新不会修改原来的 segment,更新和创建操作都会生成新的一个 segment。数据哪里来呢?先会存在内存的 bugger 中,然后持久化到 segment 。

数据持久化步骤如下:write -> refresh -> flush -> merge

1 write 过程

Elasticsearch_第2张图片

一个新文档过来,会存储在 in-memory buffer 内存缓存区中,顺便会记录 Translog。

这时候数据还没到 segment ,是搜不到这个新文档的。数据只有被 refresh 后,才可以被搜索到。那么 讲下 refresh 过程

2 refresh 过程

Elasticsearch_第3张图片

refresh 默认 1 秒钟,执行一次上图流程。ES 是支持修改这个值的,通过 index.refresh_interval 设置 refresh (冲刷)间隔时间。refresh 流程大致如下:

  • in-memory buffer 中的文档写入到新的 segment 中,但 segment 是存储在文件系统的缓存中。此时文档可以被搜索到

  • 最后清空 in-memory buffer。注意: Translog 没有被清空,为了将 segment 数据写到磁盘

文档经过 refresh 后, segment 暂时写到文件系统缓存,这样避免了性能 IO 操作,又可以使文档搜索到。refresh 默认 1 秒执行一次,性能损耗太大。一般建议稍微延长这个 refresh 时间间隔,比如 5 s。因此,ES 其实就是准实时,达不到真正的实时。

3 flush 过程

Elasticsearch_第4张图片

上个过程中 segment 在文件系统缓存中,会有意外故障文档丢失。那么,为了保证文档不会丢失,需要将文档写入磁盘。那么文档从文件缓存写入磁盘的过程就是 flush。写入磁盘后,清空 translog。

translog 作用很大:

  • 保证文件缓存中的文档不丢失

  • 系统重启时,从 translog 中恢复

  • 新的 segment 收录到 commit point 中

具体可以看官方文档:https://www.elastic.co/guide/...

4 merge 过程

Elasticsearch_第5张图片

上面几个步骤,可见 segment 会越来越多,那么搜索会越来越慢?怎么处理呢?

通过 merge 过程解决:

  • 就是各个小段文件,合并成一个大段文件。段合并过程

  • 段合并结束,旧的小段文件会被删除

  • .liv 文件维护的删除文档,会通过这个过程进行清除

5 写入总结

Elasticsearch_第6张图片

如这个图,ES 写入原理不难,记住关键点即可。

write -> refresh -> flush

  • write:文档数据到内存缓存,并存到 translog

  • refresh:内存缓存中的文档数据,到文件缓存中的 segment 。此时可以被搜到

  • flush 是缓存中的 segment 文档数据写入到磁盘

八.Elasticsearch查询

(1)查询过程
    1.客户端发送一个请求给coordinate node(协调节点)
    2.协调节点将搜索的请求转发给所有的shard对应的primary shard 或replica shard
    3.query phase:每一个shard 将自己搜索的结果(其实也就是一些唯一标识),返回给协调节点,有协调节点进行数据的合           并,排序,分页等操作,产出最后的结果
    4.fetch phase ,接着由协调节点,根据唯一标识去各个节点进行拉去数据,最总返回给客户端
(2)查询原理
         查询过程大体上分为查询和取回这两个阶段,广播查询请求到所有相关分片,并将它们的响应整合成全局排序后的结果集              合,这个结果集合会返回给客户端。

       查询阶段

           1.当一个节点接收到一个搜索请求,这这个节点就会变成协调节点,第一步就是将广播请求到搜索的每一个节点的分片拷                  贝,查询请求可以被某一个主分片或某一个副分片处理,协调节点将在之后的请求中轮训所有的分片拷贝来分摊负载。
           2.每一个分片将会在本地构建一个优先级队列,如果客户端要求返回结果排序中从from 名开始的数量为size的结果集,每                一个节点都会产生一个from+size大小的结果集,因此优先级队列的大小也就是from+size,分片仅仅是返回一个轻量级                 的结果给协调节点,包括结果级中的每一个文档的ID和进行排序所需要的信息。
           3.协调节点将会将所有的结果进行汇总,并进行全局排序,最总得到排序结果。

      取值阶段

          1.查询过程得到的排序结果,标记处哪些文档是符合要求的,此时仍然需要获取这些文档返回给客户端
          2.协调节点会确定实际需要的返回的文档,并向含有该文档的分片发送get请求,分片获取的文档返回给协调节点,协调节                点将结果返回给客户端

九.Elasticsearch并发解决方案

为什么会出现并发问题,简单的说就是多个线程去操作同一个数据.

假如现在下单系统下2单,都要去减库存(100件),第一个线程进去减1件100-1=99,这时候还没更像到ES中,第二线程进去了,也要减一个库存100-1=99.现在系统卖出去2个,可是库存却还有99个,应该是98个

解决方案-悲观锁
解决方案-乐观锁
温馨提示,乐观锁会出现ABA情况

下面是2种解决方案,在网上找的图片:

Elasticsearch_第7张图片
 

 

 

ElasticSearch更新后延迟解决方法

在index后有一个refresh_interval默认1秒,在这个时间间隔内search是不可见的。
解决办法有两种:
1,通过ui层解决。比如更新成功后,弹出提示框;
2,设置刷新策略,有以下三种;

public static enum RefreshPolicy implements Writeable {
    NONE("false"), // 默认;异步刷新,立即返回;
    IMMEDIATE("true"), // 立即刷新,对es集群压力会比较大
    WAIT_UNTIL("wait_for"); // 等刷新时返回,响应性不太好,

Elasticsearch操作数据后马上更新的办法,在写入、更新、删除后立即执行刷新数据,通过设置.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);即可。

示例如下:

update后立即更新数据

UpdateRequest request = new UpdateRequest(ES_INDEX, ES_TYPE, keyId).doc(body, XContentType.JSON);
        //写入完成立即刷新
        request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
        UpdateResponse updateResponse = Proxy.getRestHighLevelClient(host).update(request);

你可能感兴趣的:(Elasticsearch)