文章目录
分布式介绍及cerebro
分布式特性:
es支持集群模式,是一个分布式系统,其好处主要有两个:
- 增大系统容量,如内存、磁盘,使得es集群可以支持PB级的数据。-提高系统可用性,即使部分节点停止服务,整个集群依然可以正常服务
es集群由多个es实例组成
- 不同集群通过集群名字来区分,可通过cluster.name进行修改,默认为elasticsearch
- 每个es实例本质上是一个JVM进程,且有自己的名字,通过node.name进行修改
cerebro安装地址:
http://github.com/lmenezes/cerebro
解压运行/bin/cerebro
es集群相关的数据称为cluster state ,主要记录如下信息:
- 节点信息,比如节点名称、连接地址等
- 索引信息,比如索引名称、配置等
- ……
Master Node
- 可以修改cluster state的节点称为master节点,一个集群只能有一个-cluster state存储在每个节点上, master维护最新版本并同步给其他节点
- master节点是通过集群中所有节点选举产生的,可以被选举的节点称为master-eligible节点,相关配置如下:
- node.master:true
- node.master:true
创建一个索引
我们通过如下api创建一个索引
PUT test_index
Coordinating Node
.处理请求的节点即为coordinating节点,该节点为所有节点的默认角色,不能取消
·路由请求到正确的节点处理,比如创建索引的请求到master节点
Data Node
存储数据的节点即为data节点,默认节点都是data类型,相关配置如下:
- node.data: true
副本与分片
提高系统的可用性
服务可用性
- 2个节点的情况下,允许其中1个节点停止服务
数据可用性
- 引入副本(Replication)解决
- 每个节点上都有完备的数据
副本
如下图所示, node2上是test index的副本
增大系统容量
如何将数据分布于所有节点上?
- 引入分片( Shard )解决问题
分片是es支持PB级数据的基石
- 分片存储了部分数据,可以分布于任意节点上
- 分片数在索引创建时指定且后续不允许再更改,默认为5个
- 分片有主分片和副本分片之分,以实现数据的高可用
- 副本分片的数据由主分片同步,可以有多个,从而提高读取的吞吐量
分片
下图演示的是3个节点的集群中test index的分片分布情况,创建时我们指定了3个分、和1个副本, api如下所示:
此时增加节点是否能提高test_index的数据容量?
此时增加副本数是否能提高test_index的读取吞吐量?
此时增加节点是否能提高testindex的数据容量?
- 不能。因为只有3个分片,已经分布在3台节点上,新增的节点无法利用。
.此时增加副本数是否能提高testindex的读取吞吐量? - 不能。因为新增的副本也是分布在这3个节点上,还是利用了同样的资源。如果要增加吞吐量,还需要新增节点。
分片数的设定很重要,需要提前规划好 - 过小会导致后续无法通过增加节点实现水平扩容
- 过大会导致一个节点上分布过多分片,造成资源浪费,同时会影响查询性能
集群状态
Cluster Health
通过如下api可以查看集群健康状况,包括以下三种
- green健康状态,指所有主副分片都正常分配.
- yellow指所有主分片都正常分配,但是有副本分片未正常分配
- red有主分片未分配
故障转移
node1所在机器宕机导致服务终止,此时集群会如何处理?
- node2和node3发现nodel无法响应一段时间后会发起master选举,比如这里选择node2为master节点。此时由于主分片P0下线,集群状态变为Red,
- node2发现主分片P0未分配,将R0提升为主分片。此时由于所有主分片都正常分配,集群状态变为Yellow.
- node2为P0和P1生成新的副本,集群状态变为绿色
文档分布式存储
文档最终会存储在分片上,如下图所示:
- Document 1最终存储在分片P1上
Document 1是如何存储到分片P1的?选择P1的依据是什么? - 需要文档到分片的映射算法
.目的
- 使得文档均匀分布在所有分片上,以充分利用资源
·算法
- 随机选择或者round-robin算法?
- 不可取,因为需要维护文档到分片的映射关系,成本巨大
- 根据文档值实时计算对应的分片
文档到分片的映射算法
es通过如下的公式计算文档对应的分片
- shard = hash(routing) % number_of_primary_shards
- hash算法保证可以将数据均匀地分散在分片中
- routing是一个关键参数,默认是文档id ,也可以自行指定
- number_of_primary_shards是主分片数
该算法与主分片数相关,这也是分片数一旦确定后便不能更改的原因
文档创建的流程
- Client向node3发起创建文档的请求
- node3通过routing计算该文档应该存储在Shard 1上,查询cluster state后确认主分片P1在 node2上,然后转发创建文档的请求到node2 .
- P1接收并执行创建文档请求后,将同样的请求发送到副本分片R1
- R1接收并执行创建文档请求后,通知P1成功的结果,
- P1接收副本分片结果后,通知node3创建成功,
- node3返回结果到Client
文档读取的流程
- Client向node3发起获取文档1的请求
- node3通过routing计算该文档在Shard 1上,查询cluster state后获取Shard 1的主副分片列表,然后以轮询的机制获取一个shard,比如这里是R1 ,然后转发读取文档的请求到node1
- R1接收并执行读取文档请求后,将结果返回node3
- node3返回结果给Client
文档批量创建的流程
- Client向node3发起批量创建文档的请求(bulk)
- node3通过routing计算所有文档对应的shard ,然后按照主shard分配对应执行的操作,同时发送请求到涉及的主shard,比如这里3个主shard都需要参与
- 主shard接收并执行请求后,将同样的请求同步到对应的副本shard
- 副本shard执行结果后返回结果到主shard,主shard再返回node3
- node3整合结果后返回Client后5、node3整合结果后返回Client后
文档批量读取的流程
- Client向node3发起批量获取文档的请求(mget)
- node3通过routing计算所有文档对应的shard ,然后以轮询的机制获取要参与shard ,按照shard " 构建mget请求,同时发送请求到涉及的shard ,比如这里有2个shard需要参与
- R1, R2返回文档结果
- node3返回结果给Client
脑裂问题
·脑裂问题,英文为split-brain ,是分布式系统中的经典网络问题,如下图所示:
- 3个节点组成的集群,突然nodel的网络和其他两个节点中断
- node2与node3会重新选举master ,比如node2成为了新master ,此时会更新 cluster state
- node1自己组成集群后,也会更新cluster state·同一个集群有两个master ,而且维护不同的cluster state ,网络恢复后无法选择正确的主
解决方案为仅在可选举master-eligible节点数大于等于quorum时才可以进行master选举 - quorum = master-eligible节点数/2 +1,例如3个master-eligible节点时, quorum为2.
- 设定discovery.zen.minimum_master_nodes为quorum即可避免脑裂
shard详解
倒排索引的不可变更
倒排索引一旦生成,不能更改
其好处如下:
- 不用考虑并发写文件的问题,杜绝了锁机制带来的性能问题
- 由于文件不再更改,可以充分利用文件系统缓存,只需载入一次,只
要内存足够,对该文件的读取都会从内存读取,性能高 - 利于生成缓存数据
- 利于对文件进行压缩存储,节省磁盘和内存存储空间
坏处为需要写入新文档时,必须重新构建倒排索引文件,然后替换老文件后,新文档才能被检索,导致文档实时性差,
文档搜索实时性
解决方案是新文档直接生成新的倒排索引文件,查询的时候同时查询所有的倒排文件,然后做结果的汇总计算即可
Lucene便是采用了这种方案,它构建的单个倒排索引称为segment ,合在一起称为 Index,与ES中的Index概念不同。ES中的一个Shard对应一个Lucene Index.
Lucene会有一个专门的文件来记录所有的segment信息,称为commit point
文档搜索实时性-refresh
refresh发生的时机主要有如下几种情况:
- 间隔时间达到时,通过index.settings.refresh_interval来设定,默认是1秒 .
- index.buffer占满时,其大小通过indices.memory.index_buffer_size设置,默认为jvm heap的10%,所有shard共享
- flush发生时也会发生refresh
segment写入磁盘的过程依然很耗时,可以借助文件系统缓存的特性,先将segment在缓存中创建并开放查询来进一步提升实时性,该过程在es中被称为refresh.
在refresh之前文档会先存储在一个buffer中, refresh时将buffer中的所有文档清空并生成segment
es默认每1秒执行一次refresh ,因此文档的实时性被提高到1秒,这也是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文件
flush发生的时机主要有如下几种情况: - 间隔时间达到时,默认是30分钟, 5.x之前可以通过 index.translog.flush_threshold_period修改,之后无法修改 .
- translog占满时,其大小可以通过index.translog.flush_threshold_ size控制,默认是512mb ,每个index有自己的translog
文档搜索实时性-删除与更新文档
. segment一旦生成就不能更改,那么如果你要删除文档该如何操作?
- Lucene专门维护一个.del的文件,记录所有已经删除的文档,注意.del上记录的是文档在Lucene内部的id
- 在查询结果返回前会过滤掉.del中的所有文档
·更新文档如何进行呢?
- 首先删除文档,然后再创建新文档
整体视角
ES Index与Lucene Index的术语对照如下所示:
Sagment Merging
·随着segment的增多,由于一次查询的segment数增多,查询速度会变慢
.es会定时在后台进行segment merge的操作,减少segment的数量
·通过force-merge api可以手动强制做segment merge的操作