ElasticSearch概念与架构原理

时间:2023-01-31 17:22:42

一、概述

  • ElasticSearch简介
    • 简介
      • ES是建立在Lucene基础之上的分布式准实时搜索引擎,它所提供的诸多功能中有一大优点,就是实时性好。比如:在业务需求中,新增数据需要1min才能被搜索到,而在ES中数秒或1s内就能搜索到新增的数据。
      • ES不仅是一个搜索引擎框架,而且官方还提供了一个全家桶,为构建搜索引擎提供了很好的解决方案,就是ELK:
        • E:ElasticSearch:提供数据搜索和分析功能
        • L:Logstash:借助它可以将数据库和日志等结构化或非结构化的数据轻松的导入到ES中
        • K:Kibana:可已经分析结果进行图形化展示出来
    • 特性
      • 实时性好
      • 可以分布式部署ES
      • 可以通过Http请求使用REST风格API接口的方式对ES执行请求完成搜索任务
      • ES 还提供了聚合的功能,可以对数据进行统计分析
      • 在数据安全方面ES还提供了X-Pack进行用户验证
  • ElasticSearch基本概念
    • 索引

      • 在传统的关系型数据库中,我们对数据进行操作[CURD],需要建一个库,而ES中的需要建索引,对数据的操作的对象全部对应索引。ES中的一个索引对应一个或者多个Lucene索引,这是由分布式设计方案决定的。
    • 文档

      • 在传统的关系型数据库中,需要将数据封装成数据库中的一条记录,而在ES中是对应是文档。ES的文档中可以有多个字段,每个字段建议是各种数据类型。为了减轻集群的堵在和提升效率,ES提供了文档的批量索引、更新和删除功能。
    • 字段

      • 一个文档中可以包含多个字段,每个字段有一个数据类型与其对应。除了常用的数据类型(字符串、文本和数值)外,ES还提供了多种数据类型,如:数组类型、经纬度、IP地址类型等。ES对不同类型的字段可以支持不同的搜索功能。例如:使用文本类型的数据时,可以按照分词的方式对数据进行搜索,并且可以对设定搜索后的打分因子来影响最终的排序;再如:使用经纬度数据时,ES可以搜索某个地点附近的文档,也可以查询地理围栏内的文档。在排序函数上ES也可以基于某个地点按照衰减函数进行排序。

        索引、文档和字段的关系,如图:

        ElasticSearch概念与架构原理

    • 映射

      • 建立索引使需要定义文档的数据结构,这种结构叫做映射。在映射中,文档中的字段的数据类型设定后不能在进行更改。因为字段类型在定义后,ES已经定义的类型建立了特定的索引结构,这种结构不能更改。借助映射可以给文档新增字段。另外,ES还提供了自动映射的功能,在添加数据时,如果该字段没有定义类型,ES会根据用户提供的该字段的真实数据来猜测可能的类型,从而自动进行字段类型的定义。
    • 集群与节点

      • 在分布式系统中,为了完成海量数据的存储、计算并提升系统的高可用性,需要多台计算机集成协作,这种形式称之为集群。这些集群中的每个计算机我们又称之为节点。ES集群的节点个数是没有限制的,用户可以根据业务量、数据量增加节点,如图:

        ElasticSearch概念与架构原理

    • 分片

      • 在分布式系统中,为了能存储和计算海量数据,会对数据进行分片,将数据存储在不同的计算机中。在ES中一个分片对应一个Lucene索引,每个分片下面还可以设置多个副分片。索引的分片个数只能设置一次,之后不能在更改,默认情况下每个索引设置5个分片。

      • 优点

        • 分担集群的存储和计算压力
        • 提高系统中数据的高可用性
        • 每一个分片可以设置多个副分片当主分片所在的计算机因某些原因离线时,副分片可以充当主分片继续服务
    • 副分片

      • 为了提升系统索引数据的高可用性和减轻集群搜索的负载,可以启用分片的副本,副本我们称之为副分片。在一个索引中,主分片的副分片的个数是没有限制的,用户可以按需设定。在默认情况下,ES不会为主分片开启副分片,需要手动进行设置。一个分片的主分片和副分片可以存储在不同的计算机上,如图:

        ElasticSearch概念与架构原理

      • 注意事项

        • 在极端情况下,当只有一个节点时,如果索引的副分片个数设置大于1,则系统只分配主分片,而不会设置副分片。
    • DSL

      • ES使用DSL[Domain Specific Language]来定义查询,常见的 html/css/sql都属于DSL。ES中的DSL采用json的方式来表达,客户端接受的数据也是封装好的json形式;优点:可以简单明了的表达请求/响应内容,而且还屏蔽各种编程语言之间数据通信的差异。
  • ElasticSearch与其他数据库对比
    类型 ElasticSearch 关系型数据库
    索引方式 正排索引和倒排索引 B-Tree结构
    事务支持 不支持事务[更新文档时,先读取在更新,如果并发修改数据,使用乐观锁] 支持事务
    SQL与DSL 查询、大小、相等比较、逻辑、或、与、关系运算,文本搜索、地理位置搜索和复杂数据的搜索 查询、大小、相等比较、逻辑、或、与、关系运算
    扩展方式 本身支持分片和副分片 需要借助第三方组件进行分库分表,分库分表会对一些业务造成延迟,查询结果的合并和多表join的操作
    数据的查询速度 ES是基于Lucene库的搜索引擎,可以支持全字段建立索引,单个索引存储上百个字段或几十亿条数据记录都是没有问题的,查询速度也不会变慢 字段数量与数据量过大查询速度很慢
    数据的实时性 为了提高写入数据的性能,ES在内存和磁盘之间增加一层系统缓存,ES响应写入数据的请求后,会将数据存储到内存中,此时该数据还不能搜索到,内存中的数据每隔一段时间会被刷新到系统缓存内,此时的数据才能被搜索到。因此,ES的写入数据不是实时的,而是准实时的。 存储和查询数据基本上是实时的,即单条数据的写入可以立即查询

二、ElasticSearch架构原理

  • 节点职责

    • 节点职责类型

      • master节点

        • master节点负责维护整个集群的相关工作,管理集群的变更,如创建/删除索引、节点健康监测、节点上/下线等。maste节点是由集群节点通过选举算法选举出来的,一个集群中只有一个节点为master节点,但是可以有一个或多个节点参与master节点的选举,在默认情况下,任意节点都可以作为master候选节点,可以通过配置项node.master对当前节点是否作为master候选节点进行控制。
      • 数据节点

        • 数据节点主要负责索引数据的保存工作,此外也执行数据的其他操作,如文档删除、修改和查询操作。数据节点的很多工作是调用Lucene库进行Lucene索引操作,因此这种节点对内存和I/O的消耗比较大,生产环境中应多注意数据节点的计算机负载情况。
      • 协调节点

        • 客户端可以向ES集群的节点发起请求,这个节点叫做协调节点。在默认情况下,协调节点可以是集群中的任意节点,此时他的生命周期是和一个单独的请求相关的。也就是说当客户端向集群中的某个节点发起请求时,此时该节点被称为当前请求的协调节点,当它将响应结果返回客户端后,该协调节点生命周期就结束了。如图所示,分别表示访问不同的节点,因为请求时客户端指定的请求地址不同,所以左图中的请求协调节点时node1,右图中的请求协调节点时node3。协调节点会根据具体的情况将请求转发给其他节点,并将最终汇总的处理结果返回客户端;

          ElasticSearch概念与架构原理ElasticSearch概念与架构原理

      为了降低集群的负载,可以将某个节点作为单独的协调节点。在节点的配置文件中设置node.maste和node.data配置项为false,此时这个节点不会选中master节点并且不在担任数据节点,而客户端可以将这类节点作为协调节点来使用,把所有的请求分发到这些节点上,如图:

      ElasticSearch概念与架构原理

    • 每个节点可以单独配置,默认情况下集群不会对节点角色进行划分,所有的节点都是相互平等的,可以担任所有的职责。但是在生产环境中需要对这些节点的角色进行最优的划分,否则在高并发的情况下集群容易出现服务阻塞超时甚至服务崩溃的隐患。

  • 主分片和副分片

    • ES为了支持分布式搜索,会把数据按照分片进行切分。一个索引由一个或者多个分片构成,并且每个分片有0个甚至多个副分片。多个分片可以分布在不同的节点中,通过这种方式提高分片数据的高可用性和服务的高并发支持。

    • 集群中的索引主分片和副分片在不同的计算机上,如果某个主分片所在的节点宕机,则原有的某个副分片会提升为主分片进行对外服务,如图:

      ElasticSearch概念与架构原理

      如果node1发生了宕机,集群感知到分片0的主分片A0将要丢失,此时集群会立即对其他节点[node3]上的分片0对应的副分片C0作为主分片A0进行服务,集群中的node2和node3对外提供服务,所有的分片服务不受影响;如图:

    ElasticSearch概念与架构原理

    如果node1恢复了服务并加入了集群中,因为在node1上还保留有分片0的数据,此时node1上的分片A0会变成副分片C0。在此期间丢失的数据会通过node3上的主分片A0进行补充。并且node1上的分片也会通过node2和node3对应的分片进行补充数据。

    • 当客户端对某个索引的请求被分发到ES的协调节点时,协调节点会将请求进行转发,转发的对象时包括这个索引的所有分片的部分节点。协调节点中有一份分片-节点路由表,该表主要存放分片和节点的对应关系。协调节点会用轮询的算法,选取该索引的主/副分片所在的节点进行请求转发。一个索引的主分片设定后就不能修改,如果想提升索引的并发性可以增加副分片的数量,此时协调节点会将这些副分片加入到轮询算法中。
  • 路由计算

    • 当客户端向一个ES的协调节点发送请求时,协调节点是如何将数据存储在哪个节点的哪个分片上?

      • 根据hash算法,公式如下:

        shard = hash(routing)%number_of_prinary_shards
        #routing :每条文档的提交是的参数,该值是可变的,用户可以自定义,在默认情况下使用的是文档的_id值
        #number_of_prinary_shards:索引主分片的个数
        

    计算出routing值后除以索引主分片的个数在取余,就是当前文档应该存储的分片ID。

    获取分片ID后,根据分片-节点路由表获取该分片的主/副分分片节点列表,然后在转发请求。

    通过上面的公式number_of_primary_shards主分片作为取余的分母不能随意改变,否则分片ID将会计算错误,进而找不到存储数据的分片节点。

  • 文档读写过程

    • 写入

      • 当ES的协调节点收到来自客户端写入数据的请求时,协调节点会根据路由算法算出将文档写入到主分片上,然后将请求转发给分片所在节点上,完成数据存储后,该节点会将请求转发给该分片的其他副分片所在的节点,直到副分片节点完全写入成功,ES协调节点向客户端返回成功信息,如图:

      ElasticSearch概念与架构原理

      • 假如:我们的集群中有三个节点,索引有三个主分片和六个副分片,当客户端发送写入数据请求到节点1,假设节点1为协调节点,根据路由算法算出将数据写入到分片1上,由于分片1的主分片在节点2上,则会将请求转发给节点2,节点接收到客户端的数据并完成存储,再将请求转发给副分片1所在的节点1和节点3上,当所有的副分片完成数据存储后,协调节点再向客户端返回成功标志。
    • 读取

      • 当ES协调节点收到客户端发来的查询文档请求,协调节点会根据文件找到所有的分片,根据轮询算法从主/副分片中选一个分片,然后将请求转发给该分片所在的节点,该节点完成请求后并将目标数据返回给协调节点,协调节点再将数据返回给客户端,如图:

        ElasticSearch概念与架构原理

        当客户端发起查询请求到节点1[协调节点],协调节点会根据文档找到所有的分片,使用轮询算法从主/副分片中选一个分片,假如在节点3中的副分片1中,完成请求后节点3将目标数返回给节点1,节点1再将数据返回给客户端。

三、ElasticSearch搜索入门

  • 创建索引 使用 postman创建索引
    //在postman输入ES地址:http://localhost:9200/[索引名称]
    //访问方式为:PUT
    { 
        "mappings":{ 
            "properties":{           //指定字段名称及其数据类型 
                "title":{ 
                    "type":"text"    //title字段为text类型 
                }, 
                "city":{ 
                   "type":"keyword"   //city字段为keyword类型 
                }, 
                "price":{ 
                    "type":"double"   //price字段为double类型 
                } 
            } 
        } 
    }
    
    • 写入文档
      //在postman输入ES地址:http://localhost:9200/[索引名称]/_doc/[id编号]
      //访问方式为:Post
      { 
      "title":"好再来酒店", 
      "city":"青岛", 
      "price":578.23 
      }
      
  • 根据_Id搜索文档
    //在postman输入ES地址:http://localhost:9200/[索引名称]/_doc/[id编号]
    //访问方式为:Get
    { 
     "_index" : "hotel",                  //索引名称 
      "_type" : "_doc", 
     "_id" : "001",                       //文档ID 
     "_version" : 1,                      //文档版本 
      "_seq_no" : 0, 
      "_primary_term" : 1, 
      "found" : true, 
     "_source" : {                        //文档内容 
        "title" : "好再来酒店", 
        "city" : "青岛", 
        "price" : 578.23 
      } 
    }
    
  • 根据一般字段搜索文档
    • 在ES中进行搜索时需要用到query子句,代码如下
    //在postman输入ES地址:http://localhost:9200/[索引名称]/_search
    //访问方式为:Get
    {
      "query":{ //查询内容
         .........
       }
    }
    //例如:
    {
      "query":{
         "price":{
               "value":578.23 
           }
       }
    }
    //查询结果如下:
    { 
      "took" : 1, 
      "timed_out" : false, 
     "_shards" : {       //命中的分片信息 
        "total" : 1, 
        "successful" : 1, 
        "skipped" : 0, 
        "failed" : 0 
      }, 
      "hits" : {  
       "total" : {                         //命中的文档总数 
          "value" : 1, 
          "relation" : "eq" 
        }, 
       "max_score" : 1.0,                  //命中文档中的最高分
       "hits" : [                          //命中文档集合的信息
          { 
            "_index" : "hotel",            //文档所在索引 
            "_type" : "_doc",   
            "_id" : "001",                //文档ID 
            "_score" : 1.0,               //文档分值 
            "_source" : {                 //文档内容 
              "title" : "好再来酒店", 
              "city" : "青岛", 
              "price" : 578.23 
            } 
          } 
        ] 
      } 
    }
    
  • 根据文本字段搜索文档
    • 前面的搜索功能在传统的关系型数据库中也可以胜任,但是对文本进行模糊匹配并给出匹配分数这一功能是搜索引擎独有的,此时使用match搜索对某个字段进行模糊匹配,代码如下:
    //在postman输入ES地址:http://localhost:9200/[索引名称]/_search
    //访问方式为:Get
    {
       "query":{
         "match":{
            "title":"再来"
          }
       }
    }
    //查询结果:
    { 
      … 
        "max_score" : 0.5753642,                     //命中文档中的最高分 
       "hits" : [                                    //命中文档集合的信息 
          {  
            "_index" : "hotel", 
            "_type" : "_doc", 
            "_id" : "001", 
            "_score" : 0.5753642, 
            "_source" : { 
              "title" : "好再来酒店", 
              "city" : "青岛", 
              "price" : 578.23 
            } 
          } 
        ] 
      } 
    }