zoukankan      html  css  js  c++  java
  • Elasticsearch FAQ

    0 基础知识  

     

    1 接近实时(NRT):
    Elasticsearch是一个接近实时的搜索平台。这意味着,从索引一个文档直到这个文档能够被搜索到有一个轻微的延迟(通常是1秒)。
    2 集群(cluster): 
    一个集群就是由一个或多个节点组织在一起,它们共同持有你整个的数据,并一起提供索引和搜索功能。一个集群由一个唯一的名字标识,这个名字默认就是“elasticsearch”。这个名字是重要的,因为一个节点只能通过指定某个集群的名字,来加入这个集群。在产品环境中显式地设定这个名字是一个好习惯,但是使用默认值来进行测试/开发也是不错的。
    3 节点(node): 
    一个节点是你集群中的一个服务器,作为集群的一部分,它存储你的数据,参与集群的索引和搜索功能。和集群类似,一个节点也是由一个名字来标识的,默认情况下,这个名字是一个随机的漫威漫画角色的名字,这个名字会在启动的时候赋予节点。这个名字对于管理工作来说挺重要的,因为在这个管理过程中,你会去确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。
    一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中,这意味着,如果你在你的网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中。
    在一个集群里,只要你想,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。
    4 索引(index): 
    一个索引就是一个拥有几分相似特征的文档的集合。比如说,你可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引。一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。在一个集群中,如果你想,可以定义任意多的索引。 
    5 类型(type): 
    在一个索引中,你可以定义一种或多种类型。一个类型是你的索引的一个逻辑上的分类/分区,其语义完全由你来定。通常,会为具有一组共同字段的文档定义一个类型。比如说,我们假设你运营一个博客平台并且将你所有的数据存储到一个索引中。在这个索引中,你可以为用户数据定义一个类型,为博客数据定义另一个类型,当然,也可以为评论数据定义另一个类型。 
    6 文档(document) 
    一个文档是一个可被索引的基础信息单元。比如,你可以拥有某一个客户的文档,某一个产品的一个文档,当然,也可以拥有某个订单的一个文档。文档以JSON(Javascript Object Notation)格式来表示,而JSON是一个到处存在的互联网数据交互格式。 
    在一个index/type里面,只要你想,你可以存储任意多的文档。注意,尽管一个文档,物理上存在于一个索引之中,文档必须被索引/赋予一个索引的type。 
    7 分片和复制(shards & replicas): 
    1)分片 
      当需要存储大规模数据时,由于内存或磁盘空间的限制,或者cpu计算能力的问题,不能达到复杂功能的要求时;可以考虑将数据拆分,每部分是一个单独的索引,称为分片;每个分片可以存储在不同节点上,当需要查询一个有多个分片构成的索引时,elasticsearch会将查询发送到每个相关的分片上,并将结果合并;这个过程对整个应用而言是透明的;一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。 
    分片之所以重要,主要有两方面的原因: 
    •  允许你水平分割/扩展你的内容容量
    • 允许你在分片(潜在地,位于多个节点上)之上进行分布式的、并行的操作,进而提高性能/吞吐量
    • 至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由Elasticsearch管理的,对于作为用户的你来说,这些都是透明的。  
    2)复制 
    在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,Elasticsearch允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片,或者直接叫复制。
    复制之所以重要,有两个主要原因: 
    •  在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。
    •  扩展你的搜索量/吞吐量,因为搜索可以在所有的复制上并行运行 
    总之,每个索引可以被分成多个分片。一个索引也可以被复制0次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。分片和复制的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变复制的数量,但是你事后不能改变分片的数量。 
    默认情况下,Elasticsearch中的每个索引被分片5个主分片和1个复制,这意味着,如果你的集群中至少有两个节点,你的索引将会有5个主分片和另外5个复制分片(1个完全拷贝),这样的话每个索引总共就有10个分片。  

     

    如图:60和61是一个集群,其中60为主节点。默认一共有5个分片,每个分片有一个复制。分片可以在集群中的任何一个节点上,图中是在60上。被复制的分片叫主分片,图中亮色的0,1,2,3,4都是主分片,暗色的为主分片的副本分片(复制)。简而言之,亮色的为主分片,暗色的为副本分片(复制)

    分片策略

    选择合适的分片数和副本数。ES的分片分为两种,主分片(Primary Shard)和副本(Replicas)。默认情况下,ES会为每个索引创建5个分片,即使是在单机环境下,这种冗余被称作过度分配(Over Allocation),目前看来这么做完全没有必要,仅在散布文档到分片和处理查询的过程中就增加了更多的复杂性,好在ES的优秀性能掩盖了这一点。假设一个索引由一个分片构成,那么当索引的大小超过单个节点的容量的时候,ES不能将索引分割成多份,因此必须在创建索引的时候就指定好需要的分片数量。此时我们所能做的就是创建一个新的索引,并在初始设定之中指定这个索引拥有更多的分片。反之如果过度分配,就增大了Lucene在合并分片查询结果时的复杂度,从而增大了耗时,所以我们得到了以下结论:我们应该使用最少的分片!主分片,副本和节点最大数之间数量存在以下关系: 节点数<=主分片数(副本数+1)*

    副本和分片的关系

    1. 副本是主分片的一个copy,同时具有分片和副本的索引建立文档时两者都得修改,默认是同步sync更新。es会将所有主分片的变动通知所有副本
    2. 更多副本意味着查询吞吐量将会增加,因为执行查询可以使用分片或分片的任一副本
    3. 更多副本会增强集群系统的容错性,因为当原始分片不可用时副本将替代原始分片发挥作用
    4. 更多分片使索引传送到更多服务器,意味着可以处理更多文件而不降低性能,同时意味着每个分片需要获取的数据量会减少,因为相对于较少分片时,单个分片的文件量更大
    5. 更多分片意味着搜索时会面临更多问题,因为必须从更多分片中合并结果,查询聚合时需要更多资源
    路由: 
    默认情况下,一批数据进入索引时es通过一个哈希算法平均的将文档分不到不同的分片上,查询的时候在各自分片上执行查询然后合并结果集并返回。但是在提交数据的时候或者在建立mapping的时候指定路有值,可以将指定文档放到指定分片上。查询的时候es直接在指定分片上查找需要的文档,例如同一个用户的订单放到同一个分片上,当这个用户来查找自己订单的时候就直接通过指定路由值来在单个分片上查询。我们只需要在数据提交到索引时,和查询时指定路由值即可,至于在哪个分片上查找由es管理,对于我们是透明的

    es默认最大返回 10000条数据,修改此配置:

    productindex/_settings  put
    
    {
      "index": {
        "max_result_window": 100000
      }
    }

    索引和查询时的路由机制: 

    默认情况下es会在所有索引分片中均匀地分配文档,es通过计算文档标识符的散列值来决定将文档放置于哪一个主分片上。查询时,首先查询所有节点(整个索引的所有主分片)得到标识符和匹配文档的得分(查询发散阶段)接着在发送一个内部查询,但仅仅发送到相关的分片上,最后获取所需文档构建响应(查询收集阶段)当指定路由值得时候,相同的路由值会生成相同的散列值,这也就保证了相同的路由值文档被分配到相同的分片上。搜索的时候指定路由值(直接在指定分片上执行查询命令),只需要搜索单个分片,而不是整个索引的所有分片。

    新建、索引和删除文档

    image 

    新建、索引和删除请求都是写(write)操作,它们必须在主分片上成功完成才能复制到相关的复制分片上。 下面我们罗列在主分片和复制分片上成功新建、索引或删除一个文档必要的顺序步骤:

    1. 客户端给Node 1发送新建、索引或删除请求。
    2. 节点使用文档的_id确定文档属于分片0。它转发请求到Node 3,分片0位于这个节点上。
    3. Node 3在主分片上执行请求,如果成功,它转发请求到相应的位于Node 1和Node 2的复制节点上。当所有的复制节点报告成功,Node3报告成功到请求的节点,请求的节点再报告给客户端。

    客户端接收到成功响应的时候,文档的修改已经被应用于主分片和所有的复制分片。你的修改生效了。 

    复制默认的值是sync(同步)。这将导致主分片得到复制分片的成功响应后才返回。如果你设置replication为async,请求在主分片上被执行后就会返回给客户端。它依旧会转发请求给复制节点,但你将不知道复制节点成功与否。上面的这个选项不建议使用。默认的sync复制允许Elasticsearch强制反馈传输。async复制可能会因为在不等待其它分片就绪的情况下发送过多的请求而使Elasticsearch过载。

    一 文档类型:

    es可以有多个文档类型,每个文档类型中有field名字相同的字段,他们的field类型必须是一致的;因为多个类型在索引中的存储结构是扁平化的,

    例如索引index有两个类型,类型1包含了A ,B 两个field, 类型2包含了 B ,C 两个field;他们在索引里的结构是:A,B,C 。相同的field名称共享他们的区域,所以B的类型在两个类型中必须是相同的

    每条文档类型的数据里,对方类型的独有字段里都是空值,类似于下面 product和attribute两个类型

    1)从技术上讲只要他们的field不是冲突的(无论field相互排斥(相互独立)还是共享field(field相同))都可以在同一个索引中

    2)重要的一点是:当你需要区分单个集合的不同部分时使用多类型是很好的

    3)类型不适合完全不同类型的数据。如果两个类型有互斥的字段集,这意味着你的索引的一半将包含“空”值(字段将是稀疏的),这将最终导致性能问题。

    在这些情况下,我们需要根据下面两个指标来判别:

    • Good: kitchen and lawn-care types inside the products index, because the two types are essentially the same schema

    • Bad: products and logs types inside the data index, because the two types are mutually exclusive. Separate these into their own indices

    二 应该采用何种Mapping数据结构

    es索引数据是类似于nosql数据库的存储方式,索引是独立文档文档的扁平的集合,一个文档应该包含所有用于搜索的信息;每个文档都是独立于其他节点的;

    但是我们是无法避免索引之间存在关联关系的情况,我们需要一种方式来建立文档之间的关系而解决现实世界中的业务联系问题;一共有四种方式:

    1)在一个类型的文档中加入另外一个文档的标识符(模拟关系型数据库)

    PUT /my_index/user/1 { "name": "John Smith", "email": "john@smith.com", "dob": "1970/10/24" } PUT /my_index/blogpost/2 { "title": "Relationships", "body": "It's complicated...", "user": 1 }

      在帖子对的文档类型里面加入用户的id,证明这个帖子是这个用户发的

    这样的话在搜索的时候我们要运行两次查询,例如我要搜索第一个名字叫john的用户发的帖子:首先你要先查出第一个名字叫john的所有id,然后根据user的id再去博客类型索引里查博客信息

    GET /my_index/user/_search { "query": { "match": { "name": "John" } } } GET /my_index/blogpost/_search { "query": { "filtered": { "filter": { "terms": { "user": [1,2] } } } } }

      优点是:文档数据都是独立的,增删改查都是针对同一个文档类型

      缺点是:我们要运行额外的查询,特别是当第一次查询返回数据多时,第二次将面临成千上万的查询;所以如果我们能保证第一次查询返回的数据极小时,且不经常发生改变(意味着可以缓存)的情况下使用这种方式是很好的

    2)通过非规范化数据(数据冗余)可以为搜索带来很好的性能

    PUT /my_index/user/1 { "name": "John Smith", "email": "john@smith.com", "dob": "1970/10/24" } PUT /my_index/blogpost/2 { "title": "Relationships", "body": "It's complicated...", "user": { "id": 1, "name": "John Smith" } }

      博客类型的索引里冗余了作者的信息,此时去搜索某个用户发的帖子只需要查询博客类型索引就可以了

    GET /my_index/blogpost/_search { "query": { "bool": { "must": [ { "match": { "title": "relationships" }}, { "match": { "user.name": "John" }} ] } } }

      这样子查询将会很快,因为文档中包含了额外的信息所以省略了链接的消耗

    3)嵌套

    文档对象嵌套,例如帖子和评论,一个帖子可以有很多评论;那么我们可以创建一个帖子文档,里面包含了评论的嵌套的模型;

    一个主文档链接多个附属文档,主文档和附属文档一同被索引并被放置在同一个块上确保为该数据结果获取最佳性能;修改时你需要同时索引主文档和附属文档

    由于嵌套文档的存储方式,我们的查询还是很快的,就像他们是同一个文档一样

    {
      "block": {
        "properties": {
          "name": {
            "type": "string",
            "index": "analyzed"
          },
          "comment": {
            "type": "nested",
            "properties": {
              "content": {
                "type": "string",
                "index": "analyzed"
              }
            }
          }
        }
      }
    }
      //在索引里的存储格式类似:
    
    { //附属文档
      "comments.name":    [ john, smith ],
      "comments.comment": [ article, great ],
      "comments.age":     [ 28 ],
      "comments.stars":   [ 4 ],
      "comments.date":    [ 2014-09-01 ]
    }
    { //附属文档
      "comments.name":    [ alice, white ],
      "comments.comment": [ like, more, please, this ],
      "comments.age":     [ 31 ],
      "comments.stars":   [ 5 ],
      "comments.date":    [ 2014-10-22 ]
    }
    {   //主文档
      "title":            [ eggs, nest ],
      "body":             [ making, money, work, your ],
      "tags":             [ cash, shares ]
    }
    View Code

    这些额外的嵌套附属文档是隐藏的,我们不能直接访问它们。更新,添加或删除一个嵌套的对象,我们必须重建整个文件;索请求返回的结果不是嵌套的对象,它是整个文档

    4)父子

    父子关系允许两个文档类型进行关联,亲子关系在性质上是相似的嵌套模型:都允许你将一个实体与另一个。不同的是,用嵌套的对象,所有的实体都在同一个文档内,而与父母和孩子是完全独立的文件。

    相对嵌套的优点:

    1 父和子的更新都可以在互不影响的情况下进行。

    2 当更新频繁的时候比嵌套要好

    3 可以独立返回父或子的搜索结果而不是整个文档

    缺点:

    1) 父和子要在同一个分片中,当插入文档指定了路由分片时,要注意这个问题

    2) 比起查询单独的文档,父子关联查询相对会慢

    3) 执行父子查询时,es会加载并缓存文档标识符到内存,必须确保es有足够的内存,不然会导致内存溢出

    4)es会维护一个父子关系的map,所以查询速度非常快(可能3中加载文档标识符与此相关)

  • 相关阅读:
    原型设计作业
    案例分析作业
    编程作业
    阅读任务
    自我介绍
    5 20210420-1 团队作业1—团队展示
    3 20210405-1 案例分析作业
    阅读任务
    自我介绍
    原型设计
  • 原文地址:https://www.cnblogs.com/shaner/p/6281898.html
Copyright © 2011-2022 走看看