集群内原理

1 节点类型

默认情况下,每个节点都有成为主机点的资格,也会存储数据,也会处理客户端请求. 在一个集群中,我们一般对节点职责进行划分.

节点类型配置职责
master节点node.master=true,node.data=false集群相关操作:建立/删除索引、节点健康管理、分片分配给那些节点
data节点node.master=false,node.data=true存储和搜索数据,不参与选举
client节点node.mast=false,node.data=false接受海量客户端请求,请求负载均衡

2 索引

  • 2.1 概念

    索引是指向一个或多个物理分片的逻辑命名空间.

    一个分片是一个底层的工作单元,保存了全部数据中的一部分. 一个分片是一个Lucene示例,是一个完整的搜索引擎.
    我们的文档被索引到分片内,应用程序是直接和索引交互,而不是分片.

    比如:我们在一个单节点集群创建一个索引:

    PUT /blogs
    curl -X PUT "localhost:9200/blogs?pretty" -H 'Content-Type: application/json' -d ''	
    {
    	"settings" : {
      		"number_of_shards" : 3,                 # 主分片:创建索引被定义,我们无法后续修改,只能修改副本分片数量
      		"number_of_replicas" : 1                # 副本分片
    	}
    }
    

3 集群可扩展性

  • a. 拥有一个索引的单节点集群 — 副本分片无法分配

在这里插入图片描述
查看集群的健康状态: 健康状态为Yellow,表示有副本分片异常. 看到未分配副本数==3, 因为在单节点存储副本是没有意义的.

  • b. 拥有两个节点的集群——所有主分片和副本分片都已被分配 此时集群状态为"green".

在这里插入图片描述

  • c. 拥有三个个节点的集群—为了分散负载而对分片进行重新分配

    每个节点的硬件资源(cpu、内存、IO)被更少的分片共享,每个分片的性能将会得到提升.
    分片是一个功能完整的搜索引擎,拥有使用一个节点上的所有资源的能力. 我们拥有6个分片(3个主分片和3个副本分片)的索引最大可以扩容到6个节点,每个节点存在一个分片,并且每个分片拥有所在节点的全部资源.
    在这里插入图片描述

  • d. 更多的扩容

    主分片的数目在索引创建时被确定. 主分片的数目决定了索引能够存储的最大数据量. 但是,读操作–搜索和返回数据,可以同时被主分片和副本分片处理.
    因此,增加副本分片数量,可以提高es集群的吞吐量.

    将参数 number_of_replicas 调大到 2: 注意:在相同节点增加副本数量并不能提高吞吐量,因为每个分片获取的硬件资源会减少.
    在这里插入图片描述

  • e.应对故障

    要是宕掉一个节点,那么es集群故障恢复如下:

    1.选举新的master节点. 此时集群状态为red(要是丢失主分片的话.)
    2.新master节点将丢失的主分片的副本提升为主分片. 此时集群状态为yellow,因为有副本分片数量不符合条件.
    3.进行副本分片的复制,最后集群状态为green.

4 分布式文档存储

  • 4.1 文档如何选择主分片

    索引一个文档的时候,文档会被存储到一个主分片中.
    主分片选择公式:

    # routing一般为文档的_id值. 也可以设置自定义值
    shard = hash(routing) % number_of_primary_shards
    

    这就解释了为什么我们要在创建索引的时候就确定好主分片的数量 并且永远不会改变这个数量:因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了.

  • 4.2 新建、索引和删除文档

    新建、索引和删除 请求都是 写 操作, 必须在主分片上面完成之后才能被复制到相关的副本分:
    在这里插入图片描述
    1、客户端向 Node 1 发送新建、索引或者删除请求.
    2、节点使用文档的 _id 确定文档属于分片 0 。请求会被转发到 Node 3,因为分片 0 的主分片目前被分配在Node 3 上.
    3、Node 3 在主分片上面执行请求。如果成功了,它将请求并行转发到 Node 1 和 Node 2 的副本分片上。一旦所有的副本分片都报告成功, Node 3 将向协调节点报告成功,协调节点向客户端报告成功.

  • 4.3 取回文档

    可以从主分片或者从其它任意副本分片检索文档:
    在这里插入图片描述
    1、客户端向 Node 1 发送获取请求.
    2、节点使用文档的 _id 来确定文档属于分片 0 .分片 0 的副本分片存在于所有的三个节点上。 在这种情况下,它将请求转发到 Node 2 .
    3、Node 2 将文档返回给 Node 1 ,然后将文档返回给客户端.

  • 4.4 局部更新文档

    基于文档的复制: 主分片会将更新后完整的文档异步复制到副本分片,而不会转发更新请求,避免出现文件损坏.
    1、客户端向 Node 1 发送更新请求。
    2、它将请求转发到主分片所在的 Node 3 。
    3、Node 3 从主分片检索文档,修改 _source 字段中的 JSON ,并且尝试重新索引主分片的文档。 如果文档已经被另一个进程修改,它会重试步骤 3 ,超过 retry_on_conflict 次后放弃。
    4、如果 Node 3 成功地更新文档,它将新版本的文档并行转发到 Node 1 和 Node 2 上的副本分片,重新建立索引。 一旦所有副本分片都返回成功, Node 3 向协调节点也返回成功,协调节点向客户端返回成功。

在这里插入图片描述

  • 4.5 多文档模式

    多文档模式类似于单文档模式,区别在于协调节点直到每个文档位于那个分片.
    请求:协调节点将多文档请求拆分成每个分片的多文档请求,转发请求到每个参与节点
    响应:收到每个节点的应答,将每个节点的响应整合成单个响应,返回给客户端

5 分析器

  • 5.1 什么时候使用分析器?

    当我们 索引 一个文档,它的全文域被分析成词条以用来创建倒排索引.

    • 当你查询一个 全文 域时, 会对查询字符串应用相同的分析器,以产生正确的搜索词条列表.
    • 当你查询一个 精确值 域时,不会分析查询字符串, 而是搜索你指定的精确值.

6 映射

  • 6.1 核心简单域类型

    字符串: string
    整数 : byte, short, integer, long
    浮点数: float, double
    布尔型: boolean
    日期: date
    
  • 6.2 查看映射

    GET /gb/_mapping/tweet
    

7 执行分布式检索

搜索被执行成一个两阶段过程,我们称之为 query then fetch.

  • 7.1 查询阶段

    协调节点:收到请求的节点,负责广播请求到索引相关分片. 协调节点将在之后的请求中轮询所有的分片拷贝来分摊负载.

    1、客户端发送一个 search 请求到 Node 3 , Node 3 会创建一个大小为 from + size 的空优先队列。
    2、Node 3 将查询请求转发到索引的每个主分片或副本分片中。每个分片在本地执行查询并添加结果到大小为 from + size 的本地有序优先队列中。
    3、每个分片返回各自优先队列中所有文档的 ID 和排序值给协调节点,也就是 Node 3 ,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。
    在这里插入图片描述

  • 7.2 取回阶段

    1、协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。
    2、每个分片加载并 丰富 文档,如果有需要的话,接着返回文档给协调节点。
    3、一旦所有的文档都被取回了,协调节点返回结果给客户端。
    在这里插入图片描述

8 深分页

先查后取的过程支持用 from 和 size 参数分页,但是这是 有限制的 。 要记住需要传递信息给协调节点的每个分片必须先创建一个 from + size 长度的队列,协调节点需要根据 number_of_shards * (from + size) 排序文档,来找到被包含在 size 里的文档.
足够大的 from 值,排序过程可能会变得非常沉重,使用大量的CPU、内存和带宽.
如果你 确实 需要从你的集群取回大量的文档,你可以通过用 scroll 查询禁用排序使这个取回行为更有效率.
  • 8.1 游标查询

    scroll 查询 可以用来对 Elasticsearch 有效地执行大批量的文档查询,而又不用付出深度分页那种代价.
    游标查询允许我们 先做查询初始化,然后再批量地拉取结果.

    GET /old_index/_search?scroll=1m 
    {
    	"query": { "match_all": {}},
    	"sort" : ["_doc"], 
    	"size":  1000
    }
    
    GET /_search/scroll
    {
    "scroll": "1m", 
    "scroll_id" : "cXVlcnlUaGVuRmV0Y2g7NTsxMDk5NDpkUmpiR2FjOFNhNnlCM1ZDMWpWYnRROzEwOTk1OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MTA5OTM6ZFJqYkdhYzhTYTZ5QjNWQzFqVmJ0UTsxMTE5MDpBVUtwN2lxc1FLZV8yRGVjWlI2QUVBOzEwOTk2OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MDs="
    }
    

9 索引管理

  • 9.1 创建索引

    PUT /my_index
    {
    "settings": { ... any settings ... },
    "mappings": {
        "type_one": { ... any mappings ... },
        "type_two": { ... any mappings ... },
        ...
    	}
    }
    

    如果你想禁止自动创建索引,你 可以通过在 config/elasticsearch.yml 的每个节点下添加下面的配置:

    action.auto_create_index: false
    
  • 9.2 删除索引

    DELETE /my_index
    
    DELETE /index_one,index_two
    DELETE /index_*
    
    DELETE /_all
    DELETE /*
    

    避免意外的大量删除, 你可以在你的 elasticsearch.yml 做如下配置:

    # 不允许通过指定 _all 或通配符来删除指定索引库
    action.destructive_requires_name: true
    
  • 9.3 索引配置

    两个 最重要的设置:
    number_of_shards:每个索引的主分片数,默认值是 5 .这个配置在索引创建后不能修改.
    number_of_replicas:每个主分片的副本数,默认值是 1 .对于活动的索引库,这个配置可以随时修改.
    创建只有 一个主分片,没有副本的小索引:

    PUT /my_temp_index
    {
    	"settings": {
        	"number_of_shards" :   1,
        	"number_of_replicas" : 0
    	}
    }
    

    可以用 update-index-settings API 动态修改副本数:

    PUT /my_temp_index/_settings
    {
    	"number_of_replicas": 1
    }
    
  • 9.4 动态映射

    Elasticsearch 遇到文档中以前 未遇到的字段,它用 dynamic mapping 来确定字段的数据类型并自动把新的字段添加到类型映射.

    可以用 dynamic 配置来控制这种行为 ,可接受的选项如下:
    true:动态添加新的字段–缺省
    false:忽略新的字段
    strict: 如果遇到新字段抛出异常
    配置参数 dynamic 可以用在根 object 或任何 object 类型的字段上:

    PUT /my_index
    {
    	"mappings": {
        	"my_type": {
            	"dynamic":      "strict",                      # 不允许动态新增新字段
            	"properties": {
                	"title":  { "type": "string"},
                	"stash":  {
                    	"type":     "object",      
                    	"dynamic":  true                      # 允许动态新增新字段
             	  	 }
            	}
        	}
    	}
    }
    

    可以给 stash 对象添加新的可检索的字段:

    PUT /my_index/my_type/1
    {
    	"title":   "This doc adds a new field",
    	"stash": { "new_field": "Success!" }
    }
    

    对根节点对象 my_type 进行同样的操作会失败:

    PUT /my_index/my_type/1
    {
    	"title":     "This throws a StrictDynamicMappingException",
    	"new_field": "Fail!"
    }
    

10 分片内部原理

  • 10.1 为什么搜索是 近 实时的?

    elasticsearch是按段进行搜索的,每个segment都可以认为是一个倒排索引.

    基本数据组成结构:
    在这里插入图片描述
    新段进来时,首先会存在于内存缓冲区中,随后每个一秒触发refresh操作(写入和打开一个新段的轻量的过程叫做 refresh)—提交新段到文件系统缓存中,此时新段已经可以被搜索到.
    文档的变化并不是立即对搜索可见,但会在一秒之内变为可见: 如果没有搜索到,可以执行:POST /_refresh 、POST /blogs/_refresh .
    在这里插入图片描述

  • 10.2 为什么文档的 CRUD (创建-读取-更新-删除) 操作是 实时 的?

    当你试图通过文档ID来读取、更新、删除一个文档时,它会首先检查translog日志看看有没有最新的更新,然后再从相应的segment中获得文档。

  • 10.3 Elasticsearch 是怎样保证更新被持久化在断电时也不丢失数据?

    一个文档被索引之后,就会被添加到内存缓冲区,并且 追加到了 translog.
    translog 提供所有还没有被刷到磁盘的操作的一个持久化纪录.
    在这里插入图片描述

  • 10.4 为什么删除文档不会立刻释放空间?

    段是不可变的,既不能把文档从旧段删除,也不能修改旧段进行文档更新.
    取而代之是每个提交点会维护一个.del文件,文件中会列出这些被删除文档的段信息.

    当一个文档被 “删除” 时,它实际上只是在 .del 文件中被 标记 删除. 一个被标记删除的文档仍然可以被查询匹配到, 但它会在最终结果被返回前从结果集中移除.
    文档更新也是类似的操作方式:当一个文档被更新时,旧版本文档被标记删除,文档的新版本被索引到一个新的段中. 可能两个版本的文档都会被一个查询匹配到,但被删除的那个旧版本文档在结果集返回前就已经被移除.

    在段合并的时候,一个被删除的文档才会被文件系统回收.

  • 10.5 段合并

    由于自动刷新流程每秒会创建一个新的段 ,这样会导致短时间内的段数量暴增。而段数目太多会带来较大的麻烦。 每一个段都会消耗文件句柄、内存和cpu运行周期。更重要的是,每个搜索请求都必须轮流检查每个段;所以段越多,搜索也就越慢。

    两个提交的段和一个未提交的段合并成一个新的段:
    在这里插入图片描述
    一旦合并结束,老的段被删除:
    合并大的段需要消耗大量的I/O和CPU资源,如果任其发展会影响搜索性能。Elasticsearch在默认情况下会对合并流程进行资源限制,所以搜索仍然 有足够的资源很好地执行在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值