一、es 分布式特性
es 支持集群模式,是一个分布式系统,其好处有两个:
- 增大系统容量,如内存、磁盘,使得 es 集群可以支持 PB 级的数据
- 提高系统可用性,即使部分节点停止服务,整个集群依然可以正常运行
es 集群由多个 es 实例组成
- 不同集群通过集群名字来区分,可通过 cluster.name 进行修改,默认为 elasticsearch
- 每个 es 实例本质是一个 JVM 进程,且有自己的名字,通过 node.name 进行修改
二、集群和节点
启动一个节点
bin/elasticsearch -E cluster.name=my_cluster -E node.name=node1
Cluster State
es 集群相关的数据称为 cluster state,主要记录如下信息:
- 节点信息,比如节点名称、连接地址等
- 索引信息,比如索引名称、配置等
Master Node
- 可以修改 cluster state 的节点成为 master 节点,一个集群只能有一个
- cluster state 存储在每个节点上,master 维护最新版本并同步给其他节点
- master 节点是通过集群中所有节点选举产生的,可以被选举的节点称为 master-eligible 节点,相关配置是: node.master:true
Coordinating Node
处理请求的节点即为 coordinating 节点,该节点为所有节点的默认角色,不能取消。路由请求到正确的节点处理,比如创建索引的请求到 master 节点
Data Node
存储数据的节点即为 data 节点,默认节点都是 data 类型,相关配置:node.data:true
单点问题
如果集群只有一个节点,那么唯一的节点停止服务,集群也将停止服务
新增节点
运行如下命令可以启动一个 es 节点实例:
bin/elasticsearch -E cluster.name=my_cluster -E node.name=node2
提高系统可用性
1)服务可用性
- 2 个节点的情况下,允许其中 1 个节点停止服务
2)数据可用性
- 引入副本(Replication)解决
- 每个节点都有完备的数据
增大系统容量
如何将数据分布于所有节点上?
- 引入分片(Shard)解决问题
分片是 es 支持 PB 级数据的基石
- 分片存储了部分数据,可以分布于任意节点上
- 分片数在索引创建时指定,且后续不允许更改,默认为 5 个
- 分片由主分片和副本分片之分,以实现数据的高可用
- 副本分片的数据由主分片同步,可以有多个,从而提高读取的吞吐量
三、分片
以下演示 3 个节点的集群中 test_index 的分片分布情况,创建时我们指定了 3 个分片和 1 个副本
PUT test_index
{
"setting":{
"number_of_shards":3,
"number_of_replicas":1
}
}
关于上图有两个问题需要注意:
- 此时增加节点是否能提高 test_index 的数据容量?
答案是不能的,因为只有 3 个分片,已经分布在 3 台节点上,新增的节点无法利用
- 此时增加副本数是否能提高 test_index 的读取吞吐量?
答案也是不能的,因为新增的副本也是分布在这三个节点上,如下图所示,还是利用了同样的资源。如果要增加吞吐量,还要新增节点。
分片数的设定很重要,需要提前规划好,过小会导致后续无法通过增加节点实现水平扩容。过大会导致一个节点上分布过多分片,造成资源浪费,同时会影响查询性能
Cluster Health
通过如下 API 可以查看集群健康状况,包括一下三种:
- green:健康状态,指所有主副分片都正常分配
- yellow:指所有主分片,都正常分配,但是有副本分片未正常分配
- red:有主分片未分配
故障转移
如下图集群有 3 个节点组成,此时集群状态是 green
突然 node1(master)所在机器宕机导致服务终止,此时集群会如何处理?
- Node2 和 node3 发现 node1 无法响应一段时间后发起 master 选举,比如这里选择 node2 为 master 节点。此时由于主分片 P0 下线,集群状态转变为 Red。
- node2 发现主分片 P0 未分配,将副本分片 R0 提升为主分片。此时由于所有主分片都正常分配,集群状态变为 yellow。
- node2 为 P0 和 P1 生成新的副本,集群状态恢复为绿色
四、文档分布式存储
文档最终会存储在分片上,如下图所示:
PUT test_index/doc/1
{
"title":"seina gao",
"desc":"nothing here"
}
doc1 最后存储在 P1 分片上
那么 doc1 是如何存储到分片 P1 的?选择 P1 的依据是什么?
需要文档到分片的映射,目的是使得文档均匀分布在所有分片上,以充分利用资源。
1)比如常见的随机选择还是 round-robin 轮训算法呢,显然是不可取的,存储的时候把 doc1 存到 P1 了,读取的时候需要一个地方来维护文档到分片的映射关系,大家都知道 es 是海量数据存储文档,比如有上亿个文档,那么维护成本巨大
2)根据文档值实时计算对应的分片,es 通过如下公式计算文档对应的分片
- shard = hash(routing) % number_of_primary_shards
- hash 算法保证可以将数据均匀地分散在分片中
- routing 是一个关键参数,默认是文档 id,也可以自行指定
- number_of_primary_shards 是主分片数
该算法与主分片数相关,这也是分片数一旦确定之后便不能更改的原因
文档创建的流程
- 用户像 node3 发起创建文档的请求
- node3 通过routing 机算该文档应该存储在 Shard1 上,查询 cluster state 后确认主分片 P1 在 node 2 上,然后转发创建文档的请求到 node2
- P1 接收并执行创建文档请求后,将同样的请求发送到副本分片 R1
- R1 接受并执行创建文档请求后,通知 P1 成功的结果
- P1 接收副本分片结果后,通知 node3 创建成功
- node3 返回结果给用户
文档读取的流程
- 用户向 node3 发起获取文档 1 的请求
- node3 通过 routing 计算该文档在 Shard1 上,查询 cluster state 后获取 Shard1 的主副分片列表,然后以轮询的机制获取一个 Shard,比如这里是 R1,然后转发读取文档的请求到 node1
- R1 接收并执行读取文档请求后,将结果返回 node3
- Node3 返回结果给用户
倒排索引的不可变更
倒排索引一旦生成,不能更改,其好处如下:
- 不用考虑并发写文件的问题,杜绝了锁机制带来的性能问题
- 由于文件不再更改,可以充分利用文件系统缓存,只需载入一次,只要内存足够,对该文件的读取都会从内存读取,性能高
- 利于对文件进行压缩存储,节省磁盘和内存存储空间
坏处是需要写入新文档时,必须重新构建倒排索引文件,然后替换老文件,新文档才能被检索,导致文档实时性差。解决办法就是新文档直接生成新的倒排索引文件,查询的时候同时查询所有的倒排文件,然后做结果的汇总计算即可。