本文转载自微信公众号“小姐姐的味道”,作者姐姐养的狗。转载本文请联系味觉小姐公众号。ES应用广泛,尤其是ELKB,几乎被一定规模的日志系统所采用。日志属于写多读少的业务场景,对写入速度要求高。以我们的一个集群为例。单集群日志量达到数百TB,日志写入量达到每秒10万条。ES不是简单的顺序写的。为了构建倒排索引,保证数据的可靠性和实时性,其背后有很多耗时的合并或附加操作。磁盘I/O和CPU压力很大!使用iotop观察,可以发现ES进程几乎占据了SSD盘的I/O资源,达到了200MB/s+。使用top命令观察,发现8核CPU的用户几乎满了。ES非常耗费资源。既然很容易找到系统的瓶颈,那就着重优化这个资源,把ES的写入速度压榨到极限。本次优化的样本版本为7.9.2。ES的版本升级真快,完全脱离了5的时代。1.优化哪些操作占用资源,首先要知道ES的写流程,知道哪些步骤是最耗时的——消费。首先是副本问题。为了至少保证高可用,这里的副本数设置为1,不能保存。所以设置副本数为0只适合第一次导入数据。如上图所示,一条数据要想最终落地,需要经历多个步骤。在这个过程中,甚至还会有tranlog这样的备份机制。ES的底层存储是Lucene,里面包含了一系列的倒排索引。这样的索引称为段。但是记录并不是直接写到segment中,而是先写到一个buffer中。当buffer满了,或者在buffer中停留的时间足够长,达到刷新时间(标记关键点),buffer的内容会一次性写入到segment中。这就是refresh_interval属性的配置会严重影响性能的原因。如果不想要高实时性,还不如配置大一点。缓冲区默认使用10%的堆空间,最小值为48mb(用于碎片化)。如果你索引多,写重,这部分内存占用比较大,可以适当增加。2.开始优化数据写入,主要有flush、refresh和merge三个动作。通过调整它们的行为,可以在性能和数据可靠性之间做出权衡。从上面的介绍可以看出,translog写的是全量数据。有点像MysSQL中的binlog或者redis的aof,用来保证异常情况下的数据安全。这是因为,我们将数据写入磁盘后,需要调用fsync将数据刷入磁盘。如果我们不这样做,数据将在系统断电时丢失。ES默认每次请求都会flush,但是对于日志来说,这不是必须的。您可以将此过程更改为异步。参数如下:curl-H"Content-Type:application/json"-XPUT'http://localhost:9200/_all/_settings?preserve_existing=true'-d'{"index.translog.durability":"async","index.translog.flush_threshold_size":"512mb","index.translog.sync_interval":"60s"}'这个可以说是优化最重要的一步,对性能影响最大,但是在极端情况下,一些数据可能会丢失。对于日志系统来说,是可以容忍的。refresh除了写入translog外,还会将数据写入缓冲区。但是注意!这时候buffer的内容是查不到的,需要写入segment中。这是刷新动作,默认是1秒。即你写的数据1秒后大概率会被搜索到。所以ES不是一个实时搜索系统,它是一个近实时系统。这个刷新间隔可以通过index.refresh_interval修改。对于日志系统,当然要调大一点。这里xjjdog调整为120s,减少了这些落到segment的频率,速度自然会更快。curl-H"Content-Type:application/json"-XPUT'http://localhost:9200/_all/_settings?preserve_existing=true'-d'{"index.refresh_interval":"120s"}'mergemerge其实就是lucene该机制主要是将小段块合并生成更大的段,以提高检索速度。原因是刷新过程会产生很多小段文件,数据删除也会产生空间碎片。所以merge,通俗的说,就像一个碎片整理的过程。像postgresql等,也有vaccum进程在做同样的事情。显然,这种排序操作会消耗I/O并浪费CPU。可怕的是merge有三种策略。tiered是默认选项,它合并大小相似的索引段,同时考??虑到每层允许的最大索引段数。log_byte_size以字节数的对数为计算单位,选择多个索引合并创建新索引。log_doc以索引段中的文档数为计算单位,选择多个索引合并创建新的索引。每个策略都有非常详细的针对性配置,这里就不赘述了。由于日志系统不会随意删除,我们可以保持默认。3.微调新版本优化了线程池的配置,不再需要配置复杂的搜索、批量、索引线程池。如有必要,配置以下内容:thread_pool.get.size、thread_pool.write.size、thread_pool.listener.size、thread_pool.analyze.size。具体可以调整observable_cat/thread_pool接口暴露的数据。其实可以通过配置多块磁盘来分散I/O压力,但是容易造成数据热点集中在单块磁盘上。Lucene的索引建立过程消耗大量CPU,可以通过减少倒排索引的数量来降低CPU消耗。第一个优化是减少字段数量;第二个优化是减少索引字段的数量。具体操作是将不需要查找的字段的index属性设置为not_analyzed或者no。至于_source和_all,在实际调试中作用不大,这里不再赘述。另外,如果通过filebeat或logstash等组件传输日志,一般会启用batch模式。批处理可以提高性能,但不能太大。可根据实际观察设置。一般在1k-1w之间是可以的。EndES有多种使用场景。鉴于其NoSQL特性,有些人甚至用它来取代传统的关系数据库。这没问题,但要注意它的延迟。本文主要针对吞吐量优先的日志写入场景,数据延迟尤为明显。ES默认不是为这种场景设计的,所以开箱即用的配置效率很低。我们了解到ES写有flush、refresh和merge过程。其中,对I/O影响最大的动作是translog和merge的动作;对CPU影响最大的是索引创建和合并的过程。在通常的映射设计中,需要尽量减少字段数和索引字段数。这样双管齐下,可以解决CPU和I/O这两个瓶颈。作者简介:品味小姐姐(xjjdog),一个不允许程序员走弯路的公众号。专注于基础架构和Linux。十年架构,每天百亿流量,与你探讨高并发世界,给你不一样的滋味。我的个人微信xjjdog0,欢迎加好友进一步交流。
