Elasticsearch学习系列七(Es分布式集群)
核心概念
集群(Cluster)
一个Es集群由多个节点(Node)组成,每个集群都有一个共同的集群名称作为标识
节点(Node)
一个Es实例就是一个Node。Es的配置文件中可以通过node.master、node.data来设置节点类型。
Es的节点有如下几个类型:
- 主节点
master节点在每个集群中有且只有一个。master节点应该只承担轻量级的任务:如创建删除索引、分片均衡等
# 设置为true
node.master: true
# 尽量设置主节点不为数据节点,提⽰效率
node.data: false
- 候选节点
和主节点的配置相同,可以在选举时被选举为主节点
- 数据节点
这样的节点主要负责数据的存储和查询
# 是否为候选节点
node.master: false
# 是否为数据节点
node.data: true
- 协调节点(coordinating)
每⼀个节点都隐式的是⼀个协调节点,协调节点既不当候选节点,也不作为数据节点,仅负责负载均衡,进⾏投票
# 是否为候选节点
node.master: false
# 是否为数据节点
node.data: false
- 仅投票节点(voting)
设置为仅投票节点后即使配置了data.master=true,也不会参选,但是仍然可以作为数据节点
# 是否为投票节点
Node.voting_only = true
- 默认
Es节点默认配置既是候选节点又是数据节点,这样节点一旦被选举为master,压力还是比较大的。
分片
当有大量的文档时,由于内存的限制、磁盘处理能力不足、无法足够快的响应客户端的请求等,一个节点可能不够。这种情况下,数据可以分为较小的分片。每个分片放到不同的服务器上。当你查询的索引分布在多个分片上时,ES会把查询发送给每个相关的分片,并将结果组合在一起,而应用程序并不知道分片的存在。一个索引会有一个或多个分片。
副本
为提高查询吞吐量或实现高可用性,可以使用分片副本。副本是一个分片的精确复制,每个分片可以有零个或多个副本。ES中可以有许多相同的分片,其中之一被选择更改索引操作,这种特殊的分片称为主分片。当主分片丢失时,集群将副本提升为新的主分片。
分片和副本的区别:
当你分片设置为5,数据量为30G时,es会自动帮我们把数据均衡地分配到5个分片上,即每个分片大概有6G数据,当你查询数据时,ES会把查询发送给每个相关的分片,并将结果组合在一起。
而副本,就是对分布在5个分片的数据进行复制。因为分片是把数据进行分割而已,数据依然只有一份,这样的目的是保障查询的高效性,副本则是多复制几份分片的数据,这样的目的是保障数据的高可靠性,防止数据丢失。
Es分布式架构
特性:
- 高扩展性:体现在Elasticsearch添加节点非常简单,新节点无需做复杂的配置,只要配置好集群信息将会被集群自动发现
- 高可用性:因为Elasticsearch是分布式的,每个节点都会有备份,所以宕机一两个节点也不会出现问题,集群会通过备份进行自动复盘
- 实时性:使用倒排索引来建立存储结构,搜索时常在百毫秒内就可完成
分层结构:
第一层:Gateway
Es支持的索引快照的存储格式,es默认是先把索引存放在内存中,当内存满了之后再持久化到本地磁盘。gateway对索引快照进行存储,当es关闭再启动的时候,它就会从这个gateway里面读取索引数据;支持的格式有:本地的Local FileSystem、分布式的Shard FileSystem、Hadoop的文件系统HDFS、Amazon的S3.
第二层:Lucene框架
第三层:Es数据的加工处理方式
Index Module(创建Index模块)、Search Module(搜索模块)、Mapping(映射)、River代表es的一个数据源(运行在Elasticsearch集群内部的一个插件,主要用来从外部获取获取异构数据,然后在Elasticsearch里创建索引;常见的插件有RabbitMQ River、Twitter River)
第四层:Es发现机制、脚本
Discovery 是Elasticsearch自动发现节点的机制的模块,Zen Discovery和 EC2 discovery。
EC2:亚马逊弹性计算云 EC2discovery主要在亚马云平台中使用。
Zen:相当于solrcloud中的zookeeper。zen Discovery 从功能上可以分为两部分,第一部分是集群刚启动时的选主,或者是新加
入集群的节点发现当前集群的Master。第二部分是选主完成后,Master 和 Folower 的相互探活。
Scripting 是脚本执行功能,有这个功能能很方便对查询出来的数据进行加工处理
3rd Plugins 表示Elasticsearch支持安装很多第三方的插件,例如elasticsearch-ik分词插件、elasticsearch-sql sql插件
第五层:Es的交互方式
有Thrift、Memcached、Http三种协议,默认的是用Http协议传输
第六层:Es的API支持模式
RESTFul Style API风格的API接口标准是当下十分流行的。Elasticsearch作为分布式集群,客户端到服务端,节点与节点间通信有TCP和Http通信协议,底层实现为Netty框架
集群规划
- 我们需要多个规模的集群
计算的依据:Es JVM heap最大可以设置32G,30G heap大概能处理10T,如果内存很大的机器,可以在一台机器上运行多个ES节点。
两类应用场景:
-
用于构建业务搜索功能模块,且多是垂直领域的搜索。数据量几千万到十亿级别。一般2-4台机器的规模
-
用于大规模数据的实时联机处理分析,如ELK等,数据规模可能达千亿或更多。一般几十到上百节点的规模
- 如何避免脑裂
6.x和之前版本 尽量避免脑裂,需要添加最小数量的主节点配置:
discovery.zen.minimum_master_nodes: (有master资格节点数/2) + 1
这个参数控制的是,选举主节点时需要看到最少多少个具有master资格的活节点,才能进行选举。官方推荐的值是(N/2)+1,其中N是具有master资格的节点的数量。
在新版7.X的ES中,对es的集群发现系统做了调整,不再有discovery.zen.minimum_master_nodes这个控制集群脑裂的配置,转而由集群自主控制,并且新版在启动一个新的集群的时候需要有cluster.initial_master_nodes初始化集群列表。
常用做法(中大规模集群):
- Master 和 dataNode 角色分开,配置奇数个master
- discovery.zen.ping.multicast.enabled: false —— 关闭多播发现机制,默认是关闭的
- 延长ping master的等待时长
discovery.zen.ping_timeout:30(默认值是3秒)——其他节点ping主节点多久时间没有响应就认为主节点不可用了。
es7中换成了 discovery.request_peers_timeout
- 索引应该设置多少个分片
分片数指定后不可变,除非重建索引。
参考原则:
ElasticSearch推荐的最大JVM堆空间是30~32G,所以把你的分片最大容量限制为30GB, 然后再对分片数量做合理估算. 例如: 你认为你的数据能达到200GB, 推荐你最多分配7到8个分片。
在开始阶段, 一个好的方案是根据你的节点数量按照1.5~3倍的原则来创建分片. 例如,如果你有3个节点,则推荐你创建的分片数最多不超过9(3x3)个。当性能下降时,增加节点,ES会平衡分片的放置。
对于基于日期的索引需求, 并且对索引数据的搜索场景非常少. 也许这些索引量将达到成百上千, 但每个索引的数据量只有1GB甚至更小.对于这种类似场景,建议只需要为索引分配1个分片。如日志管理就是一个日期的索引需求,日期索引会很多,但每个索引存放的日志数据量就很少。
- 分片应该设置几个副本
为了保证高可用,副本数设置为2即可。要求集群至少要有3个节点,来分开存放主分片、副本。如发现并发量大时,查询性能会下降,可增加副本数,来提升并发查询能力。
注意:新增副本时主节点会自动协调,然后拷贝数据到新增的副本节点,副本数是可以随时调整的。
分布式集群调优策略
写调优
- 首次导入,副本数设为0
如果是集群首次灌入数据,可以将副本数设置为0,写入完毕再调整回去,这样副本分片只需要拷贝,节省了索引过程。
- 自动生成doc id
如果写入doc时指定了id,则Es会先尝试读取原来doc的版本号,以判断是否需要更新。这会涉及一次读取磁盘的操作,通过自动生成doc id就可以避免这个环节。
- 合理设置mappings
- 将不需要建立索引的字段index属性设置为not_analyzed或no。对字段不分词,或者不索引,可以减少很多运算操作,降低CPU占用。尤其是binary类型,默认情况下占用CPU非常高,而这种类型进行分词通常没有什么意义。
- 减少字段内容长度,如果原始数据的大段内容无须全部建立索引,则可以尽量减少不必要的内容。
- 使用不同的分析器(analyzer),不同的分析器在索引过程中运算复杂度也有较大的差异。
- 调整_source字段
source字段用于存储doc原始数据,对于部分不需要存储的字段,可以通过includes或excludes过滤
- 对analyzed的字段禁用norms
Norms用于搜索时计算评分,如果不需要评分的场景,可以禁用掉
"title": {
"type": "string",
"norms": {
"enabled": false
}
- 调整索引的刷新间隔
该参数默认为1s,强制ES每秒创建一个新的segment,从而保证新写入的数据近实时的可见,可被搜索到。比如该参数被调整为30S,降低了刷新操作的频率,则可省一些系统资源
PUT /my_index/_settings
{
"index" : {
"refresh_interval": "30s"
}
- 批处理
批处理就是把多个index操作请求合并到一个batch中去处理
- Document的路由处理
当对一批中的documents进行index操作时,该批index操作所需要的线程的个数由要写入的目的分片数决定。
上图中,有2批documents写入ES,每批都需要写入4个shard,所以总共需要8个线程。如果能减少shard的个数,那么耗费的线程个数也会减少。例如下图,两批中每批的shard个数都只有2个,总共线程消耗个数4个,减少一半。
默认的routing就是id,也可以在发送请求的时候,手动指定一个routing value,比如说put/index/doc/id?routing=user_id
值得注意的是线程数虽然降低了,但是单批的处理耗时可能增加了。和提高刷新间隔方法类似,这有可能会延长数据的实时性
读优化
- 数据分组
很多人拿Es存储日志,日志的索引管理方式一般是基于日期的,如天、周、年。
当搜索单天的数据,只需要查询一个索引的shards就可以。当需要查询多天的数据时,需要查询多个索引的shards。这种方案其实和数据库的分表、分库、分区查询方案思路相似
- 使用Filter代替Query
搜索时使用Query,需要为Document的相关度打分。使用Filter,没有打分环节,所以理论上更快。
- ID字段定义为keyword
一般情况,如果ID字段不会被用作Range类型的搜索字段,都可以定义为keyword类型。这是因为keyword会被优化,以便进行terms查询。Integers等数字类的mapping类型,会被优化来进行range类型搜索。将integers改成keyword类型之后,搜索性能大约能提升30%
- 别让用户的无约束输入拖累Es性能
避免用户输入很多OR语句或者通配符*开头的这种语句。