ES--集群搭建及原理

一、集群搭建

  1、单机搭建请见:https://www.cnblogs.com/liconglong/p/15005229.html

  2、es参数配置

  集群搭建,需要在原有单价搭建的基础上修改es配置文件(vi /home/es/elasticsearch-6.2.4/config/elasticsearch.yml),添加如下配置:

cluster.name: escluster
node.name: es1
node.master: true
node.data: true
path.data: /data/es/data
path.logs: /data/es/logs
bootstrap.memory_lock: true
bootstrap.system_call_filter: false
http.port: 9200
network.host: 0.0.0.0
discovery.zen.minimum_master_nodes: 2
discovery.zen.ping_timeout: 3s
discovery.zen.ping.unicast.hosts: ["192.168.124.14:9300","192.168.124.12:9300","192.168.124.15:9300"]

  参数说明:

    cluster.name:集群名称,同一集群的es服务器要保持一致

    node.name:节点名称,同一集群内的节点名称不能相同

    discovery.zen.minimum_master_nodes:表示集群最少的master数,如果集群的最少master数少于指定的数,将无法启动,官方推荐master数为:集群中节点数/2+1,如果集群中是三个节点,那么最少要配置两台master,整个集群才可以运行

    node.master:该节点是否有资格被选举为master,如果上面master节点数配置为2,那么集群中必须有两个节点的该参数配置为true。但是如果配置了两个master的话,如果有master宕机,则整个集群不可用,如果配置3个master的话,如果一个主节点宕机,集群会重新选举一个mater来代替,整个集群仍然可用,因此我这里配置的三个节点均为master。master服务器主要管理集群状态,负责元数据处理,比如索引增删分片分配等,数据存储和查询都不会走主节点。

    node.data:存储索引数据,三台都设置为true即可

    bootstrap.memory_lock:锁住物理内存,不使用swap内存

    discovery.zen.ping_timeout:自动发现其他节点的超时时间

    discovery.zen.ping.unicast.hosts:集群节点ip

  3、解决内存未锁定的问题

    配置完es集群后,启动es,发生如下错误:memory locking requested for elasticsearch process but memory is not locked

    解决方案:https://www.cnblogs.com/liconglong/p/15026089.html

  4、JVM调优

    解决完上述问题后,重新启动es,直接显示“已杀死”(这里说明一下,我是使用虚拟机搭建的集群,每个虚拟机占用内存为1g)

    修改es的jvm配置文件(/home/es/elasticsearch-6.2.4/config/jvm.options),调整jvm参数,一般设置为物理内存的一半为最佳。

-Xms512m
-Xmx512m

  

二、ES集群原理

(一)节点类型

  在ES集群中,有master节点、数据节点、协调节点三种节点类型。

  1、master节点:

    整个集群中只会有一个master节点,其并不需要涉及到文档级别的变更和搜索等操作,所以放集群只拥有一个master节点的情况下,其不会称为集群的性能瓶颈。master节点需要从众多候选的master节点中选择一个

    它负责管理集群范围内的所有变更,例如

      增删索引

      增删节点

      shard分片的重新分配

      接受集群状态的变化并推送给所有节点(集群中各节点都有一份完整的集群状态信息,都由master node负责维护)

      协调创建索引请求或查询请求,将请求分发到相关的node上。

  2、数据节点

    其负责存储数据,提供建立索引和搜索索引的服务。data节点消耗内存和磁盘IO的性能比较大

  3、协调节点

    其不会被选举为主节点,也不会存储任何索引数据,主要用于查询负载均衡,将查询请求分发到索格node服务器,并对结果进行汇总处理。协调节点尽量不要分离,跟数据节点一起就可以。

  节点的配置在ES的配置文件中,如果要配置节点为mater节点,则node.master为true,如果要配置节点为数据节点,则node.data为true,如果两者均为true,则表示该节点即为mater节点,又为数据节点,每个节点都默认为协调节点。一般情况下,都会设置节点同时为master节点、数据节点和协调节点。

(二)索引分片

  ES的分片与其他分布式的组件分片一样,将数据平分到各个节点上,以支持水平扩展。同时ES还支持索引复制,其是为了解决高可用问题。

  ES的分片分为主分片(primary shard)和复制分片(replica shard),ES中的每一个索引文档都是一个主分片。

(三)集群选举

  ES集群的选举是由master-eligble(master节点)发起,当该节点发现当前节点不是master,并且该节点通过ZenDiscovery模块ping其他模块,得到超过mininum_master_nodes个节点没有连接上master时,其就会发起选举。

  选举时,首选选举ClusterStateVersion最大的Node节点,如果ClusterStateVersion相同,则选举ID最小的Node。

  ClusterStateVersion是集群的状态版本号,每一次集群选举ClusterStateVersion都会更新,因此最大的ClusterStateVersion是与原有集群数据最接近或者是相同的,这样就尽可能的避免了数据丢失。

  Node的ID是在第一次服务启动时随机生成的,直接选用最小ID的Node,主要是为了选举的稳定性,尽量少的出现选举不出来的问题。

(四)ES集群脑裂

  脑裂是一个集群中存在两个以上的master节点,例如原来的master节点与其他所有的node网络出现异常,那么node就会重新发起选举并选举一个新的master,那么此时集群就存在两个master节点。

  避免脑裂:设置iscovery.zen.minimum_master_nodes的值为:node数量/2+1

  为什么这么设置就可以避免脑裂呢?例如3个节点的情况下:因为脑裂主要是由于网络不通导致的,因此master节点和其他节点不在一个网络中,那么原来的master节点因为没有足够的node在该网络中,因此选举失败,不能再作为master,而另外一个区域的两个node,则可以选举出来一个master。

  因为ES是可以动态增加或删除节点的,所以随时都需要去改变mininum_master_node的值,调整请求:

PUT /_cluster/settings { "persistent"{"discovery.zen.minimum_master_nodes" : 2 } }

(五)集群扩展

  集群扩展可以横向扩展和继续扩展。

  横向扩展是指增加服务器,当有新的Node节点加入到集群中时,集群会动态的重新进行分配和负载,例如原来有两个Node节点,每个节点上有3个分片,如果再添加一个node节点到集群中,集群会动态的将6个分片分配到这三个节点上,最终每个节点上有两个分片。

  继续扩展指的是配置的复制分片(number_of_replicas)的大小,如果有3个node,分支分片为2,那么每一个node上都是全量的数据,由于主分片和复制分片都可以处理读请求,所以数据的冗余越多,查询性能就会越高,但是如果超过了全量数据,就会导致查询性能下降,因为这样做的话,每个分片所占用的硬件资源就会变小。

  动态设置副本数量

# 更新副本数量
PUT /blogs/_settings    {"number_of_replicas" : 2}

(六)故障转移

  ES中有两种集群故障探查机制,通过master去ping集群中所有的node是否存活,以及通过node去pingmaster是否存活(不存活需要发起选举)

  配置参数如下所示:

ping_interval : 每隔多长时间会ping一次node,默认是1s
ping_timeout : 每次ping的timeout等待时长是多长时间,默认是30s
ping_retries : 如果一个node被ping多少次都失败了,就会认为node故障,默认是3次

三、ES数据存储

  1、存储流程

         

    首先说几个概念,内存缓冲区memory buffer、事务日志translog、页面高速缓存File System Cache。

    ES的整体存储流程:当一个写请求到达时,先将数据写入内存缓冲区memory buffer,同时将操作写入操作日志中,此时数据在内存中,不能被读取,然后ES将内存缓冲区的内容刷新到页面高速缓存,然后清空内存缓冲区,最后定时将页面高速缓存的数据写入磁盘。

    ES这样设计的原因是,如果每一次直接写操作都是直接将内存缓冲区的数据直接写入磁盘,那么将会非常耗性能,因为写入的数据肯定是离散的,因此写入磁盘的操作也是随机写入,随机写入的性能非常低,因此就将内存缓冲区的数据写入了页面高速缓存,然后定时将页面高速缓存的数据写入磁盘,那么这样就会存在一个问题,如果在数据还未写入磁盘时,服务器发生了宕机等问题,那么数据就会发生丢失,因此就引入了操作日志,每一次写操作都会将操作写入到操作日志,如果发生了服务宕机等情况,还是可以根据操作日志进行数据恢复。

    ES默认将内存缓冲区写入到页面高速缓存的时间间隔为1秒,将页面高速缓存的数据写入磁盘的时间间隔为30分钟,每一次写操作都会写入操作日志中。

    由于每一次写操作都要写一次操作日志,就会影响性能,如果存储的数据不是非常重要,允许丢失部分数据的情况下,可以通过index.translog.durability 和 index.translog.sync_interval这两个参数设置每隔一段时间再向磁盘写入操作日志。

  2、动态更新索引

    总的来说就是信赖的数据写到新的索引文件中。

      Lucene把每一次生成的倒排索引叫做一个segment,每将页面高速缓存的数据刷新到磁盘一次,就会重新生成一个segment。

  3、磁盘写入时机的控制

    在动态更新索引里面说了每隔一秒就会将内存缓冲区的数据写入到页面高速缓冲中,客户端就可以进行搜索了,也就是说,延迟最大为一秒,但是如果还是觉得不可忍受的话,可以主动调用 /_refresh接口进行写入。

    再ES5.0之后还提供了一个请求参数?refresh=wait可以在写入的时候不强制刷新,但是必须等到刷新后再返回。

    但是如果是日志收集的场景,我们并不需要实时读取,但是需要写入速度,因此可以调大刷新的时间区间。

# curl -XPOST http://127.0.0.1:9200/logstash-2015.06.21/_settings -d'{ "refresh_interval": "10s" }'

    如果是导入历史数据的话,可以先将同步停掉,在全部录入完成后,再手动刷新一次即可

# curl -XPUT http://127.0.0.1:9200/logstash-2015.05.01 -d'{"settings" : {"refresh_interval": "-1"}}'
# curl -XPOST http://127.0.0.1:9200/logstash-2015.05.01/_refresh

  4、translog提供的磁盘同步控制

    上面说到translog是用于数据恢复的,其流程就是当写操作写入到内存缓冲区中之后,会将操作日志写入到translog中,translog中有一个commit位置,如果将页面高速缓存的数据刷新到磁盘后,会更新translog的commit位置,如果在刷新到磁盘之前,就发生了宕机,在ES重新启动后,会从translog的commit位置直接恢复数据到页面高速缓存中。

    将页面高速缓存刷新到磁盘中默认时间为30分钟或者当translog文件的大小大于512M时,会主动进行一次刷新,这两个配置可以通过index.translog.flush_threshold_period 和 index.translog.flush_threshold_size 进行设置。除了这个还可以通过index.translog.flush_threshold_ops来设置达到多少条后进行刷新。

  5、translog的一致性

    磁盘上的数据完整性是通过translog来控制的,那么translog的一致性是通过什么来配置的呢?

    默认情况下,系统会每5秒或者在一次请求结束前将translog刷新到磁盘上。

    每次请求都刷新translog到磁盘会影响性能,如果可以允许几秒钟的数据,可以将刷新translog设置为异步操作。

    在index template中设置:

{"index.translog.durability": "async"}

  6、段的合并

    在上面说到,每一次将内存缓冲区刷新到页面高速缓存时,都会重新生成一个segment,但是每个文件都需要文件句柄、内存、CPU的使用,文件打开的多会非常影响性能。

    为了解决问题,ES后台程序会将这些零散的segment做合并,尽量让segment少,并且每个segment都比较大。

    在做segment合并时,还未完成合并的segment不在查找范围内,当新的segment合并完成后,会更新translog中的commit位置。

四、集群路由

  document路由到shard分片上,就叫做文档路由。

  文档路由的算法:shard = hash(routing)%number_of_primary_shards,例如一个索引有三个分片,分片键为id,id的值如果为1,那么值等于hash(1)%3=0,那么就会被路由到第0个node上。

  默认分片键为 _id,也可以设置自定义分片键,设置方法:

PUT /index/item/id?routing = user_id(自定义路由)--自定义分片key

  当客户端发起请求时:

    客户端首先会选择一个node进行请求,这个node就叫做协调节点

    协调节点对文档进行路由,将请求打到被路由到的node上

    实际处理的node对数据进行处理,然后将数据同步到自己的复制节点上

    协调节点确认主分片和复制分片都已经处理完数据,返回完成。

五、实战的几个问题

  我们需要多大的集群规模,需要多少个分片,多少个副本?

  这个问题需要结合项目的实际情况来定,比如当前项目的数据量、后续可能达到的数据量、服务器的配置(CPU、内存、硬盘),但是总体要遵守以下两点:

  1、避免分片过大或过小(一般为20G到40G之间最好)

    如果分片过大,集群从故障恢复的时候会性能会非常低,一般情况下,50G为分片的最大值

    如果分片国小,那么生成的segment就会过小,进而增加服务器开销

  2、每个节点上存储的分片数量与堆内存大小成正比

    这个比例并没有明确的规定,但是经过其他人验证,一般一个G的内存对应分片在20以下即可,也就是说,1G内存可以设置成20个分片。

------------------------------------------------------------------
-----------------------------------------------------------
---------------------------------------------
朦胧的夜 留笔~~
原文地址:https://www.cnblogs.com/liconglong/p/15026140.html