专栏名称: 方志朋
号主为CSDN博客之星,博客访问量突破一千万,著有畅销书《深入理解SpringCloud与微服务构建》。主要分享Java、后端架构等技术,用大厂程序员的视角来探讨技术进阶、面试指南、职业规划等。15W技术人的选择!
目录
相关文章推荐
程序员的那些事  ·  3 个“芯片镖客”犯了 28 ... ·  18 小时前  
程序员的那些事  ·  刚刚!TypeScript 之父宣布一重大好消息 ·  昨天  
程序员的那些事  ·  华为重拳出击!华为重拳出击!华为重拳出击! ·  2 天前  
OSC开源社区  ·  MCP这么火,来一波简单实操记录 ·  3 天前  
码农翻身  ·  微软发明了世界上最流行的编程语言! ·  2 天前  
51好读  ›  专栏  ›  方志朋

Elasticsearch使用优化之拙见

方志朋  · 公众号  · 程序员  · 2019-09-04 09:31

正文

点击上方“ 方志朋 ”,选择“ 设为星标

做积极的人,而不是积极废人


Elasticsearch常常作为日志存储和分析的工具,在企业级应用中常常使用。Elasticsearch提供强大的搜索、分析功能,已经是后端技术栈不可缺少的一部分。
在维护ElastciSearch集群的时候,对Elasticsearch进行了一些调优和分析,现整理成文,纯属拙见,如果有不合理之处,欢迎指出探讨。我所使用的Elasticsearch版本为5.x。

文件句柄优化

Elasticsearch有大量的查询数据和插入数据的请求,需要大量文件句柄,centos系统默认的1024个文件句柄。如果文件句柄用完了,这就意味着操作系统会拒绝连接,意味着数据可能丢失,这是灾难性的后果,
不能被接受。登陆Elasticsearch的启动用户,用一下命令查看:

ulimit -a

查看结果:

core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 127673
max locked memory       (kbytes, -l) unlimited
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 2056474
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

上面的文件句柄(open files)的个数为1024,在ElasticSearch大量请求的情况下,这个句柄数量是不够的,可以改成655360。

临时修改可以通过执行以下命令,即可立即生效,但是机器重启后又会失效:

ulimit -n 655360

永久生效,修改/etc/security/limits.conf,需要重启机器生效:

u_es - nofile 655360

上述配置中u_es为启动ElasticSearch的用户,设置了该用户的ElasticSearch的文件句柄为655360、

JVM参数优化

Elasticsearch是运行在JVM上的,对其做JVM参数调优至关重要。最常见的调优是Java内存的分配。下面是JVM的内存模型,具体每块的作用,不在这里阐述。

新生代和老年代分配的内存比例给多大?

Jvm内存分为新生代和老年代。

  • 新生代(或者伊甸园)
    新实例化的对象分配的空间。新生代空间通常都非常小,一般在 100 MB–500 MB。新生代也包含两个 幸存 空间。

  • 老年代
    较老的对象存储的空间。这些对象预计将长期留存并持续上很长一段时间。老生代通常比新生代大很多。

新生代、老生代的垃圾回收都有一个阶段会“stop the world”。在这段时间里,JVM 停止了程序运行,以便对对象进行可达性分析,收集死亡对象。在这个时间停止阶段,一切都不会发生。请求不被服务,ping 不被回应,分片不被分配。整个世界都真的停止了。
对于新生代,这不是什么大问题;那么小的空间意味着 GC 会很快执行完。但是老生代大很多,而这里面一个慢 GC 可能就意味着 1 秒乃至 15 秒的暂停——对于服务器软件来说这是不可接受的。

那一般我们给新生代和老年代分配多大的内存呢?他们的比例是多少呢?
一般来说,老年代和新生代的内存比例为2:1是比较合适的。比如给堆内存分配3G,则新生代分配1G,其余都给老年代。在ElasticSearce的配置文件jvm.options文件配置:

-Xms3g  //配置堆初始化大小
-Xmx3g   //配置堆的最大内存
-Xmn1g   //配置新生代内存。

该分配多大的内存给Elasticesearch?

在使用Elasticesearch的时候,我们对装Elasticesearch的机器进行了升级,从最小的8G内存升级到了16G内存,然后到目前的32G内存。一台机器装一个Elasticesearch节点,我们应该怎么分配机器的内存呢?
官方给出了解决方案,把一半(少于)的内存分配给Luence,另外的内存分配给ElasticSearch.

内存对于 Elasticsearch 来说绝对是重要的,它可以被许多内存数据结构使用来提供更快的操作。但是说到这里, 还有另外一个内存消耗大户 非堆内存 (off-heap):Lucene。

Lucene 被设计为可以利用操作系统底层机制来缓存内存数据结构。Lucene 的段是分别存储到单个文件中的。因为段是不可变的,这些文件也都不会变化,这是对缓存友好的,同时操作系统也会把这些段文件缓存起来,以便更快的访问。

Lucene 的性能取决于和操作系统的相互作用。如果你把所有的内存都分配给 Elasticsearch 的堆内存,那将不会有剩余的内存交给 Lucene。这将严重地影响全文检索的性能。

标准的建议是把 50% 的可用内存作为 Elasticsearch 的堆内存,保留剩下的 50%。当然它也不会被浪费,Lucene 会很乐意利用起余下的内存。

我们实际的解决办法是将机器的一半分给Elasticesearch的堆,栈内存、方法区、常量池、非堆内存占用另外一半。

分配给堆最大内存应该小于 32766 mb(~31.99 gb)

JVM 在内存小于 32 GB 的时候会采用一个内存对象指针压缩技术。

对于 32 位的系统,意味着堆内存大小最大为 4 GB。对于 64 位的系统, 可以使用更大的内存,但是 64 位的指针意味着更大的浪费,因为你的指针本身大了。更糟糕的是, 更大的指针在主内存和各级缓存(例如 LLC,L1 等)之间移动数据的时候,会占用更多的带宽。

Java 使用一个叫作 内存指针压缩(compressed oops)的技术来解决这个问题。它的指针不再表示对象在内存中的精确位置,而是表示 偏移量 。这意味着 32 位的指针可以引用 40 亿个 对象 , 而不是 40 亿个字节。最终, 也就是说堆内存增长到 32 GB 的物理内存,也可以用 32 位的指针表示。

一旦你越过那个神奇的 ~32 GB 的边界,指针就会切回普通对象的指针。每个对象的指针都变长了,就会使用更多的 CPU 内存带宽,也就是说你实际上失去了更多的内存。事实上,当内存到达 40–50 GB 的时候,有效内存才相当于使用内存对象指针压缩技术时候的 32 GB 内存。

这段描述的意思就是说:即便你有足够的内存,也尽量不要 超过 32 GB。因为它浪费了内存,降低了 CPU 的性能,还要让 GC 应对大内存。

关掉swap

内存交换 到磁盘对服务器性能来说是 致命 的。

如果内存交换到磁盘上,一个 100 微秒的操作可能变成 10 毫秒。再想想那么多 10 微秒的操作时延累加起来。不难看出 swapping 对于性能是多么可怕。

用以下命令关掉swap:

sudo swapoff -a

不要碰以下的配置

所有的调整就是为了优化,但是这些调整,你真的不需要理会它。因为它们经常会被乱用,从而造成系统的不稳定或者糟糕的性能,甚至两者都有可能。

线程池配置

许多人 喜欢 调整线程池。无论什么原因,人们都对增加线程数无法抵抗。索引太多了?增加线程!搜索太多了?增加线程!节点空闲率低于 95%?增加线程!
Elasticsearch 默认的线程设置已经是很合理的了。对于所有的线程池(除了 搜索 ),线程个数是根据 CPU 核心数设置的。如果你有 8 个核,你可以同时运行的只有 8 个线程,只分配 8 个线程给任何特定的线程池是有道理的。
搜索线程池设置的大一点,配置为 int(( 核心数 * 3 )/ 2 )+ 1 。

垃圾回收器

Elasticsearch 默认的垃圾回收器( GC )是 CMS。这个垃圾回收器可以和应用并行处理,以便它可以最小化停顿。然而,它有两个 stop-the-world 阶段,处理大内存也有点吃力。

尽管有这些缺点,它还是目前对于像 Elasticsearch 这样低延迟需求软件的最佳垃圾回收器。官方建议使用 CMS。

合理设置最小主节点

minimum_master_nodes 设置及其重要,为了防止集群脑裂,这个参数应该设置为法定个数就是 ( master 候选节点个数 / 2) + 1。

分片均匀,磁盘优化,剔除掉高负载的Master竞选?

笔者在实际生产环境中遇到了有一个节点的负载是其他节点的几倍,从虚拟机监控上看,所有的节点的qps是差不多的。机器的配置是一样的,为什么负载会有如此大的差距?

  • 首先,我们怀疑数据分配不均匀,我们排查了下,没有这种现象。

  • 然后,我们监控到了高负载的节点磁盘IO非常的高,经常达到100%,我们怀疑是那个虚拟机磁盘性能不行。但是我们当时没有更好的磁盘。

  • 我们找到了一个适中的解决办法是将这台高负载的节点剔除Master竞选,即将elasticsearch.yml文件中的node.master改为false然后重启,负载下降了一些。

数据存储天数的优化

存储天数的优化,这个需要根据实际的业务来,下面是删除过期数据的脚本,该脚本来源于https://stackoverflow.com/questions/33430055/removing-old-indices-in-elasticsearch#answer-39746705  ;

#!/bin/bash
searchIndex=logstash-monitor
elastic_url=logging.core.k94.kvk.nl
elastic_port=9200

date2stamp () {
    date --utc --date "$1" +%s
}

dateDiff (){
    case $1 in
        -s)   sec=1;      shift;;
        -m)   sec=60;     shift;;
        -h)   sec=3600;   shift;;
        -d)   sec=86400;  shift;;
        *)    sec=86400;;
    esac
    dte1=$(date2stamp $1)
    dte2=$(date2stamp $2)
    diffSec=$((dte2-dte1))
    if ((diffSec then abs=-1; else abs=1; fi
    echo $((diffSec/sec*abs))
}

for index in $(curl -s "${elastic_url}:${elastic_port}/_cat/indices?v" |     grep -E ${searchIndex}-20[0-9][0-9]\.[0-1][0-9]\.[0-3][0-9]" | awk '{     print $3 }'); do
  date=$(echo ${index: -10} | sed 's/\./-/g')
  cond=$(date +%Y-%m-%d)
  diff=$(dateDiff -d $date $cond)
  echo -n "${index} (${diff})"
  if [ $diff






请到「今天看啥」查看全文