集群角色
主节点master
可修改cluster state的节点。一个集群仅一个
选举节点master-eligible
可以参与选举master的节点
配置为:node.master:true。默认所有节点都可以参与选举
协调节点cordinating
处理请求的节点。是所有节点的默认角色,且不能取消
路由请求到正确的节点处理,如:创建索引的请求到master节点
从节点data
存储数据的节点,默认所有节点都是data类型
配置为:node.data:true
副本和分片
提高系统可用性
为了提高系统的可用性,从两个角度进行考虑:
A、服务可用性:在2个/多个节点的情况下,允许1个/一部分节点停止服务,整个集群依然可以对外提供服务;
B、 数据可用性:引入副本(replication)解决1个/一部分节点停止,节点上数据也同时丢失的情况。此时每个节点都有完备数据。
增大系统容量
如何将数据分布到所有节点?答案:分片
什么是分片?
分片是ES能支持PB级别数据的基石
a、分片存储部分数据,可以分布于任意节点;
b、分片数在索引创建时指定,且后续不能更改,默认为5个;
c、有主分片和副本分片之分,以实现数据的高可用;
d、副本分片由主分片同步数据,可以有多个,从而提高数据吞吐量
集群的健康状态
Greed,绿色。表示所有主分片和副本分片都正常分配;
Yellow,黄色。表示所有主分片都正常分配,但有副本分片未分配
Red,红色。表示有主分片未分配
故障转移 Failover
1)什么是故障转移?
在一个多节点集群中的master节点突然宕机,此时集群缺少主节点,
剩余的节点组成新的集群,并将幸存副本分片转为主分片,同时在剩余节点生成副本分片,
并依然对外进行正常服务的情况,称为故障转移。
2)如何进行故障转移?
当其余节点发现定时ping主节点master无响应的时候,集群状态转为Red。
此时会发起master选举。称为master的新主节点发现有主分片没有进行分配,
会将继续工作的节点上的副本分片升级为主分片,此时集群状态转为Yellow。
之后,主节点会为对应节点生成未分配的副本分片,此时集群状态转为Green。
整个故障转移过程结束。
文档分布式存储
Document最终存储到分片上,那么文档的数据是如何选择要存储的分片的呢?
此时,就需要文档到分片的映射算法,routing路由
文档创建的流程
A、Client向node3发送创建文档请求
B、node3通过routing计算该文档存储在shard1上,查询cluster state后,确认主shard1在node2上,然后转发请求到node2
C、node2上的主shard1接收并执行创建文档的请求后,将同样的请求转发到副shard1,查询cluster state后,确认副shard1在node1,向node1发送请求
D、node1上的副shard1接收并执行创建文档的请求后, 通知主shard1结果
E、node2上的主shard1接收到副shard1创建文档成功的结果,通知node3创建成功
F、node3返回结果给Client
文档读取的流程
A、Client向node3发送读取文档doc1的请求
B、node3通过routing计算该doc1在shard1上,查询cluster state后,确认主shard1和副shard1的位置,然后以轮询的机制获取一个shard(比如这次是主shard1,下次就是副shard1)
C、本次是在node2上的主shard1接收到读取文档的请求,执行并返回结果给node3
D、node3返回结果给Client
文档批量读取的流程
A、Client向node3发送批量读取文档请求(bulk)
B、node3通过routing计算文档对应的所有shard,再以轮询的机制,按shard构建mget请求,通过发送给设计的shard
C、由shard返回文档结果
D、node3整合后返回结果到Client
脑裂问题
什么是脑裂问题?
在分布式系统中有一个经典的网络问题
当一个集群在运行时,作为master节点的node1的网络突然出现问题,无法和其他节点通信,出现网络隔离情况。
那么node1自己会组成一个单节点集群,并更新cluster state;同时作为data节点的node2和node3因为无法和node1通信,
则通过选举产生了一个新的master节点node2,也更新了cluster state。
那么当node1的网络通信恢复之后,集群无法选择正确的master。
如何解决脑裂问题?
解决方案也很简单:仅在可选举的master-eligible节点数 >= quorum的时候才进行master选举
简单说就是节点过半选举
Shards分片详解
1)倒排索引一旦生成,不能更改
优点
不用考虑并发写文件的问题,杜绝了锁机制带来的性能问题
文件不在更改,则可以利用文件系统缓存,只需载入一次,只要内存足够,直接从内存中读取该文件,性能高
利于生成缓存数据(且不需更改)
利于对文件进行压缩存储,节省磁盘和内存存储空间
缺点
在写入新的文档时,必须重构倒排索引文件,然后替换掉老倒排索引文件后,新文档才能被检索到,导致实时性差
2)解决文档搜索的实时性问题的方案
新文档直接生成新待排索引文件,查询时同时查询所有倒排索引文件,然后做结果的汇总即可,从而提升了实时性
3)Segment
Lucene中构建的单个倒排索引,多个Segment合在一起称为Index(Lucene中的Index),Lucene中的一个index对应ES中的shard分片
Segment写入磁盘的过程依然很耗时,可以借助文件系统缓存的特性。【先将Segment在内存中创建并开放查询,来进一步提升实时性】,这个过程在ES中被称为:refresh。
在refresh之前,文档会先存储到一个缓冲队列buffer中,refresh发生时,将buffer中的所有文档清空,并生成Segment
ES默认每1s执行一次refresh操作,因此实时性提升到了1s。这也是ES被称为近实时的原因(Near Real Time)
4)translog文件
当文档写入buffer时,同时会将该操作写入到translog中,
这个文件会即时将数据写入磁盘,在6.0版本之后默认每个要求都必须落盘,这个操作叫做fsync操作。
这个时间也是可以通过配置:index.translog.*进行修改的。
比如每五秒进行一次fdync操作,那么风险就是丢失这5s内的数据
这个操作解决了:在节点写入磁盘之前就发生了宕机,这时候内存中的segment丢失的问题
5)文档搜索实时性—flush(十分重要)
flush的功能,就是:将内存中的Segment写入磁盘,主要做如下工作:
A、将translog写入磁盘;
B、将index bufffer清空,其中的文档生成一个新的Segment,相当于触发一次refresh;
C、更新Commit Point文件并写入磁盘;
D、执行fsync落盘操作,将内存中的Segment写入磁盘;
E、删除旧的translog文件。
6)refresh与flush的发生时机
A、refresh:发生时机主要有以下几种情况:
a、间隔时间达到。
通过index.settings.refresh_interval设置,默认为1s。
b、index.buffer占满时。
通过indices.memory.index_buffer_size设置,默认JVM heap的10%,且所有shard共享。
c、flush发生时。会触发一次refresh。
B、flush:发生时机主要有以下几种情况:
a、间隔时间达到。
5.x版本之前,通过index.translog.flush_threshold_period设置,默认30min。
5.x版本之后,ES强制每30min执行一次flush,不能再进行更改。
b、translog占满时。
通过index.translog.flush_threshold_size设置,默认512m。且每个Index有自己的translog。
7)删除和更新文档
A、删除:
Segment一旦生成,就不能更改,删除的时候,Lucene专门维护一个.del文件,记录所有已删除的文档。
.del 文件 上记录的是文档在Lucene中的ID,在查询结果返回之前,会过滤掉.del 文件 中的所有文档。
B、更新:
先删除老文档,再创建新文档,两个文档的ID在Lucene中的ID不同,但是在ElasticSearch中ID相同。
8)Segment Merging(合并)
A、随着Segment的增多,由于每次查询的Segment数量也增多,导致查询速度变慢;
B、ES会定时在后台进行Segment merge的操作,减少Segment数量;
C、通过force_merge api可以手动强制做Segment的合并操作。