ElasticSearch核心之——分布式特性
ES支持集群模式,是一个分布式系统,其好处主要有两个∶
es集群由多个ES 实例组成。不同集群通过集群名字来区分,可通过 cluster.name 进行修改,默认为elasticsearch。每个ES实例本质上是一个 JVM 进程,且有自己的名字,通过 node.name 进行修改
ES集群相关的数据称为 cluster state ,主要记录如下信息∶节点信息,比如节点名称、连接地址等;索引信息,比如索引名称、配置等
可以修改 cluster state 的节点称为master节点,一个集群只能有一个 cluster state 存储在每个节点上,master维护最新版本并同步给其他节点
master节点是通过集群中所有节点选举产生的,可以被选举的节点称为 master-eligible 节点 ,相关配置如下: node.master: true
处理请求的节点即为coordinating节点,该节点为所有节点的默认角色,不能取消。路由请求到正确的节点处理,比如创建索引的请求到master节点
存储数据的节点即为data节点,默认节点都是data类型,相关配置如下∶ node.data: true
谈及副本和分片两个概念之前,我们先说一下这两个概念存在的意义: 解决系统可用性和增大系统容量
我们想象这样一个场景,我们的数据只存放在一台ES服务器上,那么一旦这台ES出现宕机或者其他不可控因素影响的话,我们除了丧失了服务的可用性外,可能还存在着数据丢失的可能。同时,单机服务的存储容量也无法应对项目对大数据量的要求。
系统可用性可以分为 服务可用性 和 数据可用性
服务可用性 含义为:当前服务挂掉后,是否有其他服务器顶替当前节点提供服务支持。
数据可用性 含义为:当前服务挂掉后,存储在当前服务器上的数据,是否还可以对外提供访问和修改的服务。
副本可以理解为是某个数据的复制体,副本和源数据内容一致。副本的存在可以有效地满足系统可用性的需求,比如说,我们可以在原有节点的基础上复制一个和源节点一模一样的节点,这样一旦原有节点挂掉了,另外一个节点也还是可以替代源节点提供服务,而且复制出来的节点拥有和源节点一样的数据,这样也保障了数据可用性。
我们在上一小节讲到可以使用副本来解决系统可用性的问题,但是这里存在一个问题,不管存在多少个副本(节点),都无法增大源节点的存储空间。在这个问题上,ES引入了Shard分片这个概念来解决问题。
看完分片的特点后可能还有人不太清楚到底什么是分片,其实分片是n/1个源节点数据。比如说原ES集群中只有一个主节点,所有的索引数据都存储在这个节点上。现在我们将某个索引数据分成3份,分别存放在3个ES节点上,那么每台ES服务器上就各自有1个分片shard。该索引的所有节点Shard分片的集合,就是索引的全部数据。
下面我们来演示一下:
为了更好的了解ES的分片机制,大家不妨在上面的案例上进一步思考两个问题:
答案是不能。原因是我们创建索引时定义的分片数量只有3个,且都已经落在了3个节点上。所以即使再增加多一个节点,也不会有对应的Shard分片可以落在新的节点上,并不能扩大 test_shard_index 的数据容量。
答案是不能。因为新增的副本也是分布在这3个节点上,还是利用了同样的资源。如果要增加吞吐量,还需要新增节点。
通过上面两个问题,相信大家已经可以认识到分片的重要性,分片数过小,会导致后续无法通过增加节点实现水平扩容;(副本)分片数过大会导致一个节点上分布过多分片,造成资源浪费,同时会影响查询性能
集群健康状况,包括以下三种: green健康状态,指所有主副分片都正常分配; yellow指所有主分片都正常分配,但是有副本分片未正常分配; red表示有主分片未分配
我们可以通过这个api查看集群的状态信息: GET _cluster/health
我们也可以通过cerebro或者head插件来直接获取当前集群的状态
需要注意的是,即使当前集群的状态为 red ,也并不代表当前的ES丧失了提供服务的能力。只是说未被分配主分片的索引无法正常存储和操作而已。
这里故障转移的意思是,当ES集群出现某个或者多个节点宕机的情况,ES实现服务可用性的应对策略。
这里我们新建一个分片为3,副本为1的索引,分片分别分布在三个节点,此时集群为 green
当master节点所在机器宕机导致服务终止,此时集群会如何处理呢?
我们可以看到,从node1主节点宕机到ES恢复集群可用性的过程中,ES有着自己的故障转移机制,保障了集群的高可用性。我们也可以在自己的本地上去进行试验,建好索引后,kill掉主节点,观察集群状态就行。
同时,此时就算node2宕机了,那么node3也能够很快的恢复服务的提供能力。
我们知道,我们创建的文档最终会存储在分片上,那么在分布式集群的基础上,ES集群是怎么判断当前该文档最终应该落在哪一个分片上呢?
很显然,我们需要一个可以实现文档均匀分布到各个分片上的映射算法,那么常见的随机算法和round-robin(轮询)算法可以满足需要吗?答案是不可以,这两个算法虽然可以实现文档均匀分布分片的存储需要,但是当我们通过 DocumentId 查询文档时,ES并不能知道这个文档ID到底存储在了哪个节点的分片上,所以只能够从所有分片上检索,时间长。如果我们为这个问题建立一个文档和分片映射关系的表,虽然确实可以快速定位到文档对应的存储分片,但是当文档的数据量很大的时候,那么检索的效率也会随之变低。
对于上面这个问题,ES提供的解决方法是 建立文档到分片的映射算法
es 通过如下的公式计算文档对应的分片:
hash算法 保证可以将数据均匀地分散在分片中
routing 是一个关键参数,默认是文档id,也可以自行指定
number_of_primary_shards 是主分片数
我们可以看到,该算法与主分片数相关, 这也是分片数一旦确定后便不能更改的原因
我们已经知道了ES是如何将文档映射到分片上去了,下面我们就来详细讲解一下文档创建、读取的流程。
脑裂问题,英文为 split-brain ,是分布式系统中的经典网络问题,如下图所示:
3个节点组成的集群,突然node1的网络和其他两个节点中断
解决方案为 仅在可选举master-eligible节点数大于等于quorum时才可以进行master选举
在讲文档搜索实时性之前,先讲一下倒排索引的不可变更特性。由于倒排索引一旦生成,不可变更的特定,使得其有着以下3点好处:
下面,将针对Lucene实现文档实时性搜索的几个动作进行讲解,分析其是如何在新增文档后实现ES的搜索实时性的。
我们从上面的描述中知道,当我们新增了一个文档后会新增一个倒排索引文件 segment ,但是 segment 写入磁盘的时间依然比较耗时(难以实现实时性),所以ES借助文件系统缓存的特性, 先将 segment 在缓存中创建并开放查询来进一步提升实时性 ,该过程在es中被称为refresh。
在refresh之前文档会先存储在一个buffer中,refresh时将 buffer中的所有文档清空并生成 segment
es默认每1秒执行一次refresh,因此文档的实时性被提高到1秒 ,这也是es被称为近实时(Near Real Time)的原因
reflush虽然通过 将文档存放在缓存中 的方式实现了秒级别的实时性,但是如果在内存中的segment还没有写入磁盘前发生了宕机,那么其中的文档就无法恢复了,如何解决这个问题呢?
ES 引入 translog 机制。写入文档到 buffer 时,同时将该操作写入 translog 中。
translog文件会即时写入磁盘(fsync),在ES 6.x中,默认每个请求都会落盘,我们也可以修改为每5秒写一次,这样风险便是丢失5秒内的数据,相关配置为index.translog.*。同时ES每次启动时会检查translog 文件,并从中恢复数据。
flush 负责将内存中的segment写入磁盘,主要做如下的工作:
Reflush和Flush执行的时机
ES的做法是 首先删除文档,然后再创建新文档
我们上面提到,新增文档是通过新建segment来解决,删除文档是通过维护.del文件来进行的,假如现在我们设置的 reflush 时间间隔为1秒,那么一小时单个ES索引就会生成3600个segment,一天下来乃至一个月下来会产生的segment文件数量更是不可想象。为了解决Segment过多可能引起的性能下降问题,ES中采用了Segment Merging(即segment合并)的方法来减少segment的数量。
执行合并操作的方式有两种,一种是ES定时在后台进行 Segment Merging 操作,还有一种是我们手动执行 force_merge_api 命令来实现合并操作。
2024-12-11 广告