索引分片和lucene关系近实时搜索(refresh)flush什么是translog
translog基本流程 merge
什么是mergemerge操作相关流程 集群内部原理
集群节点角色集群状态 集群启动流程
选举主节点
投票与得票的实现确立Master或加入集群节点失效检测 选举集群元信息allocation过程index recovery
索引分片和lucene关系一个Es索引包含很多分片,一个分片是一个Lucene的索引,它本身就是一个完整的搜索引擎,可以独立执行建立索引和搜索任务。Lucene索引又由很多分段组成,每个分段都是一个倒排索引。ES每次refresh都会生成一个新的分段,其中包含若干文档的数据,在每个分段内部,文档的不同字段被单独建立索引。每个字段的值由若干词(Term)组成,Term是原文本内容经过分词器处理和语言处理后的最终结果。
在写操作中,首先被写入到内存缓存中,默认每1秒将in-memory index buffer中的文档生成一个新的段并清空原有in-memory index buffer,新写入的段变为可读状态,但是还没有被完全提交。该新的段首先被写入文件系统缓存,保证段文件可以正常被正常打开和读取,后续再进行刷盘操作。由此可以看到,ES并不是写入文档后马上就可以搜索到,而是一个近实时的搜索(默认1s后)
不完整提交(因为没有刷盘)
refresh资源消耗相对较小,避免每次文档写入fsync导致资源上的瓶颈
默认每1s进行一次refresh,refresh后的段可以被打开,实现近实时搜索
flushES的reresh调用的事Lucene的flush,ES的flush调用的Lucene的commit
即使通过每秒refresh实现了近实时搜索,但refresh无法保障数据安全,我们仍然需要经常进行完整提交来确保能从失败中恢复。flush就是一次完全提交的过程,一次完整的提交会将段刷到磁盘,并写入一个包含所有段列表的提交点。Elasticsearch 在启动或重新打开一个索引的过程中使用这个提交点来判断哪些段隶属于当前分片,保证数据的安全。
为此ES增加了一个 translog ,或者叫事务日志,在每一次对 ES的变更操作除写入内存缓存外还会写入到translog中,translog周期性刷盘,保证变更的持久性。
将translog中所有的段进行全量提交并对translog进行截断的操作叫做flush,flush操作期间会做的事项主要有:
强制refresh,将内存缓冲区所有文档写入一个新的段,写入到文件系统缓存并将旧的内存缓冲区被清空(refresh)将最新的commit point写入磁盘将文件系统缓存中的段通过fsync进行刷盘删除老的translog,启动新translog 什么是translog
translog就是ES的一个事务日志,当发生一个文档变更操作时,文档不仅会写入到内存缓存区也会同样记录到事务日志中,事务日志保证还没有被刷到磁盘的操作的进行持久化。translog持久化后保证即使意外断电或者ES程序重启,ES首先通过磁盘中最后一次提交点恢复已经落盘的段,然后将该提交点之后的变更操作通过translog进行重放,重构内存中的segment。
translog也可以被用来实时CRUD搜索,当我们通过_id进行查询/更新/删除文档时,ES在检索该文档对应的segment时会优先检查translog中最近一次的变更操作,以便获取到最新版本的文档记录。
translog基本流程一个文档被索引之后,就会被添加到内存缓冲区,并且追加到了translog默认每秒refresh一次,refresh会清空内存缓存,但是不会清空translogrefresh操作不断发生,更多的文档被添加到内存缓冲区和追加到translogtranslog周期性通过fsync进行刷盘,默认5s,可通过参数index.translog.sync_interval、index.translog.durability控制,保证应用重启后先确认最后记录的commit point,commit point之后的变更操作通过落盘的translog进行重构恢复段默认当translog太大(512MB)时,进行flush操作 merge
每次refresh操作都会生成一个新的segment,随着时间的增长segmengt会越来越多,这就出现一个比较严重的问题是每次search操作必须依次扫描所有的segment,导致查询效率变慢,为了避免该问题es会定期多这个segment进行合并操作。
什么是merge将refresh产生的多个小segment整合为一个大的segment的操作就叫做merge。同时merge操作会将已经打.del标签的文档从文件系统进行物理删除。merge属于一个后台操作。
在es中每个delete操作其实都是对将要删除的文档打一个.del的标签,同理update操作就是将原文档进行.del打标然后插入新文档,只有merge操作才会将这些已经打标的.del文件真正进行物理删除。
一个大segment的merge操作是很消耗CPU、IO资源的,如果使用不当会影响到本身的serach查询性能。es默认会控制merge进程的资源占用以保证merge期间search具有足够资源。
merge操作相关流程refresh操作会相应的产生很多小的segment文件,并刷入到文件系统缓存(此时文件系统中既有已经完全commit的segment也有不完全提交仅searchable的segment)es可以对这些零散的小segment文件进行合并(包含完全提交以及searchalbe的segment)es会对merge操作后的segment进行一次flush操作,更新磁盘commit point
将merge之后的segment打开保证searchalbe,然后删除merge之前的零散的小segment
集群内部原理
集群节点角色
1. 主节点
主节点负责集群层面的相关操作,管理集群变更。通过配置node.master:true(默认)是节点具备选举为master的资格,主节点事全局唯一的,将从有资格成为master的节点中进行选举。为了避免网络分区出现多主的情况,配置discovery.zen.mininum_master_nodes原则是主节点数过半加一。
2. 数据节点
负责保存数据、执行数据相关操作,配置node.data:true(默认)来使一个节点成为数据节点。
3.协调节点
协调节点将请求转发给保存数据的数据节点。每个数据节点在本地执行请求,并将结果返回协调节点。协调节点收集完数据后,将每个数据节点的结果合并成为单个全局结果,对结果收集和排序的过程可能需要CPU和内存资源。 集群状态
从数据完整性的角度划分,集群状态分为三种:
Green,所有的主分片和副分片都正常运行。Yellow,所有的主分片都运行正常,但不是所有的副分片都正常运行。这意味着存在单点故障的风险。Red,有主分片没能正常运行。
每个索引也有上述的三种状态,假设丢失一个副分片,该分片所属的索引和整个集群变为Yellow状态,其他索引认为Green。
集群启动流程 选举主节点ES的选主算法事基于Bully算法的改进,主要思路是对节点ID排序,取ID最小的节点作为Master,每个节点都运行这个流程。但是会出现选举出的主节点可能不是最新的元数据信息,实际上被分解为两步:先确定唯一的、大家公认的主节点,再想办法把最新的机器元数据复制到选举出的主节点上。
内置的实现称为Zen Discovery,基于节点ID排序的简单选举算法有三个附加约定条件:
参选人数需要过半,达到quorum(多数)后选出了临时的主,为什么是临时的?每个节点运行排序取最小值的算法,结果不一定相同。举个例子,集群中有5台主机,节点ID分别是1、2、3、4、5。当产生网络分区或节点启动速度差异较大时,节点4看到的节点列表是1、2、3、4,选出的节点是2;节点5看到的是2、3、4,选出的是2,结果就不一致了。由此加了第二条限制得票数过半,某节点被选为主节点,必须判断加入它的节点数过半,才确认Master身份,解决第一个问题。当探测节点离开事件时,必须判断当前节点数是否过半。如果达不到quorum,则放弃Master身份,重新加入集群。如果不这么做,则设想一下情况:假设5台机器组成的集群产生网络分区,2台一组,3台一组,产生分区前,Master位于2台其中的一个,此时3台中一组的节点会重新选取Master,产生双主,俗称脑裂。
discovery.zen.minimum_master_nodes: 最小主节点数,这是防止脑裂、防止数据丢失的及其重要的参数,这个参数的实际作用早已超越了其表面的含义。除了在选主时用于决定"多数",还用于多处重要的判断,至少包含以下时机:
触发选主 进入选主的流程之前 ,参选的节点数需要达到法定人数决定Master 选出临时的Master之后,这个临时的Master需要判断加入他的节点达到法定人数,才确认选主成功。gateway选举元信息 向有Master资格的节点发起请求,获取元数据,获取的响应数量必须达到法定人数,也就是参与元信息选举的节点数。Master发布集群状态 发布成功数量为多数 投票与得票的实现
在ES中,投票就是发送加入集群请求,得票就是申请加入该节点的请求数量
确立Master或加入集群临时节点Master是本节点:
- 等待足够多的具备Master资格的节点加入本节点(投票数达到法定人数),已完成选举。超时(默认为30s,可配置)后还没有满足数量的join请求,则选举失败,需要进行重新选举。成功后发布新的clusterStatus
如果其他节点被选为Master
不再接受其他节点加入请求。向Master发送加入请求,并等待回复,超时时间默认为1分钟,如果遇到异常,则默认重试三次 节点失效检测
节点失效检测会监控节点是否离线,然后处理其中的异常。失效检测是选主流程之后不可获取的步骤,不执行失效检测可能会产生脑裂,在此我们需要启动两种失效探测器:
在Master节点,启动NodesFaultDetection,简称NodesFD,定期探测加入集群的节点是否活跃,检查一下当时集群总节点数是否到达法定节点数(过半),如果不足,则放弃Master身份,重新加入集群在非Master节点启动MasterFaultDetection,简称MasterFD,定期探测Master节点是否活跃,如果检测失败,则重新发起选主流程。 选举集群元信息
被选举的Master和集群元信息的新旧程度没有关系,因此它的第一个任务时选举元信息,让各个节点把各自的元信息发过来,根据版本号确定最新的元信息,然后把这个信息广播下去,这样集群的所有节点都有了最新的元信息。
集群元信息的选举包括两个级别:集群级和索引级。不包含哪个shard存在哪个节点这种信息。这种信息以节点存储为准,需要上报,为什么呢?因为读写流程是不经过Master的,Master不知道各shard副本之间的数据差异。
为了集群一致性,参与选举的元信息需要过半,Master发布集群状态成功的规则也是等待发布成功的节点数据过半。
集群元信息选举完毕后,Master发布首次集群状态,然后开始选举shard级元信息。
allocation过程选举shard级元信息,构建内容路由表,是在allocation模块完成的。在初试阶段,所有的shard都处于unassigned(未分配)状态。ES中通过分配过程决定哪个分片位于哪个节点,构建内容路由表。
1. 选主分片
给每个shard都设置一个UUID,然后在集群级的元信息中记录哪个shard是最新的,因为ES是先写主分片,在由主分片节点转发请求去写副分片,所以主分片节点肯定是最新的,主分片选举是通过集群级元信息中记录了“最新主分片的列表“来确定主分片的;所以,集群重启状态为Yellow,而非Red。
2. 选副分片
主分片选举完成后,从上一个过程中汇总的shard信息中选择一个副本作为副分片。如果汇总信息中不存在,则分配一个全新副本的操作依赖于延迟配置项:
index.unassigned.node_left.delayed_timeout
分配分片成功后进入recovery流程,主分片的recovery不会等待某副本分片成功才开始recovery。它们是独立的流程,只是副本分片的recovery需要主分片恢复完毕才开始。
为什么需要recovery?对于主分片来说,可能有一些数据还没有来得及刷盘;对于副分片来说,一是没刷盘,二是主分片写完了,副分片还没有来得及写,主副分片数据不一致。
1. 主分片recovery
由于每次写操作都会记录事务日志(translog),事务日志中记录了哪种操作,以及相关的数据,因此将最后一次提及(Lucene的一次提交就是fsync刷盘的过程)之后的translog中进行重放,建立Lucene索引,如此完成主分片的recovery。
2. 副分片recovery
副分片需要恢复与主分片一致,同时,恢复期间允许新的索引操作,在目前6.0版本中,恢复分成两阶段执行。
phase1:在主分片所在节点,获取translog保留锁,调用Lucene接口把shard做快照,这是已经刷磁盘中的分片数据。把这些shard数据复制到副本节点。
phase1: 对translog做快照,这个快照从phase1开始,到执行translog快照期间的新增索引,将这些translog发送到副本分片所在节点进行重放。
第一阶段尤其漫长,因为它需要从主分片拉取全量的数据。在ES6.x中,对第一阶段做了优化,标记每个操作,每次写入成功的操作都会分配一个序号,通过对比序列号就可以计算出差异范围,在实现方式上,添加了global checkpoint和local checkpoint,主分片负责维护global checkpoint,代表所有分片已经这个序号的位置,local checkpoint代表当前分片已写入成功的最新位置,恢复时通过对比两个序列号,计算出缺失的数据范围,然后通过translog重放这部分数据。



