“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个primaryshard的基础上,每个primaryshard都有对应的replicashard。额外的副本既有优点也有缺点。拥有副本可以有更强的故障恢复能力,但也会占用对应副本数倍的磁盘空间。那么我们在创建索引的时候,应该创建多少分片,多少份呢?对于副本数,比较容易确定,可以根据我们的集群节点数和我们的存储空间来确定。我们集群服务器多,存储空间足够,可以设置多份,一般是1-3份。如果集群服务器比较小,存储空间不是那么松散,可以只设置一个副本保证容灾(副本数可以动态调整)。对于分片的数量,比较难确定,因为一个索引的分片数量一旦确定,就不能再改变了。因此,在创建索引之前,我们必须充分考虑到我们以后创建的索引存储的数据量,否则创建不合适的分片数量会对我们的性能产生很大的影响。关于分片数量的大小,业界一致认为分片数量与内存挂钩。认为1GB的堆内存对应20-25个分片,一个分片的大小最好不要超过50G。这样的配置有利于集群的健康。不过个人认为这样的配置方式过于死板。在调优ES集群的过程中,我根据总数据量设置了相应的分片,保证每个分片的大小不超过50G(大概在40G以内),但是和之前的分片数量相比,效果是不明显。之后尝试增加shard个数,发现shard个数增加后,查询速度明显提升,每个shard的数据量控制在10G左右。查询大量的小分片使得每个分片处理数据的速度更快。这是否意味着分片越多,我们的查询越快,ES性能越好?其实不是,因为在查询过程中,有一个分片合并的过程。如果分片数量继续增加,合并时间会增加。并且随着更多的任务需要按顺序排队和处理,更多的小分片不一定比查询较少数量的大分片更快。如果有许多并发查询,拥有许多小分片也会降低查询吞吐量。如果分片数量在你当前的场景中不合适,但是又不知道如何调整,那么一个好的解决方案就是根据时间创建索引,然后进行通配查询。如果每天的数据量很大,可以按天创建索引。如果由于一个月的积累导致数据量较大,可以针对一个月创建索引。如果要对现有索引重新分片,则需要重建索引。我会在文末总结重建索引的过程。参数调优下面我将介绍一些ES关键参数的调优。在很多场景下,我们的ES集群占用了多少CPU,又该如何调整呢?CPU占用率高可能是写入或查询导致的,那么如何查看呢?可以先通过GET_nodes/{node}/hot_threads查看线程栈,看看哪个线程占用CPU高:如果是elasticsearch[{node}][search][T#10],就是查询导致的。如果是elasticsearch[{node}][bulk][T#1]就是写数据导致的。在我实际调优中,CPU占用率非常高。如果不是SSD,建议设置index.merge.scheduler.max_thread_count:1,将indexmerge***线程数设置为1,该参数可以有效调整写入性能。因为在存储介质上并发写入,由于寻址的原因,写入性能不会提高,只会降低。还有几个重要的参数可以设置,同学们可以根据自己的集群情况和数据情况来决定。index.refresh_interval:该参数表示数据写入后几秒内可以被查找到,默认1s。索引的每次刷新都会产生一个新的lucene段,这会导致频繁的合并行为。如果业务需求没有这么高的实时性要求,可以增加这个参数。实际调优告诉我,这个参数真的很厉害,CPU占用直线下降。indices.memory.index_buffer_size:如果我们要进行非常繁重的高并发写操作,那么最好增大indices.memory.index_buffer_size。索引缓冲区的大小对所有分片都是通用的。一般建议(见大牛博客)每个shard最大512mb,因为再大也不会有性能上的提升。ES会使用这个设置作为每个shard共享的indexbuffer,那些特别活跃的shard会更多的使用这个buffer。这个参数的默认值是10%,也就是jvm堆的10%。translog:为了保证数据不丢失,ES会在每次index、bulk、delete、update完成时触发translog刷新到磁盘。在提高数据安全性的同时,当然也会降低一点性能。如果你不关心这种可能性,仍然希望性能优先,可以设置如下参数:"index.translog":{"sync_interval":"120s",--syncintervalincrease"durability":"async",--Asynchronousupdate"flush_threshold_size":"1g"--logfilesize}该设置表示启用异步写入磁盘,并设置写入的时间间隔和大小,有助于提高写入性能。还有一些超时参数设置:discovery.zen.ping_timeout决定了其他节点在选主过程中存活的超时设置。discovery.zen.fd.ping_intervalping节点的频率,检测节点是否存活。discovery.zen.fd.ping_timeout节点存活响应时间,默认30s,如果网络中可能存在安全隐患,可以适当调整。discovery.zen.fd.ping_retries多少次ping失败/超时导致一个节点被认为失败,默认为3。id向ES写入数据,ES会检查对应索引下是否存在相同的id。这个操作会随着文档数量的增加而消耗越来越大,所以如果业务上没有硬性要求,建议使用ES自动生成的id,以加快写入速度。避免稀疏索引:索引稀疏后,会导致索引文件增大。ES的关键字,数组类型采用doc_values结构。即使字段为空,每个文档也会占用一定的空间,所以稀疏索引会导致磁盘增大,导致查询和写入效率降低。我的调优先说说我的调优:主要是重建索引,改变现有索引的分片数,不断测试找到最优的分片数。重建索引需要很长时间。期间相应调整ES的写入,降低CPU占用率。附上我的调优参数:index.merge.scheduler.max_thread_count:1#Indexmerge***threadcountindices.memory.index_buffer_size:30%#Memoryindex.translog.durability:async#这个可以异步写入硬盘,增加大写速度index.translog.sync_interval:120s#translogintervaldiscovery.zen.ping_timeout:120s#heartbeattimeoutdiscovery.zen.fd.ping_interval:120s#nodedetectiontimediscovery.zen.fd.ping_timeout:120s#pingtimeoutTimediscovery.zen.fd.ping_retries:6#心跳重试次数thread_pool.bulk.size:20#写入线程数由于我们的查询线程都在代码中设置了,所以我这里只调整了写入线程数thread_pool.bulk。queue_size:1000#写线程队列大小index.refresh_interval:300s#索引刷新间隔复制代码关于重建索引在重建索引之前,首先要考虑重建索引的必要性,因为重建索引的时候是非常耗时的。ES的reindexapi不会尝试设置目标索引,也不会复制源索引的设置,所以我们应该在运行_reindex操作之前设置目标索引,包括设置映射、分片、复制等。第一步是创建新索引与创建普通索引相同。当数据量较大时,需要设置刷新间隔,设置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查询。如果要取消任务,调用_tasks/node_id:task_id/_cancel。第4步:删除旧索引以释放磁盘空间。详情请参考ES官网的reindexapi。那么可能有同学会问,如果此时我的ES是实时写的怎么办?这时候,当我们要重建索引的时候,在参数中加入上次重建索引的时间戳。说白了,比如我们的数据是100G,此时我们重建索引,但是这个100G是在增加的,所以我们在重建索引的时候,需要记录重建索引的时间戳。记录时间戳的目的是为了下次重建索引运行任务时,不需要全部重建。您只需要在此时间戳后重建。迭代直到新旧索引的数据量基本一致,将数据流切换到新的索引上。姓名。POST/_reindex{"conflicts":"proceed",//表示冲突是基于旧索引,直接跳过冲突,否则会抛出异常,stoptask"source":{"index":"old_index"//旧索引"query":{"constant_score":{"filter":{"range":{"data_update_time":{"gte":123456789//reindex开始前的毫秒时间戳}}}}}},"dest":{"index":"new_index",//新索引"version_type":"external"//以旧索引数据为准}}以上是我ES调优的总结,希望可以帮助到对ES性能有困惑的同学,谢谢。
