首先说明,此篇文章为转载,原文url:https://segmentfault.com/a/1190000017908981,我只是一个优秀的搬砖工,看到优秀的文章天然要和你们分享。node
这是目前为止我以为最优秀的es调优之一,以前的优化也各有长处,可是写这篇文章的大神是真正的10亿数据调优的大神,几乎涵盖了我翻过的全部的文章,因此佩服。json
“ELK”是 ElasticSearch、Logstash、Kibana 三门技术的简称。现在 ELK 技术栈在互联网行业数据开发领域使用率愈来愈高,作过数据收集、数据开发、数据存储的同窗相信对这个简称并不陌生,而ElasticSearch(如下简称 ES)则在 ELK 栈中占着举足轻重的地位。bootstrap
前一段时间,我亲身参与了一个 ES 集群的调优,今天把我所了解与用到的调优方法与你们分享,若有错误,请你们包涵与指正。segmentfault
系统层面的调优主要是内存的设定与避免交换内存。api
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}][search][T#10]
则是查询致使的,若是是 elasticsearch[{node}][bulk][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 在增长,那么咱们重建索引的时候,须要记录好重建索引的时间戳,记录时间戳的目的是下一次重建索引跑任务的时候不用所有重建,只须要在此时间戳以后的重建就能够,如此迭代,直到新老索引数据量基本一致,把数据流向切换到新索引的名字。
以上就是我在 ES 调优上的一点总结,但愿可以帮助到对 ES 性能有困惑的同窗们,谢谢你们。