• Es官方文档整理-2.分片内部原理


    Es官方文档整理-2.分片内部原理

    1.集群
         一个运行的Elasticsearch实例被称为一个节点,而集群是有一个或多个拥有相同claster.name配置的节点组成,他们共同承担数据和负载压力,当有节点加入或从集群中移除的时候,集群或自动平局分布所有数据。
         当一个节点被选举成为主节点时,他哈不负责额管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等。而主节点不涉及文档级别的变更和搜索操作,所以集群只有一个主节点,即使流量增加,他也不会成为瓶颈。任何节点都可以成为主节点。
     
     
    3.添加索引
         PUT /blogs
         {
              "settings":{
                   "number_of_shards":3,
                   "number_of_replicas":1
              }
         }
    设置三个主分片,1个复制分片。
     
    4.水平扩展
         PUT /blogs/_settings
         {
             "number_of_replicas":2 
         }
    复制分片从一个增加到两个。不能修改主分片个数。
     
    5.路由一个文档到分片
         当索引一个文档时,文档会被存储到主分片中。存到哪个主分片由下面这个公式决定:
    shard = hash(routing) % number_of_primary_shards.
         routing是一个可变值,默认是文档_id,也可以设置一个自定义的值。
         number_of_primary_shards主分片数。
         这里解释了为什么不能改变主分片数量:因为如果数量变化了,那么所有的之前路由的值都会无效,文档也找不到了
     
    6.新建、索引和删除单个文档
         1).客户端向集群中的一个节点(协调节点)发送请求。
         2).节点使用文档的_id确定文档所属的分片,转发请求到分片所属的节点(master).
         3).主分片执行请求。若执行成功,将请求并行转发到复制节点。一旦所有复制节点报告执行成功。master节点向协调节点报告成功,协调节点向客户端报告成功。
     
    7.检索单个文档
         1).客户端向集群中的一个节点(node1)发出请求。
         2).node1节点使用_id确定文档所属分片,将请求转发大其中个一个分片所属节点(node2)。
         3).node2将文档返回给node1,然后将文档返回给客户端。
    协调节点每次请求的时候选择不同的副本分片达到负载均衡。
     
    8.局部更新
         1).客户端向node1发送更新请求。
         2).转发请求到主分片所在的node2。
         3).node2从主分片检索文档,修改_source字段中的JSON,并尝试重新索引主分片文档。如果文档已经被另一个进程修改了,他将重试步骤3,超过retry_on_conflict次后放弃。
         4).如果node2成功更新索引,它将新版本的文档转发到所有副本分片所在的节点,重新建立索引。一旦所有副本分片都成功返回了,node2向协调节点返回成功,协调节点向客户端返回成功。
     
    9.倒排索引
         Elasticsearch使用一种称为倒排索引的结构,它适用于快速的全文搜索。一个倒排索引有文档中所有不重复的词的列表构成,对于其中每个词,有一个包含他的文档列表。类似下面:(被索引的字段都有自己的倒排索引)
    Term  | Doc 1 | Doc 2 | Doc 3 | ...
    ------------------------------------
    brown |   X   |        |  X    | ...
    fox      |   X   |   X   |  X    | ...
    quick   |   X   |   X   |        | ...
    the      |   X   |        |  X    | ...
    它会保存每一个词项出现过的文档总数, 在对应的文档中一个具体词项出现的总次数,词项在文档中的顺序,每个文档的长度,所有文档的平均长度,等等。
    不变性:
    倒排索引被芯茹磁盘后是不可改变的:它永远不会被修改。不可修改的意义:
         1).不需要锁。
         2).写入单个大的倒排索引允许被压缩。
         3).一旦索引被读入内核的文件系统缓存,便会留在哪里,由于其不变性。只要文件系统缓存中还有足够的空间,那么大部分读请求会直接请求内存,而不会命中磁盘。这提供了很大的性能提升。
         4).其它缓存(像filter缓存),在索引的生命周期内始终有效。它们不需要在每次数据改变时被重建,因为数据不会变化。
     
         当然,一个不变的索引也有不好的地方。主要事实是它是不可变的! 你不能修改它。如果你需要让一个新的文档 可被搜索,你需要重建整个索引。这要么对一个索引所能包含的数据量造成了很大的限制,要么对索引可被更新的频率造成了很大的限制。
     
    10.动态更新索引
         Elasticsearch 基于 Lucene, 这个 java 库引入了 按段搜索(per-segment search) 的概念。 每一 segment 本身都是一个倒排索引, 但 索引 在 Lucene 中除表示所有 segment 的集合外, 还增加了 提交点 的概念 — 一个列出了所有已知段的文件。
     
         索引与分片的比较:
         被混淆的概念是,一个 Lucene 索引 我们在 Elasticsearch 称作 分片 。 一个 Elasticsearch 索引 是分片的集合。 当 Elasticsearch 在索引中搜索的时候, 他发送查询到每一个属于索引的分片(Lucene 索引),然后像 执行分布式检索 提到的那样,合并每个分片的结果到一个全局的结果集。
     
    一个 Lucene 索引包含一个提交点和三个段:
     
    一个在内存缓存中包含新文档的 Lucene 索引:
    在一次提交后,一个新的segment被添加到提交点而且缓存被清空:
         1).新文档被收集到内存索引缓存, “一个在内存缓存中包含新文档的 Lucene 索引” 。
         2).不时地, 缓存被 提交 :
              一个新的segment --一个追加的倒排索引--被写入磁盘。
              一个新的包含新segment 名字的 提交点 被写入磁盘。
              磁盘进行 同步 — 所有在文件系统缓存中等待的写入都刷新到磁盘,以确保它们被写入物理文件。
         3).新的segment 被开启,让它包含的文档可见以被搜索。
         4).内存缓存被清空,等待接收新的文档。
    删除和更新:
         段是不可改变的,所以既不能从把文档从旧的段中移除,也不能修改旧的段来进行反映文档的更新。 取而代之的是,每个提交点会包含一个 .del 文件,文件中会列出这些被删除文档的段信息。
         当一个文档被 “删除” 时,它实际上只是在 .del 文件中被 标记 删除。一个被标记删除的文档仍然可以被查询匹配到, 但它会在最终结果被返回前从结果集中移除。
    文档更新也是类似的操作方式:当一个文档被更新时,旧版本文档被标记删除,文档的新版本被索引到一个新的段中。 可能两个版本的文档都会被一个查询匹配到,但被删除的那个旧版本文档在结果集返回前就已经被移除。
     
    11.近实时搜索
         随着按段(per-segment)搜索的发展, 一个新的文档从索引到可被搜索的延迟显著降低了。新文档在几分钟之内即可被检索,但这样还是不够快。
         磁盘在这里成为了瓶颈。提交(Commiting)一个新的段到磁盘需要一个 fsync 来确保段被物理性地写入磁盘,这样在断电的时候就不会丢失数据。 但是 fsync 操作代价很大; 如果每次索引一个文档都去执行一次的话会造成很大的性能问题。
         我们需要的是一个更轻量的方式来使一个文档可被搜索,这意味着 fsync 要从整个过程中被移除。
         在Elasticsearch和磁盘之间是文件系统缓存,在内存索引缓冲区中的文档会被写入到一个新的segment中。 但是这里新段会被先写入到文件系统缓存--这一步代价会比较低,稍后再被刷新到磁盘--这一步代价比较高。不过只要文件已经在缓存中, 就可以像其它文件一样被打开和读取了。 
    在内存缓冲区中包含了新文档的 Lucene 索引: 
    缓冲区的内容已经被写入一个可被搜索的段中,但还没有进行提交:
    Refresh API
         在 Elasticsearch 中,写入和打开一个新段的轻量的过程叫做 refresh 。 默认情况下每个分片会每秒自动刷新一次。这就是为什么我们说 Elasticsearch 是 近 实时搜索: 文档的变化并不是立即对搜索可见,但会在一秒之内变为可见。
         POST /_refresh :刷新(Refresh)所有的索引。
         POST /blogs/_refresh :只刷新(Refresh) blogs 索引。
         并不是所有的情况都需要每秒刷新。可能你正在使用 Elasticsearch 索引大量的日志文件, 你可能想优化索引速度而不是近实时搜索, 可以通过设置 refresh_interval , 降低每个索引的刷新频率:
         PUT /my_logs
         {
                "settings": {
                "refresh_interval": "30s"
           }
         }
         在生产环境中,当你正在建立一个大的新索引时,可以先关闭自动刷新,待开始使用该索引时,再把它们调回来:
    PUT /my_logs/_settings
    { "refresh_interval": -1 }
     
    PUT /my_logs/_settings
    { "refresh_interval": "1s" }
     
    12.持久化变更
         如果没有用 fsync 把数据从文件系统缓存刷(flush)到硬盘,我们不能保证数据在断电甚至是程序正常退出之后依然存在。为了保证 Elasticsearch 的可靠性,需要确保数据变化被持久化到磁盘。
          Elasticsearch 增加了一个 translog ,或者叫事务日志,在每一次对 Elasticsearch 进行操作时均进行了日志记录。通过 translog ,整个流程看起来是下面这样:
              1).一个文档被索引之后,就会被添加到内存缓冲区,并且 追加到了 translog.
              2).刷新(refresh)使分片,分片每秒被刷新(refresh)一次:
                   a.这些在内存缓冲区的文档被写入到一个新的段中,且没有进行 fsync 操作。   ** 这个段被打开,使其可被搜索。
                   b.内存缓冲区被清空。
              3).这个进程继续工作,更多的文档被添加到内存缓冲区和追加到事务日志.
              4).每隔一段时间--例如 translog 变得越来越大--索引被刷新(flush);一个新的 translog 被创建,并且一个全量提交被执行:
                   a.所有在内存缓冲区的文档都被写入一个新的段。
                   b.缓冲区被清空。
                   c.一个提交点被写入硬盘。
                   d.文件系统缓存通过 fsync 被刷新(flush)。
                   e.老的 translog 被删除。 
         translog 提供所有还没有被刷到磁盘的操作的一个持久化纪录。当 Elasticsearch 启动的时候, 它会从磁盘中使用最后一个提交点去恢复已知的段,并且会重放 translog 中所有在最后一次提交后发生的变更操作。
         Flush API
              这个执行一个提交并且截断 translog 的行为在 Elasticsearch 被称作一次 flush 。 分片每30分钟被自动刷新(flush),或者在 translog 太大的时候也会刷新。可以手动刷新:
                   POST /blogs/_flush
                   POST /_flush?wait_for_ongoing
    Translog 的安全问题:默认,在每次请求后都会通过fsync把请求写到日志文件。你也可以使用异步的 fsync ,写入的数据被缓存到内存中,每5秒执行一次 fsync :
    PUT /my_index/_settings
    {
        "index.translog.durability": "async",
        "index.translog.sync_interval": "5s"
    }
     
    13.segment合并
         由于自动刷新流程每秒会创建一个新的segment ,这样会导致短时间内的segment 数量暴增。而segment 数目太多会带来较大的麻烦。 每一个segment 都会消耗文件句柄、内存和cpu运行周期。更重要的是,每个搜索请求都必须轮流检查每个segment ;所以segment 越多,搜索也就越慢。
         Elasticsearch通过在后台进行segment 合并来解决这个问题。小的segment 被合并到大的segment ,然后这些大的segment 再被合并到更大的segment 。
         segment 合并的时候会将那些旧的已删除文档 从文件系统中清除。 被删除的文档(或被更新文档的旧版本)不会被拷贝到新的大segment 中。
    启动segment 合并不需要你做任何事。进行索引和搜索时会自动进行。
         1).当索引的时候,刷新(refresh)操作会创建新的segment 并将segment 打开以供搜索使用。
         2).合并进程选择一小部分大小相似的segment ,并且在后台将它们合并到更大的segment 中。这并不会中断索引和搜索。
         3).一旦合并结束,老的段被删除, 说明合并是完成的活动:
              a.新的段被刷新(flush)到了磁盘。   ** 写入一个包含新段且排除旧的和较小的段的新提交点。
              b.新的段被打开用来搜索。
              c.老的段被删除。
         合并大的segment需要消耗大量的I/O和CPU资源,如果任其发展会影响搜索性能。Elasticsearch在默认情况下会对合并流程进行资源限制,所以搜索仍然 有足够的资源很好地执行。
     
    Optimize API
         optimize API大可看做是 强制合并 API 。它会将一个分片强制合并到 max_num_segments 参数指定大小的段数目。 这样做的意图是减少段的数量(通常减少到一个),来提升搜索性能。
         optimize API 不应该 被用在一个动态索引————一个正在被活跃更新的索引。后台合并流程已经可以很好地完成工作。 optimizing 会阻碍这个进程。不要干扰它!
         在特定情况下,使用 optimize API 颇有益处。例如在日志这种用例下,每天、每周、每月的日志被存储在一个索引中。 老的索引实质上是只读的;它们也并不太可能会发生变化。在这种情况下,使用optimize优化老的索引,将每一个分片合并为一个单独的段就很有用了;这样既可以节省资源,也可以使搜索更加快速:
         POST /logstash-2014-10/_optimize?max_num_segments=1
         请注意,使用 optimize API 触发段合并的操作一点也不会受到任何资源上的限制。这可能会消耗掉你节点上全部的I/O资源, 使其没有余裕来处理搜索请求,从而有可能使集群失去响应。 如果你想要对索引执行 `optimize`,你需要先使用分片分配(查看 迁移旧索引)把索引移到一个安全的节点,再执行。 
  • 相关阅读:
    hdu 4332 Constructing Chimney 夜
    poj 2449 Remmarguts' Date 夜
    poj 2728 Desert King 夜
    poj 1639 Picnic Planning 夜
    poj 1125 Stockbroker Grapevine 夜
    poj 3621 Sightseeing Cows 夜
    hdu 4333 Revolving Digits 夜
    hdu 4345 Permutation 夜
    hdu 1874 通畅工程续 夜
    es6(二)
  • 原文地址:https://www.cnblogs.com/ydxblog/p/8304266.html
Copyright © 2020-2023  润新知