分布式特性
- es支持集群模式,是一个分布式系统,其好处主要有两个:
- 增大系统容量,如内存、硬盘,使得es集群可以支持PB级的数据。
- 提高系统的可用性,即使部分节点停止服务,整个集群仍然可以正常服务。
- es集群由多个es实例组成
- 不同集群通过集群名称进行区分,可以通过cluster.name进行修改,默认名称为elasticsearch。
- 每个es实例本质上是一个JVM进程,且有自己的名字,通过node.name进行修改。
cerebro安装与运行
通过cerebro可以可视化的监控elasticsearch集群的状态。
- 下载地址
https://github.com/lmenezes/cerebro
下载https://github.com/lmenezes/cerebro/releases/tag/v0.7.1
- 解压安装包并运行
tar zxvf cerebro-0.7.1.tgz
cd cerebro-0.7.1
cd bin
./cerebro
jdk9+安装不成功可参考:
es集群的构建
- es的安装与下载
可参见:
- 启动一个节点
./elasticsearch -Ecluster.name=my_cluster -Epath.data=my_cluster_node1 -Enode.name=node1 -Ehttp.port=5200 -d
- cluster state
- es集群相关的数据被称为cluster state,主要记录如下信息
- 节点信息,比如节点名称、链接地址等。
- 索引信息,比如索引名称配置等。
...
- master node
- 可以修改cluster state的节点称为master节点,一个集群只能有一个
- cluster state存储在每一个节点上,master节点维护最新版本并同步给其他节点。
- master节点是通过集群中所有节点选举产生的,可以被选举的节点称为master-eligible节点,相关配置如下:
node.master:true默认就是true。是可以被选举为master的节点。
- 创建一个索引
- 我们通过如下API创建一个索引
- PUT test_index
- Coordinating Node
- 处理请求的节点被称为coordinating节点,该节点为所有节点的默认角色,不能取消
- 路由请求到正确的节点进行处理,比如创建索引的请求到master节点。
- Data Node
- 存储数据的节点Data Node,默认节点都是data类型,相关配置如下:
- data.node:true
- 新增一个节点
- 新增node2节点,命令如下:
./elasticsearch -Ecluster.name=my_cluster -Epath.data=my_cluster_node2 -Enode.name=node2 -Ehttp.port=5300 -d
提高系统可用性-副本与分片
- 服务可用性
- 两个节点的情况下,允许其中一个节点停止服务。
- 数据可用性
- 引入副本(Replication)解决。
- 每个节点上都有完备的数据。
- 增大系统容量
- 如何将数据分布到所有节点上?
- 引入分片(shard)解决问题。
- 分片是es支持PB级数据的基石。
- 分片存储了部分数据,可以分布于任意节点上。
- 分片数在索引创建时指定,且后续不允许在修改,默认为5个。
- 分片有主分片和副本分片之分,以实现数据的高可用。
- 副本分片的数据由主分片同步,可以有多个,从而提高读取的吞吐量。
- 两个问题
- 此时增加节点是否能提高test_index的容量?
- 答案是不能,因为只有3个分片,已经分布在3个节点上,新增节点无法利用。
- 此时增加副本数是否能提高test_index的读取吞吐量?
- 答案是不能,因为新增的副本也是分步在当前的3个节点上,还是利用了同样的资源。如果要增加吞吐量,还需要新增节点。
- 分片设定很重要,需要提前规划好
- 过小会导致后续无法通过增加节点来提高查询的吞吐量。
- 过大会导致一个节点上的分片过多,此时也就影响查询的性能。
集群状态cluster health
- 通过_cluster/health API可以查看集群的健康状态,包括以下三种:
- green 健康状态,指所有主副分片都分配正常。
- yellow所有主分片都正常,但是副本分片未分配正常。
- red 有主分片未分配。
http://localhost:5200/_cluster/health
故障转移
- node1所在机器宕机导致服务终止,此时集群会如何处理?
- node2和node3发现node1无法响应一段时间后会发起master选举,比如这里选择node2为master节点。此时由于主分片P0下线,集群状态变为red。
- node2发现主分片P0未分配,此时将R0提升为主分片。此时由于所有主分片都正常分配,集群状态变为yellow。
- node2为P0和P1生成新的副本,集群状态变为绿色。
文档分布式存储
- 文档最终会存储到分片上,如下图所示:
- Document1是如何存储到分片P1上的?选择P1的依据是什么?
- 需要文档到分片的映射算法。
- 目的
- 使得文档均匀的分布在所有的分片上,以充分利用资源。
- 文档到分片的映射算法
- 随机选择或round-robin算法?
- 不可取,因为需要维护文档到分片的映射关系,成本巨大。
- 根据文档值时时的计算对应的分片
- shard = hash(routing)%number_of_primary_shards。
- hash算法保证可以将数据均匀地分散在分片中。
- routing是一个关键参数,默认是文档id,也可以自行指定。
- number_of_primary_shards主分片数
- 该算法与主分片数相关,这也是分片数一旦确定后便不能修改的原因。
- 文档创建的流程
- 文档读取的流程
- 文档批量创建流程
- 文档批量读取流程
脑裂问题
- 脑裂问题,英文为split-brain,是分布式系统中的经典网络问题,如下图所示:
- node2和node3会重新选举master,比如node2成为新的master,此时会更新cluster state。
- node1自己组成集群后也会更新cluster state。
- 同一个集群有两个master,而且维护不同的cluster state,网络恢复后无法选择正确的master。
- 接解决方案
- 仅在可选举master-eligible节点数大于等于quorum时才可以进行选举。
- 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
- segment写入磁盘的过程依然很耗时,可以借助文件系统缓存的特性,先将segment在缓存中创建并开放查询来进一步提升实时性,该过程在es中被称为refresh。
- 在refresh之前文档会先存储在一个buffer中,refresh时会将buffer中的所有文档清空并生成segment。
- es默认每一秒执行一次refresh,因此文档的实时性被提高到1秒,这也是es被称为近实时(near real time)的原因。
- refresh发生的时机
- 间隔时间达到时,通过index.settings.refresh_interval来设定,默认为1秒。
- index_buffer占满时,其大小通过indices.memery.index_buffer_size设置,默认时JVM heap的10%,所有shard共享。
- flush时也会发生refresh。
- 文档搜索实时性-translog
- 如果内存中的segment还没写入到磁盘前发生宕机,那么其中的文档就无法恢复了。如何解决此问题?
- es引入translog的机制,写入文档到buffer时,同时写入文档到translog。
- translog文件会即时写入到磁盘(fsync),6.x默认每次都会落盘。可以修改为每5秒写入一次,来提高写入性能,这样同时会带来可能会丢失5秒内的数据,相关配置为index.translog.*。
- es启动时会检查translog文件,并从中恢复数据。
- 文档搜索实时性-flush
- 负责将内存中的segment写入磁盘,主要工作如下:
- 将translog写入磁盘。
- 将index buffer清空,其中的文档生成一个新的segment,相当于一个refresh操作。
- 更新commit point并写入磁盘。
- 删除旧的translog文件。
- flush发生的时机
- 间隔时间达到时,默认30分钟,5.x之前的版本可以修改,之后的版本不可修改。
- translog占满时,其大小可以通过index.translog.flush_threshold_size控制,默认时512m,每个index都有自己的translog。
- 删除与更新文档
- segment一旦生成就不能修改,那么你要删除文档要如何操作?
- Lucene专门维护一个.del文件,记录所有已删除的文档,注意.del文档上记录的是文档在lucene内部的id。
- 在查询返回结果前会过滤掉.del文件中的文档。
- 更新文档如何进行
- 首先删除文档,在创建新文档。
- es index 与 lucene index的对照,如下图:
- segment merging
- 随着segment的增多,由于一次查询的segment的增多,查询速度会变慢。
- es会定时后台惊喜segment merging,减少segment数量。
- 通过force_merge api可以手动强行做segment merge的操作。