ElasticSearch性能调优

栏目: 后端 · 发布时间: 7年前

内容简介:大家好,我是皮蛋二哥。“ELK”是ElasticSearch、Logstash、Kibana三门技术的简称。如今ELK技术栈在互联网行业数据开发领域使用率越来越高,做过数据收集、数据开发、数据存储的同学相信对这个简称并不陌生,而ElasticSearch(以下简称ES)则在ELK栈中占着举足轻重的地位。前一段时间,我亲身参与了一个ES集群的调优,今天把我所了解与用到的调优方法与大家分享,如有错误,请大家包涵与指正。系统层面的调优主要是内存的设定与避免交换内存。

ElasticSearch性能调优

ElasticSearch性能调优

大家好,我是皮蛋二哥。“ELK”是ElasticSearch、Logstash、Kibana三门技术的简称。如今ELK技术栈在互联网行业数据开发领域使用率越来越高,做过数据收集、数据开发、数据存储的同学相信对这个简称并不陌生,而ElasticSearch(以下简称ES)则在ELK栈中占着举足轻重的地位。前一段时间,我亲身参与了一个ES集群的调优,今天把我所了解与用到的调优方法与大家分享,如有错误,请大家包涵与指正。

系统层面的调优

系统层面的调优主要是内存的设定与避免交换内存。

ES安装后默认设置的堆内存是1GB,这很明显是不够的,那么接下来就会有一个问题出现:我们要设置多少内存给ES呢?其实这是要看我们集群节点的内存大小,还取决于我们是否在服务器节点上还是否要部署其他服务。如果内存相对很大,如64G及以上,并且我们不在ES集群上部署其他服务,那么我建议ES内存可以设置为31G-32G,因为这里有一个32G性能瓶颈问题,直白的说就是即使你给了ES集群大于32G的内存,其性能也不一定会更加优良,甚至会不如设置为31G-32G时候的性能。以我调优的集群为例,我所调优的服务器节点内存为64G,服务器节点上也基本不跑其他服务,所以我把ES集群内存大小设置为了31G,以充分发挥集群性能。 设置ES集群内存的时候,还有一点就是确保堆内存最小值(Xms)与最大值(Xmx)的大小是相同的,防止程序在运行时改变堆内存大小,这是一个很耗系统资源的过程。

还有一点就是避免交换内存,可以在配置文件中对内存进行锁定,以避免交换内存(也可以在操作系统层面进行关闭内存交换)。对应的参数: bootstrap.mlockall: true

分片与副本

分片(shard):ES是一个分布式的搜索引擎, 索引通常都会分解成不同部分, 分布在不同节点的部分数据就是分片。ES自动管理和组织分片, 并在必要的时候对分片数据进行再平衡分配, 所以用户基本上不用担心分片的处理细节。创建索引时默认的分片数为5个,并且一旦创建不能更改。

副本(replica):ES默认创建一份副本,就是说在5个主分片的基础上,每个主分片都相应的有一个副本分片。额外的副本有利有弊,有副本可以有更强的故障恢复能力,但也占了相应副本倍数的磁盘空间。

那我们在创建索引的时候,应该创建多少个分片与副本数呢?

对于副本数,比较好确定,可以根据我们集群节点的多少与我们的存储空间决定,我们的集群服务器多,并且有足够大多存储空间,可以多设置副本数,一般是1-3个副本数,如果集群服务器相对较少并且存储空间没有那么宽松,则可以只设定一份副本以保证容灾(副本数可以动态调整)。

对于分片数,是比较难确定的。因为一个索引分片数一旦确定,就不能更改,所以我们在创建索引前,要充分的考虑到,以后我们创建的索引所存储的数据量,否则创建了不合适的分片数,会对我们的性能造成很大的影响。 对于分片数的大小,业界一致认为分片数的多少与内存挂钩,认为1GB堆内存对应20-25个分片,而一个分片的大小不要超过50G,这样的配置有助于集群的健康。但是我个人认为这样的配置方法过于死板,我个人在调优ES集群的过程中,根据总数据量的大小,设定了相应的分片,保证每一个分片的大小没有超过50G(大概在40G左右),但是相比之前的分片数查询起来,效果并不明显。之后又尝试了增加分片数,发现分片数增多之后,查询速度有了明显的提升,每一个分片的数据量控制在10G左右。 查询大量小分片使得每个分片处理数据速度更快了,那是不是分片数越多,我们的查询就越快,ES性能就越好呢?其实也不是,因为在查询过程中,有一个分片合并的过程,如果分片数不断的增加,合并的时间则会增加,而且随着更多的任务需要按顺序排队和处理,更多的小分片不一定要比查询较小数量的更大的分片更快。如果有多个并发查询,则有很多小碎片也会降低查询吞吐量。

如果现在你的场景是分片数不合适了,但是又不知道如何调整,那么有一个好的解决方法就是按照时间创建索引,然后进行通配查询。如果每天的数据量很大,则可以按天创建索引,如果是一个月积累起来导致数据量很大,则可以一个月创建一个索引。如果要对现有索引进行重新分片,则需要重建索引,我会在文章的最后总结重建索引的过程。

参数调优

下面我会介绍一些ES关键参数的调优。

有很多场景是,我们的ES集群占用了多大的cpu使用率,该如何调节呢。cpu使用率高,有可能是写入导致的,也有可能是查询导致的,那要怎么查看呢? 可以先通过GET nodes/{node}/hot threads查看线程栈,查看是哪个线程占用cpu高,如果是elasticsearch {node} [T#10]则是查询导致的,如果是elasticsearch {node} [T#1]则是数据写入导致的。 我在实际调优中,cpu使用率很高,如果不是SSD,建议把index.merge.scheduler.max thread count: 1 索引merge最大线程数设置为1个,该参数可以有效调节写入的性能。因为在存储介质上并发写,由于寻址的原因,写入性能不会提升,只会降低。

还有几个重要参数可以进行设置,各位同学可以视自己的集群情况与数据情况而定。

index.refresh_interval:这个参数的意思是数据写入后几秒可以被搜索到,默认是1s。每次索引的refresh会产生一个新的lucene段,这会导致频繁的合并行为,如果业务需求对实时性要求没那么高,可以将此参数调大,实际调优告诉我,该参数确实很给力,cpu使用率直线下降。

indices.memory.index buffer size:如果我们要进行非常重的高并发写入操作,那么最好将indices.memory.index buffer size调大一些,index buffer的大小是所有的shard公用的,一般建议(看的大牛博客),对于每个shard来说,最多给512mb,因为再大性能就没什么提升了。ES会将这个设置作为每个shard共享的index buffer,那些特别活跃的shard会更多的使用这个buffer。默认这个参数的值是10%,也就是jvm heap的10%。

translog:ES为了保证数据不丢失,每次index、bulk、delete、update完成的时候,一定会触发刷新translog到磁盘上。在提高数据安全性的同时当然也降低了一点性能。如果你不在意这点可能性,还是希望性能优先,可以设置如下参数:

"index.translog": {
            "sync_interval": "120s",     --sync间隔调高
            "durability": "async",       -– 异步更新
            "flush_threshold_size":"1g"  --log文件大小
        }

这样设定的意思是开启异步写入磁盘,并设定写入的时间间隔与大小,有助于写入性能的提升。

还有一些超时参数的设置: discovery.zen.ping timeout 判断master选举过程中,发现其他node存活的超时设置 discovery.zen.fd.ping interval 节点被ping的频率,检测节点是否存活 discovery.zen.fd.ping timeout 节点存活响应的时间,默认为30s,如果网络可能存在隐患,可以适当调大 discovery.zen.fd.ping retries ping失败/超时多少导致节点被视为失败,默认为3

其他建议

还有一些零碎的优化建议喔。

插入索引自动生成id:当写入端使用特定的id将数据写入ES时,ES会检查对应的索引下是否存在相同的id,这个操作会随着文档数量的增加使消耗越来越大,所以如果业务上没有硬性需求建议使用ES自动生成的id,加快写入速率。

避免稀疏索引:索引稀疏之后,会导致索引文件增大。ES的keyword,数组类型采用doc_values结构,即使字段是空值,每个文档也会占用一定的空间,所以稀疏索引会造成磁盘增大,导致查询和写入效率降低。

我的调优

下面说一说我的调优:我的调优主要是重建索引,更改了现有索引的分片数量,经过不断的测试,找到了一个最佳的分片数量,重建索引的时间是漫长的,在此期间,又对ES的写入进行了相应的调优,使cpu使用率降低下来。附上我的调优参数。

index.merge.scheduler.max thread count:1 #索引merge最大线程数 indices.memory.index buffer size:30% #内存 index.translog.durability:async #这个可以异步写硬盘,增大写的速度 index.translog.sync interval:120s #translog间隔时间 discovery.zen.ping timeout:120s #心跳超时时间 discovery.zen.fd.ping interval:120s #节点检测时间 discovery.zen.fd.ping timeout:120s #ping超时时间 discovery.zen.fd.ping retries:6 #心跳重试次数 thread pool.bulk.size:20 #写入线程个数 由于我们查询线程都是在代码里设定好的,我这里只调节了写入的线程数 thread pool.bulk.queue size:1000 #写入线程队列大小 index.refresh_interval:300s #index刷新间隔

关于重建索引

在重建索引之前,首先要考虑一下重建索引的必要性,因为重建索引是非常耗时的。 ES的reindex api不会去尝试设置目标索引,不会复制源索引的设置,所以我们应该在运行_reindex操作之前设置目标索引,包括设置映射(mapping),分片,副本等。

第一步,和创建普通索引一样创建新索引。当数据量很大的时候,需要设置刷新时间间隔,把refresh intervals设置为-1,即不刷新,number of_replicas副本数设置为0(因为副本数可以动态调整,这样有助于提升速度)。

{
	"settings": {

		"number_of_shards": "50",
		"number_of_replicas": "0",
		"index": {
			"refresh_interval": "-1"
		}
	}
	"mappings": {
    }
}

第二步,调用reindex接口,建议加上wait for completion=false的参数条件,这样reindex将直接返回taskId。

POST _reindex?wait_for_completion=false

{
  "source": {
    "index": "old_index",   //原有索引
    "size": 5000            //一个批次处理的数据量
  },
  "dest": {
    "index": "new_index",   //目标索引
  }
}

第三步,等待。可以通过 GET tasks?detailed=true&actions=*reindex来查询重建的进度。如果要取消task则调用 tasks/node id:task id/_cancel。

第四步,删除旧索引,释放磁盘空间。更多细节可以查看ES官网的reindex api。

那么有的同学可能会问,如果我此刻ES是实时写入的,那咋办呀? 这个时候,我们就要重建索引的时候,在参数里加上上一次重建索引的时间戳,直白的说就是,比如我们的数据是100G,这时候我们重建索引了,但是这个100G在增加,那么我们重建索引的时候,需要记录好重建索引的时间戳,记录时间戳的目的是下一次重建索引跑任务的时候不用全部重建,只需要在此时间戳之后的重建就可以,如此迭代,直到新老索引数据量基本一致,把数据流向切换到新索引的名字。

POST /_reindex
{
    "conflicts": "proceed",          //意思是冲突以旧索引为准,直接跳过冲突,否则会抛出异常,停止task
    "source": {
        "index": "old_index"         //旧索引
        "query": {
            "constant_score" : {
                "filter" : {
                    "range" : {
                        "data_update_time" : {
                            "gte" : 123456789   //reindex开始时刻前的毫秒时间戳
                            }
                        }
                    }
                }
            }
        },
    "dest": {
        "index": "new_index",       //新索引
        "version_type": "external"  //以旧索引的数据为准
        }
}

以上就是我在ES调优上的一点总结,希望能够帮助到对ES性能有困惑的同学们,谢谢大家。 ---我是皮蛋,我喂自己袋盐。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Is Parallel Programming Hard, And, If So, What Can You Do About

Is Parallel Programming Hard, And, If So, What Can You Do About

Paul E. McKenney

The purpose of this book is to help you understand how to program shared-memory parallel machines without risking your sanity.1 By describing the algorithms and designs that have worked well in the pa......一起来看看 《Is Parallel Programming Hard, And, If So, What Can You Do About 》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试