漫谈ElasticSearch关于ES性能调优⼏件必须知道的事
原创博客,转载请联系博主!
(零)ElasticSearch架构概述
ElasticSearch是现在技术前沿的⼤数据引擎,常见的组合有ES+Logstash+Kibana作为⼀套成熟的⽇志系统,其中Logstash是ETL⼯具,Kibana是数据分析展⽰平台。ES让⼈惊艳的是他强⼤的搜索相关能⼒和灾备策略,ES开放了⼀些接⼝供开发者研发⾃⼰的插件,ES结合中⽂分词的插件会给ES 的搜索和分析起到很⼤的推动作⽤。ElasticSearch是使⽤开源全⽂检索库ApacheLucene进⾏索引和搜索的,说架构必须和Lucene的⼀些东西打交道。
jvm调优参数
关于Lucene:
ApacheLucene将写⼊索引的所有信息组织成⼀种倒排索引(Inverted Index)的结构之中,该结构是种将词项映射到⽂档的数据结构。其⼯作⽅式与传统的关系数据库不同,⼤致来说倒排索引是⾯向词项⽽不是⾯向⽂档的。且Lucene索引之中还存储了很多其他的信息,如词向量等等,每个Lucene都是由多个段构成的,每个段只会被创建⼀次但会被查询多次,段⼀旦创建就不会再被修改。多个段会在段合并的阶段合并在⼀起,何时合并由Lucene的内在机制决定,段合并后数量会变少,但是相应的段本⾝会变⼤。段合并
的过程是⾮常消耗I/O的,且与之同时会有些不再使⽤的信息被清理掉。在Lucene中,将数据转化为倒排索引,将完整串转化为可⽤于搜索的词项的过程叫做分析。⽂本分析由分析器(Analyzer)来执⾏,分析其由分词器(Tokenizer),过滤器(Filter)和字符映射器(Character Mapper)组成,其各个功能显⽽易见。除此之外,Lucene有⾃⼰的⼀套完整的查询语⾔来帮助我们进⾏搜索和读写。
[注]ES中的索引指的是查询/寻址时URI中的⼀个字段如:[host]:[port(9200)]/[index]/[type]/[ID]?[option],⽽Lucene中的索引更多地和ES中的分⽚的概念相对应。
回到ElasticSearch,ES的架构遵循的设计理念有以下⼏个特征:
1. 合理的默认配置:只需修改节点中的Yaml配置⽂件,就可以迅捷配置。这和Spring4中对配置的简化有相似的地⽅。
2. 分布式⼯作模式:ES强⼤的Zen发现机制不仅⽀持组⼴播也⽀持点单播,且有“知⼀点即知天下”之妙。
3. 对等架构:节点之间⾃动备份分⽚,且使分⽚本⾝和样本之间尽量”远离“,可以避免单点故障。且Master节点和Data节点⼏乎完全等价。
4. 易于向集扩充新节点:⼤⼤简化研发或运维将新节点加⼊集所需的⼯作。
5. 不对索引中的数据结构增加任何限制:ES⽀持在⼀个索引之中存在多种数据类型。
6. 准实时:搜索和版本同步,由于ES是分布式应⽤,⼀个重⼤的挑战就是⼀致性问题,⽆论索引还是⽂档数据,然⽽事实证明ES表现优秀。(⼀)分⽚策略
选择合适的分⽚数和副本数。ES的分⽚分为两种,主分⽚(Primary Shard)和副本(Replicas)。默认情况下,ES会为每个索引创建5个分⽚,即使是在单机环境下,这种冗余被称作过度分配(Over Allocation),⽬前看来这么做完全没有必要,仅在散布⽂档到分⽚和处理查询的过程中就增加了更多的复杂性,好在ES的优秀性能掩盖了这⼀点。假设⼀个索引由⼀个分⽚构成,那么当索引的⼤⼩超过单个节点的容量的时候,ES不能将索引分割成多份,因此必须在创建索引的时候就指定好需要的分⽚数量。此时我们所能做的就是创建⼀个新的索引,并在初始设定之中指定这个索引拥有更多的分⽚。反之如果过度分配,就增⼤了Lucene在合并分⽚查询结果时的复杂度,从⽽增⼤了耗时,所以我们得到了以下结论:
我们应该使⽤最少的分⽚!
主分⽚,副本和节点最⼤数之间数量存在以下关系:
节点数<=主分⽚数*(副本数+1)
控制分⽚分配⾏为。以上是在创建每个索引的时候需要考虑的优化⽅法,然⽽在索引已创建好的前提下,是否就是没有办法从分⽚的⾓度提⾼了性能了呢?当然不是,⾸先能做的是调整分⽚分配器的类型,具体是在l中设置pe属性,共有两种分⽚器
even_shard,balanced(默认)。even_shard是尽量保证每个节点都具有相同数量的分⽚,balanced是基于可控制的权重进⾏分配,相对于前⼀个分配器,它更暴漏了⼀些参数⽽引⼊调整分配过程的能⼒。
每次ES的分⽚调整都是在ES上的数据分布发⽣了变化的时候进⾏的,最有代表性的就是有新的数据节点加⼊了集的时候。当然调整分⽚的时机并不是由某个阈值触发的,ES内置⼗⼀个裁决者来决定是否触发分⽚调整,这⾥暂不赘述。另外,这些分配部署策略都是可以在运⾏时更新的,更多配置分⽚的属性也请⼤家⾃⾏Google。
(⼆)路由优化
ES中所谓的路由和IP⽹络不同,是⼀个类似于Tag的东西。在创建⽂档的时候,可以通过字段为⽂档增加⼀个路由属性的Tag。ES内在机制决定了拥有相同路由属性的⽂档,⼀定会被分配到同⼀个分⽚上,⽆论是主分⽚还是副本。那么,在查询的过程中,⼀旦指定了感兴趣的路由属性,ES就可以直接到相应的分⽚所在的机器上进⾏搜索,⽽避免了复杂的分布式协同的⼀些⼯作,从⽽提升了ES的性能。于此同时,假设机器1上存有路由属性A 的⽂档,机器2上存有路由属性为B的⽂档,那么我在查询的时候⼀旦
指定⽬标路由属性为A,即使机器2故障瘫痪,对机器1构不成很⼤影响,所以这么做对灾况下的查询也提出了解决⽅案。所谓的路由,本质上是⼀个分桶(Bucketing)操作。当然,查询中也可以指定多个路由属性,机制⼤同⼩异。
(三)ES上的GC调优
ElasticSearch本质上是个Java程序,所以配置JVM垃圾回收器本⾝也是⼀个很有意义的⼯作。我们使⽤JVM的Xms和Xmx参数来提供指定内存⼤⼩,本质上提供的是JVM的堆空间⼤⼩,当JVM的堆空间不⾜的时候就会触发致命的OutOfMemoryException。这意味着要么内存不⾜,要么出现了内存泄露。处理GC问题,⾸先要确定问题的源头,⼀般有两种⽅案:
1. 开启ElasticSearch上的GC⽇志
2. 使⽤jstat命令
3. ⽣成内存Dump
关于第⼀条,在ES的配置⽂件l中有相关的属性可以配置,关于每个属性的⽤途这⾥当然说不完。
第⼆条,jstat命令可以帮助我们查看JVM堆中各个区的使⽤情况和GC的耗时情况。
第三条,最后的办法就是将JVM的堆空间转储到⽂件中去,实质上是对JVM堆空间的⼀个快照。
另外,通过修改ES节点的启动参数,也可以调整GC的⽅式,但是实质上和上述⽅法是等同的。
(四)避免内存交换
这⼀点很简单,由于操作系统的虚拟内存页交换机制,会给性能带来障碍,如数据写满内存会写⼊Linux中的Swap分区。
可以通过在l⽂件中的bootstrap.mlockall设置为true来实现,但是需要管理员权限,需要修改操作系统的相关配置⽂件。
(五)控制索引合并
上⽂提到过,ES中的分⽚和副本本质上都是Lucene索引,⽽Lucene索引⼜基于多个索引段构建(⾄少⼀个),索引⽂件中的绝⼤多数都是只被写⼀次,读多次,在Lucene内在机制控制下,当满⾜某种条件的时候多个索引段会被合并到⼀个更⼤的索引段,⽽那些旧的索引段会被抛弃并移除磁盘,这个操作叫做段合并。
Lucene要执⾏段合并的理由很简单充分:索引段粒度越⼩,查询性能越低且耗费的内存越多。频繁的⽂档更改操作会导致⼤量的⼩索引段,从⽽导致
⽂件句柄打开过多的问题,如修改系统配置,增⼤系统允许的最⼤⽂件打开数。总的来讲,当索引段由多⼀个合并为⼀个的时候,会减少索引段的数量从⽽提⾼ES性能。对于研发者来讲,我们所能做的就是选择合适的合并策略,尽管段合并完全是Lucene的任务,但随着Lucene开放更多配置借⼝,新版本的ES还是提供了三种合并的策略tiered,log_byte_size,log_doc。另外,ES也提供了两种Lucene索引段合并的调度器:concurrent和serial。其中各者具体区别,这⾥暂不赘述,只是抛砖引⽟。
打字好累,⼤家晚安。