CodeAshen's blog CodeAshen's blog
首页
  • Spring Framework

    • 《剖析Spring5核心原理》
    • 《Spring源码轻松学》
  • Spring Boot

    • Spring Boot 2.0深度实践
  • Spring Cloud

    • Spring Cloud
    • Spring Cloud Alibaba
  • RabbitMQ
  • RocketMQ
  • Kafka
  • MySQL8.0详解
  • Redis从入门到高可用
  • Elastic Stack
  • 操作系统
  • 计算机网络
  • 数据结构与算法
  • 云原生
  • Devops
  • 前端
  • 实用工具
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
  • Reference
GitHub (opens new window)

CodeAshen

后端界的小学生
首页
  • Spring Framework

    • 《剖析Spring5核心原理》
    • 《Spring源码轻松学》
  • Spring Boot

    • Spring Boot 2.0深度实践
  • Spring Cloud

    • Spring Cloud
    • Spring Cloud Alibaba
  • RabbitMQ
  • RocketMQ
  • Kafka
  • MySQL8.0详解
  • Redis从入门到高可用
  • Elastic Stack
  • 操作系统
  • 计算机网络
  • 数据结构与算法
  • 云原生
  • Devops
  • 前端
  • 实用工具
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
  • Reference
GitHub (opens new window)
  • MySQL8.0详解与实战

  • MySQL面试指南

  • Redis从入门到高可用

  • Elastic-Stack

    • 第01章-Elasticsearch入门
    • 第02章-倒排索引与分词
    • 第03章-Mapping设置
    • 第04章-Search API
    • 第05章-分布式特性介绍
      • 1.1 ES 集群介绍
      • 1.2 ES 集群节点
        • 1.2.1 节点状态 Cluster State
        • 1.2.2 主节点 Master Node
        • 1.2.3 协调节点 Coordinating Node
        • 1.2.4 数据节点 Data Node
      • 2.1 副本
      • 2.2 分片
      • 2.3 副本与分片的思考
      • 3.1 健康状态
      • 3.2 故障转移
      • 4.1 文档映射算法
      • 4.2 文档的读写流程
        • 4.2.1 文档创建流程
        • 4.2.2 文档读取流程
        • 4.2.3 文档批量创建流程
        • 4.2.4 文档批量读取流程
      • 6.1 倒排索引不可变
      • 6.2 搜索实时性方案
      • 6.3 文档搜索实时性操作
        • 6.3.1 refresh
        • 6.3.2 translog
        • 6.3.3 flush
      • 6.4 实时性操作发生时机
      • 6.5 删除与更新文档
      • 6.6 Shard 整体结构
      • 6.7 Segment Merging
    • 第06章-Search的运行机制
    • 第07章-聚合分析
    • 第08章-数据建模
    • 第09章-集群调优建议
  • 数据库
  • Elastic-Stack
CodeAshen
2023-02-10
目录

第05章-分布式特性介绍

# 一、ES 分布式概述

# 1.1 ES 集群介绍

es 支持集群模式,是一个分布式系统,其好处主要有两个:

  • 增大系统容量,如内存、磁盘,使得 es 集群可以支持 PB 级的数据
  • 提高系统可用性,即使部分节点停止服务,整个集群依然可以正常服务

es 集群由多个 es 实例组成

  • 不同集群通过集群名字来区分,可通过 clustername 进行修改,默认为 elasticsearch
  • 每个 es 实例本质上是一个 JVM 进程,且有自己的名字,通过 node.name 进行修改

Cerebro 是一个可以查看es集群管理工具,地址:https://github.com/lmenezes/cerebro

# 1.2 ES 集群节点

启动一个节点

运行如下命令可以启动一个 es 节点实例

bin/elasticsearch -Eclustername=my_cluster -Enode.name=node1

可以启动多个节点实例构建一个 es 集群

# 1.2.1 节点状态 Cluster State

es 集群相关的数据称为 cluster state,主要记录如下信息:

  • 节点信息,比如节点名称、连接地址等
  • 索引信息,比如索引名称、配置等
  • ……

# 1.2.2 主节点 Master Node

  • 可以修改 cluster state 的节点称为 master节点,一个集群只能有一个
  • cluster state 存储在每个节点上,master 维护最新版本并同步给其他节点
  • master 节点是通过集群中所有节点选举产生的,可以被选举的节点称为 master-eligible节点,相关配置为:-node.master:true

# 1.2.3 协调节点 Coordinating Node

  • 处理请求的节点即为 coordinating 节点,该节点是所有节点的默认角色,不能取消
  • 作用是路由请求到正确的节点处理,比如创建索引的请求到 master 节点

# 1.2.4 数据节点 Data Node

存储数据的节点即为 data 节点,默认节点都是 data 类型,相关配置为:node.data:true

# 二、副本与分片

# 2.1 副本

提高系统可用性,包括以下两个方面

  • 服务可用性:2 个节点的情况下,允许其中 1 个节点停止服务
  • 数据可用性:引入 副本(Replication) 解决,每个节点上都有完备的数据

# 2.2 分片

如何将数据分布于所有节点上?es 通过引入 分片(Shard) 解决问题

  • 分片是 es 支持 PB 级数据的基石
  • 分片存储了部分数据,可以分布于任意节点上
  • 分片数在索引创建时指定且后续不允许再更改,默认为 5 个
  • 分片有主分片和副本分片之分,以实现数据的高可用
  • 副本分片的数据由主分片同步,可以有多个,从而提高读取的吞吐量

下图演示的是 3 个节点的集群中 test_index 的分片分布情况,创建时我们指定了 3 个分片和 1 个副本,api 如下所示:

image-20201226233241250

# 2.3 副本与分片的思考

基于上图,思考两个问题:

  • 问:此时增加节点是否能提高 test_index 的数据容量?

    答:不能。因为只有 3 个分片,已经分布在 3 台节点上,新增的节点无法利用。

    image-20201226233421953

  • 问:此时增加副本数是否能提高 test_index 的读取吞吐量?

    答:不能。因为新增的副本也是分布在这 3 个节点上,还是利用了同样的资源。如果要增加吞吐量,还需要新增节点。

    image-20201226233527667

总结:分片数的设定很重要,需要提前规划好

  • 过小会导致后续无法通过增加节点实现水平扩容
  • 过大会导致一个节点上分布过多分片,造成资源浪费,同时会影响查询性能

# 三、集群状态与故障转移

# 3.1 健康状态

通过如下 api 可以查看集群健康状况,包括以下三种:

GET _cluster/health
1
  • green:健康状态,指所有主副分片都正常分配
  • yellow:指所有主分片都正常分配,但是有副本分片未正常分配
  • red:有主分片未分配(比如磁盘空间不足)

集群处于 red 状态并不意味着不能对外提供服务,是可以正常访问的

# 3.2 故障转移

集群由 3 个节点组成,如下所示,此时集群状态是 Green。

image-20201226234606149

node1 所在机器宕机导致服务终止,此时集群会如何处理?

  1. node 之间会互相 ping 检查节点状态,node2 和 node3 发现 node1 无法响应一段时间后,会发起 master 选举,比如这里选择 node2 为 master 节点。此时由于主分片 P0 下线,集群状态变为 Red。

    image-20201226234632847

  2. node2 发现主分片 P0 未分配,将 R0 提升为主分片。此时由于所有主分片都正常分配,集群状态变为 Yellow。

    image-20201226234957010

  3. node2 为 P0 和 P1 生成新的副本,集群状态变为 Green。

    image-20201226235017175

# 四、文档分布式存储

# 4.1 文档映射算法

文档最终会存储在分片上。如下图所示,Document1 最终存储在分片 P1 上

image-20201226235606967

Document1 是如何存储到分片 P1 的?选择 P1 的依据是什么?

答案:有一个文档到分片的映射算法

目的:使得文档均匀分布在所有分片上,以充分利用资源

算法:是否可以随机选择或者 round-robin 算法?不可取,因为需要维护文档到分片的映射关系,成本巨大。应该根据文档值实时计算对应的分片

映射算法:

es 通过如下的公式计算文档对应的分片

  • shard = hash(routing) % number_of_primary_shards
  • hash 算法保证可以将数据均匀地分散在分片中
  • routing 是一个关键参数,默认是文档 id,也可以自行指定
  • number_of_primary_shards 是主分片数
  • 该算法与主分片数相关,这也是 分片数一旦确定后便不能更改 的原因

# 4.2 文档的读写流程

# 4.2.1 文档创建流程

image-20201227001110197

# 4.2.2 文档读取流程

image-20201227001025827

# 4.2.3 文档批量创建流程

image-20201227001317524

# 4.2.4 文档批量读取流程

image-20201227001439884

# 五、脑裂问题

脑裂问题,英文为 split-brain,是分布式系统中的经典网络问题,如下图所示:

  • node2 与 node3 会重新选举 master,比如 node2 成为了新 master,此时会更新 cluster state
  • node1 自己组成集群后,也会更新 cluster state
  • 同一个集群有两个 master,而且维护不同的 cluster state,网络恢复后无法选择正确的 master

image-20201227001547075

解决方案为仅在可选举 master-eligible 节点数大于等于 quorum 时才可以进行 master 选举

  • quorum = master-eligible节点数 / 2 + 1,例如 3 个 master-eligible 节点时, quorum 为 2
  • 配置 discovery.zen.minimum_master nodes 为 quorum 即可避免脑裂

image-20201227001802838

# 六、shard 详解

# 6.1 倒排索引不可变

倒排索引一旦生成,不能更改,其好处如下:

  • 不用考虑并发写文件的问题,杜绝了锁机制带来的性能问题
  • 由于文件不再更改,可以充分利用文件系统缓存,只需载入一次,只要内存足够,对该文件的读取都会从内存读取,性能高
  • 利于生成缓存数据
  • 利于对文件进行压缩存储,节省磁盘和内存存储空间

坏处为需要写入新文档时,必须重新构建倒排索引文件,然后替换老文件后,新文档才能被检索,导致文档实时性差。查询流程如下图:

image-20201227025225189

# 6.2 搜索实时性方案

如何解决文档搜索实时性问题呢?

解决方案是新文档直接生成新的倒排索引文件,查询的时候同时查询所有的倒排文件,然后做结果的汇总计算即可。查询流程如下图:

image-20201227025447071

  • Lucene 便是采用了这种方案,它构建的单个倒排索引称为 segment,合在一起称为 Index,与 ES 中的 Index 概念不同。ES 中的一个 Shard 对应一个 Lucene Index。
  • Lucene 会有一个专门的文件来记录所有的 segment 信息,称为 commit point

image-20201227025633407

# 6.3 文档搜索实时性操作

# 6.3.1 refresh

  • segment 写入磁盘的过程依然很耗时,可以借助文件系统缓存的特性,先将 segment 在缓存中创建并开放查询来进一步提升实时性,该过程在 es 中被称为 refresh。
  • 在 refresh 之前文档会先存储在一个 buffer 中,refresh 时将 buffer 中的所有文档清空并生成内存 segment
  • es 默认每 1 秒执行一次 refresh,refresh 之后文档就可以被搜索到了,因此文档的实时性被提高到 1 秒,这也是 es 被称为 准实时(Near Real Time) 的原因

image-20201227030400157

# 6.3.2 translog

如果在内存中的 segment 还没有写入磁盘前发生了宕机,那么其中的文档就无法恢复了,如何解决这个问题?

  • es 引入 translog 机制。写入文档到 buffer 时,同时将该操作写入 translog。
  • translog 文件会即时写入磁盘(fsync),6.x 默认每个请求都会落盘,可以修改为每 5 秒写一次,这样风险便是丢失 5 秒内的数据,相关配置为 index.translog。
  • es 启动时会检查 translog 文件,并从中恢复数据。

image-20201227030711001

# 6.3.3 flush

flush 负责将内存中的 segment 写入磁盘,主要做如下的工作:

  • 将 translog 写入磁盘,生成 translog file
  • 将 index buffer 清空,其中的文档生成一个新的内存 segment,相当于一个 refresh 操作
  • 执行 fsync 操作,将内存中的 segment 写入磁盘,生成磁盘 segment
  • 更新 commit point,记录新生成的 segment
  • 删除旧的 translog file 和内存中的 segment

image-20201227030914473

# 6.4 实时性操作发生时机

refresh 发生的时机主要有如下几种情况:

  • 间隔时间达到时,通过 index.settings.refresh interval 来设定,默认是 1 秒
  • index.buffer 占满时,其大小通过 indices.memory.index_buffer_size 设置,默认为 jvm heap 的 10%,所有 shard 共享
  • flush 发生时也会发生 refresh

flush 发生的时机主要有如下几种情况:

  • 间隔时间达到时,默认是 30 分钟,5.x 之前可以通过 index.translog.flush threshold period 修改,之后版本无法修改
  • translog 占满时,其大小可以通过 index.translog.flush threshold _size 控制,默认是 512mb,每个 index 有自己的 translog

# 6.5 删除与更新文档

文档搜索实时性—删除与更新文档

  • segment 一旦生成就不能更改,那么如果你要删除文档该如何操作?

    Lucene 专门维护一个 .del 的文件,记录所有已经删除的文档,注意 .del 上记录的是文档在 Lucene 内部的 id,在查询结果返回前会过滤掉 .del 中的所有文档

  • 更新文档如何进行呢?

    首先删除文档,然后再创建新文档

# 6.6 Shard 整体结构

ES Index 与 Lucene Index 的术语对照如下所示:

image-20201227031538628

ES 索引分为好几个 Shard,每个 Shard 对应一个 Lucene Index,Lucene Index 由一堆 Segment 组成,还有一个 .del 文件记录已经删除的文档,有一个 Commit Point 文件来维护所有的 Segment 和 .del 文件。

# 6.7 Segment Merging

Segment Merging 操作解决 Segment 文件越来越多的问题。

  • 随着 segment 的增多,由于一次查询的 segment 数增多,查询速度会变慢
  • es 会定时在后台进行 segment merge 的操作,减少 segment 的数量
  • 通过 force_merge api 可以手动强制做 segment merge 的操作
编辑 (opens new window)
上次更新: 2023/06/04, 12:34:19
第04章-Search API
第06章-Search的运行机制

← 第04章-Search API 第06章-Search的运行机制→

最近更新
01
第01章-RabbitMQ导学
02-10
02
第02章-入门RabbitMQ核心概念
02-10
03
第03章-RabbitMQ高级特性
02-10
更多文章>
Theme by Vdoing | Copyright © 2020-2023 CodeAshen | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式