生产环境集群搭建建议
1.ES设置尽量简洁 elasticsearch.yml
中尽量只写必备的参数,其他可以通过api动态设置的参数都通过api来设定
动态设定的参数有transient
和persistent
两种设置,前者在集群重启后会丢失,后者不会,但两种设定都会覆盖elasticsearch.yml中的配置。
PUT /_cluster/settings
{
"persistent": {
"discovery.zen.minimum_master_nodes":2
},
"transient": {
"indices.store.throttle.max_bytes_per_sec":"50mb"
}
}
2.脑裂问题
解决方案:仅在可选举master-eligible
节点数大于等于quorum
时才可以进行master选举
quorum=master-eligible节点数/2 +1,
例如3个master-eligible节点时,quorum为2
设定discovery.zen.minimum_master_nodes为quorum即可避免脑裂
3.关于JVM内存设定
- 不要超过31GB
- 预留一半内存给操作系,用来做文件缓存
具体大小根据该node要存储的数据量来估算,为了保证性能,在内存和数据量间有一个建议的比例:
- 搜索类项目的比例建议在1:16以内
- 日志类项目的比例建议在1:48~1:96
假设总数据量大小为1TB,3个node,1个副本,那么每个node要存储的数据量为2TB/3=666GB,即700GB左右,做20%的预留空间,每个node要存储大约850GB的数据。
- 如果是搜索类项目,每个node内存大小为850GB/16=53GB,大于31GB。31*16=496,即每个node最多存储496GB数据,所以需要至少5个node
- 如果是日志类型项目,每个node内存大小为850GB/48=18GB,因此3个节点足够
写性能优化
目标是增大写吞吐量-EPS
(Event Per Second)越高越好
优化方案:
- 客户端:多线程写,批量写
- ES:在高质量数据建模的前提下,主要是在refresh、translog和flush之前做文章。
ES写数据分为三个过程:
refresh
translog
flush
refresh
segment
写入磁盘的过程很耗时,可以借助文件系统缓存的特性,先将segment在缓存中创建并开放查询来查询来进一步提升实时性,该过程在es中被称为refresh
。
在refresh之前文档会先存储在一个buffer中,refresh时将buffer中的所有文档清空并生成segment。
es默认每1秒执行一次refresh,因此文档的实时性被提高到1秒,这也是es被称为近实时(Near Real Time
)的原因。
Lucene构建的单个倒排索引称为
segment
,合在一起称为Index,与ES中的Index概念不同,ES中的一个shard对应一个Lucene Index。
优化: 目标为降低refresh的频率
- 增大
refresh_interval
,降低实时性,以增大一次refresh处理的文档数,默认是1s,设置为-1直接禁止自动refresh - 增大
index buffer size
,参数为indices.memory.index_buffer_size
(静态参数,需要设定在elasticsearch.yml中)默认为10%。
translog
如果在内存中的segment还没有写入磁盘前发生宕机,那么其中的文档就无法恢复了,如何解决这个问题?
es引入translog
机制。写入文档到buffer时,同时将该操作写入translog。
translog文件会即时写入磁盘(fsync),6.x默认每个请求都会落盘,可以修改为每5秒写一次,这样风险便是丢失5秒内的数据,相关配置为index.translog.*
es启动时会检查translog文件,并从中恢复数据
优化: 目标是降低translog写磁盘频率,从而提高写效率,但会降低容灾能力
index.translog.durability
设置为async,index.translog.sync_interval
设置需要的大小,比如120s,那么translog会改为每120s写一次磁盘index.translog.flush_threshold_size
默认为512mb。即translog超过该大小时会触发一次flush,那么调大该大小可以避免flush的发生
flush
flush
负责将内存中的segment写入磁盘,主要做如下工作:
- 将translog写入磁盘
- 将index buffer清空,其中的文档生成一个新的segment,相当于一个refresh操作
- 更新
commit point
并写入磁盘 - 执行fsync操作,将内存中的segment写入磁盘
- 删除旧的translog文件
Lucene有一个专门的文件来记录所有的segment信息,称为
commit point
优化: 目标为降低flush的次数,在6.x可优化的点不多,多为es自动完成
其他优化
- 副本设置为0,写入完毕再增加
- 合理设计shard数,并保证shard均匀地分配在所有node上,充分利用所有node的资源
index.routing.allocation.total_shards_per_node
限定每个索引在每个node上可分配的总主副分片数
例如:5个node,某索引有10个主分片,1个副本,上述值应该设置为多少?
(10+10)/5=4
实际要设置为5个,防止在某个node下线时,分片迁移失败的问题。
- 主要为index级别的设置优化,以日志场景举例,一般会有如下索引的设定:
读性能优化
读性能主要受以下几方面影响:
- 数据模型是否符合业务模型?
- 数据规模是否过大?
- 索引陪孩子是否优化?
- 查询语句是否优化?
高质量的数据建模是优化的基础:
- 将需要通过script脚本动态计算的值提前算好作为字段存储到文档中
- 尽量使得数据模型贴近业务模型
根据不同的数据规模设定不同的SLA
- 上万条数据与上千万条数据性能肯定存在差异
索引配置调优:
- 根据数据规模设置合理的主分片数,可以通过测试得到最合适的分片数
- 设置合理的副本数目,不是越多越好
查询语句调优:
查询语句调优主要有以下几种常见手段:
- 尽量使用filter上下文,减少算分的场景,由于filter有缓存机制,可以极大提升查询性能
- 尽量不使用script进行字段计算或者算分排序等
- 结合profile、explain API分析慢查询语句的症结所在,然后再去优化数据模型
如何设定shard数?
分片问题?
在一个有三个节点组成的es集群中创建一个有三个分片的test_index索引。
1.此时增加节点是否能够提高test_index的数据容量?
不能。因为只有三个分片,已经分布在3台节点上,新增的节点无法利用。
2.此时增加副本数是否能提高test_index的读取吞吐量?
不能。因为新增的副本也是分布在这三个节点上,还是利用了同样的资源。如果要新增吞吐量,还要新增节点。
- 分片数的设定很重要,需要提前规划好
- 过小会导致后续无法通过增加节点实现水平扩容
- 过大会导致一个节点上分布过多分片,造成资源浪费,同时会影响查询性能
es的性能基本是线性扩展的,因此我们只要测出1个shard的性能指标,然后根据实际性能需求就能算出需要的shard数。比如单shard写入eps是1000,而线上eps需求时5000,那么你需要5个shard。(实际还要考虑副本的情况)
测试一个shard的流程如下:
- 搭建与生产环境相同配置的单节点集群
- 设定一个单分片零副本的索引
- 写入实际生产数据进行测试,获取写性能指标
- 针对数据进行查询请求,获取读性能指标
压测工具可以采用esrally
:参考链接
压测的流程还是比较复杂的,如果是搜索引擎场景,单个shard大小不要超过15GB,如果是日志场景,单shard大小不要超过50GB(shard越大,查询性能越低)
此时只要估算出你索引的总数据大小,然后在除以上面单shard大小也可以得到分片数