Elasticsearch之分布式介绍
Search API实现对es中存储的数据进行查询分析,endpoint为 _search,如下图所示:查询主要有两种形式- URI Search- 操作简便,方便通过命令行测试- 仅包含部分查询语法- Request Boy Search- es 提供的完备查询语法 Query DSL (Domain Specific Language)通过 url query 参数来实现...
分布式特性
- es支持集群模式,是一个分布式系统,其好处主要有两个:
- 增大系统容量,如内存、磁盘、使得es集群可以支持PB级的数据
- 提高系统可用性,即使部分节点停止服务,整个集群依然可以正常服务 - es 集群由多个es实例组成
- 不同集群通过集群名字来区分,可通过cluster.name 进行修改,默认elasticsearch
- 每个es实例本质上是一个JVM进程,且有自己的名字,通过node.name进行修改
- 运行如下命令可以启动一个es节点实例
- bin/elasticsearch -E cluster.name=my_cluster -E node.name=node1 -E http.port=5200 -d(以后台守护进程运行)
Master Node
- 可以修改 cluster state 的节点称为 master 节点, 一个集群只能有一个
- cluster state 存储在每个节点上,master 维护最新版本并同步给其他节点
- master 节点通过集群中所有节点选举产生的,可以被选举的节点称为 master-eligible 节点,相关配置如下
- node.master:true (默认 true)
Coordinating Node
- 处理请求的节点即为 coordinating 节点,该节点为所有节点的默认角色,不能取消
路由请求到正确的节点处理,比如创建索引的请求到master节点
Data Node
- 存储数据的节点即为data节点,默认节点都是data类型,相关配置如下
node.data: true
副本与分片
提高系统可用性
- 服务可用性
- 2个节点的情况下,允许其中1个节点停止服务 - 数据可用性
- 引入副本(Replication)解决
- 每个节点上都有完备的数据
增大系统容量
- 如何将数据分布于所有节点上?
- 引入分片(Shard)解决问题 - 分片是 es支持PB级数据的基石
- 分片存储了部分数据,可以分布于任意节点上
- 分片数在索引创建时指定且不允许在更改,默认为5个
- 分片有分片和副分片之分,以实现数据的高可用(副分片数可以随时调整)
- 副本分片的数据由主分片同步,可以有多个,从而提高读取的吞吐量
分片
- 下图演示的是三个节点的集群中 test_index 的分片分布情况创建时我们指定了3个分片和一个副本,api如下图所示:(实际节点数较少,比如两个,一个节点将会有两个分片)
- 此时增加节点是否会提高 test_index 的数据容量
- 不能,因为只要3个分片,已经分布在3台节点上,新增的节点无法利用。
- 此时增加副本数是否提高 test_index 的读取吞吐量?
- 不能。因为新增的副本就是分布在这3个节点上,还是利用了同样的资源,如果需要增加吞吐量,还需要新增节点。
- 分片数的设定很重要,需要提前规划好
- 过小会导致后续无法添加节点实现水平扩容
- 过大会导致一个节点上分布过多分片,造成资源浪费,同时会影响查询性能
Cluster Health
- 通过如下 api 可以查看集群健康状态,包括以下三种:
- green 健康状态,指所有主副分片都正常分配
- yellow 指所有主分片都正常分配,但是有副本分片未正常分配
- red 有主分片为分配
故障转移
node1 所在机器宕机导致服务终止,此时集群会如何处理?
-
- nodel2 和node3发现nodel1无法响应一段时间后会发起 master选举,比如这里选择 node2为master,此时由于主分片P0下线,集群状态变为Red。
- nodel2 和node3发现nodel1无法响应一段时间后会发起 master选举,比如这里选择 node2为master,此时由于主分片P0下线,集群状态变为Red。
-
- node2 发现主分片P0未分配,将R0提升为主分片。此时有所有主分片都正常分配,集群状态变成Yellow。
- node2 发现主分片P0未分配,将R0提升为主分片。此时有所有主分片都正常分配,集群状态变成Yellow。
-
- nodel2为P0和P1生成新的副本,集群状态变成绿色
- nodel2为P0和P1生成新的副本,集群状态变成绿色
文档分布式存储
文档最终会存储在分片上,如下图所示:
- Document 1最终存储在分片P1上
Document1 是如何存储到分片P1的?选择P1的依据是什么?
需要文档到分片的映射算法
- 目的
使得文档均匀分布在所有分片上,以充分利用资源
- 算法
- 随机选择或者 round-robin 算法
- 不可取,因为需要文档到分片的映射关系,成本巨大
- 根据文档值试试极端对应的分片 - es 通过如下的公式计算文档对应的分片
- shard = hash(rounting) % number_of_primary_shards
- hash 算法保证可以将数据均匀的分散在分片中
- routing是一个关键参数,默认是文档id,也可以自行指定(比如统一板块的数据放在一个分片上,以提高查询效率)
- number_of_primary_shards 是主分片数 - 该算法与珠分配数相关,这也是分片数一旦确定后便不能改的原因
文档创建的流程
-
- Client 向node3 发起创建文档的请求
-
- node3通过 routing计算该文档应该在 Shard1 上,查询cluster state后确认主分片P1在node2 上,然后转发创建文档的请求到node2
-
- P1 接受并执行创建文档请求后,将同样的请求发送到副本分片R1
-
- R1接受并执行创建文档请求后,通知P1成功的结果
-
- P1接受副本分片结果后,通知node3创建成功
- P1接受副本分片结果后,通知node3创建成功
文档读取的流程
文档批量创建的流程
文档批量读取流程
脑裂问题
- 脑裂问题,英文为split-brain,是分布式系统中的经典网络问题,如下图所示:
- 3 个节点组成的集群,突然node1的网络和其他两个节点中断
- node2 与node3会重新选举master,比如node2成为新master,此时会更新cluster state
- node1 自己组成集群后,也会更新cluster state - 同一个集群有两个 master,而且维护不同 cluster status ,网络恢复后无法选择正确的 master
解决方案为尽在选举master-eligible 节点数大于等于quorum时才可以进行 master选举
- quorum = master-eligible 节点数/2 + 1,列如3个 master-eligible节点时,quorum为2。
- 设定 discovery.zen.minimum_master_nodes 为quorum即可避免脑裂问题
倒排索引的不可变更
-
倒排索引一旦生成,不能更改
-
其好处如下:
- 不用考虑并发写文件的问题,杜绝了锁机制带来的性能问题
- 由于文件不再更改,可以充分利用文件系统缓存,只需载入一次,只要内存足够对该文件的读取都会从内存读取,性能高
- 利于生成缓存数据
- 利于对文件进行压缩存储,节省磁盘和内存存储空间 -
坏处为需要写入新文档时,必须重新构建倒排索引文件然后替换老文件后,新文档才能被索引到,导致文档实时性差
-
解决方案是新文档直接生成新的倒排索引文件,查询的时候同时查询所有的倒排文件,然后做结果的汇总计算即可
-
Lucene 便是采用了这种方案,他构建的单个倒排索引称为 segment,合在一起称为Index,与ES中的Index感念不同。ES中的一个Shard对应一个 Lucene Index.
-
Lucene 会有一个专门的文件来记录所有的segment 信息,称为 commit point
文档搜索实时性 - refresh
- segment 写入磁盘的过程依然很耗时,可以借助文件系统缓存的特性,先将 segment在缓存中创建并开发查询来进一步提升实时性改过程在es中称为 refresh
- 在refresh之前文档会先存储在一个buffer中, refresh时将 buffer 中的所有文档清空并生成 segment
- es 默认每1s执行UI次 refresh,因此文档的实时性被提高到1s,这也是es被称为近实时(Near Real Time)的原因
文档搜索实时性 - translog
- 如果在内存中的segment还没有写入磁盘前发生了宕机,那么其中的文档就无法恢复了,如何解决问题?
- es引入了 translog机制,写入文档到buffer时, 同时将该操作写入 translog。
- translog 文件会即时写入磁盘(fsync),6.x默认每个请求都会漏盘,可以修改为每5秒写入一次,这样风险便是丢失5秒内的数据,相关配置为 index.translog.*
- es 启动时会检查 translog文件,并从中恢复数据
文档搜索实时性 - flush
-
flush 负责将内存中的 segment写入磁盘,主要做如下的工作:
- 将 translog 写入磁盘
- 将 index buffer清空,其中的文档生成一个新的segment,相当于一个refresh操作
- 更新 commit point 并写入磁盘
- 执行 fsync 操作,将内存中的 segment 写入磁盘
- 删除旧的translog
-
refresh 发送的时机主要有如下几种情况:
- 间隔时间达到时,通过 index.settings.refresh_interval 来设定,默认是1秒
- index.buffer 占满时,其大小通过 indices.menory.index_buffer_size设置,默认为 jvm heap的10%,所有shard共享
- flus发生时也会发生refresh -
flush 发生的时机主要有如下几种情况:
- 间隔时间达到时,默认是30分钟,5.x之前可以通过index.translog,flush_threshold_period 修改,之后无法修改
- translog 暂满时,其大小 index.translog.flush_threshold_size 控制,默认是512 mb,每个 index有自己的translog
删除与更新文档
- segment一旦生成就不能更改, 那么如果你要删除温暖当该如何操作?
- Lucene 专门维护一个 .del的文件,记录所有已经删除的文档, 注意.del 上记录的是文档在 Lucene 内容的 id
- 在查询结果返回前会过滤掉 .del 中的所有文档 - 更新文档如何进行呢
- 首先删除文档,然后在创建新文档
整体视角
- ES Index 与 Lucene Index 的术语对照入校所示:
Segment Merging
- 随着 segment 的增多,由于一次查询的 segment数增多,查询速度会慢
- es会定时在后台进行segment merge的操作,减少 segment数量
- 通过 force_merge api 可以手动强制做 segment merge 的操作
参考
更多推荐
所有评论(0)