• Elasticsearch性能优化汇总——写入&搜索


    概述

    本文沉淀Elasticsearch性能优化方式,包含写入、搜索优化。
    推荐结合《Elasticsearch必备原理理解》一起食用~

    一、写入速度优化

    在Elasticsearch的默认设置下,是综合考虑数据可靠性、搜索实时性、写入速度等因素的。当离开默认设置、追求极致的写入速度时,很多是以牺牲可靠性和搜索实时性为代价的。有时候,业务上对数据可靠性和搜索实时性要求并不高,反而对写入速度要求很高,此时可以调整一些策略,最大化写入速度。

    接下来的优化基于正常运行的前提下,如果是集群首次批量导入数据,则可以将副本数设置为0,导入完毕再将副本数调整回去,这样副本分片只需要复制,节省了构建索引过程。

    综合来说,提升写入速度从以下几个方面入手:

    • 加大translog flush间隔,目的是降低iops、writeblock;
    • 加大index refresh间隔,除了降低I/O,更重要的是降低了segment merge频率;
    • 调整bulk请求;
    • 优化磁盘间的任务均匀情况,将shard尽量均匀分布到物理主机的各个磁盘;
    • 优化节点间的任务分布,将任务尽量均匀地发到各节点;
    • 优化Lucene层建立索引的过程,目的是降低CPU占用率及I/O,例如,禁用_all字段。

    1.1 translog flush间隔调整

    从ES 2.x开始,在默认设置下,translog的持久化策略为:每个请求都”flush“。对应配置项如下:
    index.translog.durability:request

    这是影响ES写入速度的最大因素。但是只有这样,写操作才有可能是可靠的。如果系统可以接受一定概率的数据丢失(如主分片写入成功,副本还没写入成功的时候,主机断电。由于数据既没有刷到Lucene,translog也没有刷盘,恢复时translog中没有这个数据,数据丢失),则调整translog持久化策略为周期性和一定大小的时候”flush“,例如:
    index.translog.durability:async

    设置为async表示translog的刷盘策略按sync_interval配置指定的事件周期进行。

    index.translog.sync_interval:120s

    加大translog刷盘间隔时间。默认为5s,不可低于100ms

    index.translog.flush_threshold_size:1024mb
    超过这个大小会导致refresh操作,产生新的Lucene分段。默认值为512MB。

    1.2 索引刷新间隔refresh_interval

    默认情况下索引的refresh_interval为1秒,这意味着数据写1秒后就可以被搜索到,每次索引的refresh会产生一个新的Lucene段,这会导致频繁的segment merge行为,如果不需要这么高的搜索实时性,应该降低索引refresh周期,例如:
    index.refresh_interval: 30s

    1.3 indexing buffer

    indexing buffer在为doc建立索引时使用,当缓冲满时会刷入磁盘,生成一个新的segment,这是除refresh_interval刷新索引外,另一个生成新segment的机会。每个shard有自己的indexing buffer,下面的这个buffer大小的配置需要除以这个节点上所有shard的数量:

    indices.memory.index_buffer_size
    默认为整个堆空间的10%

    iindices.memory.min_index_buffer_size
    默认为48MB

    indices.memory.max_index_buffer_size
    默认为无限制

    在执行大量的索引操作时,indices.memory.index_buffer_size的默认设置可能不够,这和可用堆内存、单节点上的shard数量相关,可以考虑适当增大该值。

    1.4 使用bulk请求

    批量写比一个索引请求只写单个文档的效率高得多,但是要注意bulk请求的整体字节数不要太大,太大的请求可能会给集群带来内存压力,因此每个请求最好避免超过几十兆字节,即使较大的请求看上去执行得更好。

    1.5 索引过程调整和优化

    1.5.1 自动生成doc ID

    通过ES写入流程可以看出,写入doc时如果外部指定了id,则ES会先尝试读取原来doc的版本号,以判断是否需要更新。这会涉及一次读取磁盘的操作,通过自动生成doc ID可以避免这个环节。

    1.5.2 调整字段Mappings

    1. 减少字段数量,对于不需要建立索引的字段,不写入ES;
    2. 将不需要建立索引的字段index属性设置为not_analyzed或no。对字段不分词,或者不索引,可以减少很多运算操作,降低CPU占用。尤其是binary类型,默认情况下占用CPU非常高,而这种类型进行分词通常没有什么意义;
    3. 减少字段内容长度,如果原始数据的大段内容无须全部建立索引,则可以尽量减少不必要的内容;
    4. 使用不同的分词器(analyzer),不同的分析器在索引过程中运算复杂度也有较大的差异。

    1.5.3 调整_source字段

    _source字段用于存储doc原始数据,对于部分不需要存储的字段,可以通过includes excludes过滤,或者将_source禁用,一般用于索引和数据分离。

    这样可以降低I/O的压力,不过实际场景中大多不会禁用_source,而即使过滤某些字段,对于写入速度的提升作用也不大,满负荷写入情况下,基本是CPU先跑满了,瓶颈在于CPU。

    1.5.4 禁用_all字段

    从ES 6.0开始,_all字段默认认为不启用,而在此前的版本中,_all字段默认是开启的。_all字段中包含所有字段分词后的关键词,作用是可以在搜索的时候不指定特定字段,从所有字段中检索。

    1.5.5 对Analyzed的字段禁用Norms

    Norms用于在搜索时计算doc的评分,如果不需要评分,则可以将其禁用:
    "title " : { "type" : "string" , "norms" : { "enabled" : false}}

    1.5.6 index_options设置

    index_options用于控制在建立倒排索引过程中,哪些内容会被添加到倒排索引,例如,doc数量、词频、positions、offsets等信息,优化这些设置可以一定程度降低索引过程中的运算任务,节省CPU占用率。
    不过在实际场景中,通常很难确定业务将来会不会用到这些信息,除非一开始方案就明确是这样设计的。

    二、搜索速度的优化

    2.1 为文件系统cache预留足够的内存

    在一般情况下,应用程序的读写都会被操作系统“cache”(除了direct方式),cache保存在系统物理内存中(线上应该禁用swap),命中cache可以降低对磁盘的直接访问率。搜索很依赖对系统cache的命中,如果某个请求需要从磁盘读取数据,则一定会产生相对较高的延迟。应该至少为系统cache预留一半的可用物理内存,更大的内存有更高的cache命中率。

    2.2 使用更快的硬件

    写入性能对CPU的性能更敏感,而搜索性能在一般情况下更多的是在于I/O能力,使用SSD会比旋转类存储介质好得多。尽量避免使用NFS等远程文件系统,如果NFS比本地存储慢3倍,则在搜索场景下响应速度可能会慢10倍左右。这可能是因为搜索请求有更多的随机访问。

    如果搜索类型属于计算比较多,则可以考虑使用更快的CPU。

    2.3 文档模型

    为了让搜索时的成本更低,文档应该合理建模。特别是应该避免join操作,嵌套(nested)会使查询慢几倍,父子(parent-child)关系可能使查询慢数百倍,因此,如果可以通过非规范化文档来回答相同的问题,则可以显著地提高搜索速度。

    2.4 字段映射

    有些字段的内容是数值,但并不意味着其总是应该被映射为数值类型,例如,一些标识符,将它们映射为keyword可能会比integer或long更好。

    2.5 避免使用脚本

    一般来说,应该避免使用脚本。如果一定要用,则应该优先考虑painless和expressions。

    2.6 为只读索引执行force-merge

    可以对旧数据,定时强制执行强制段合并,将Lucene索引合并为单个分段,可以提升查询速度。当一个Lucene索引不存在多个分段时,每个分段会单独执行搜索再将结果合并,将只读索引强制合并为Lucene分段不仅可以优化搜索过程,对索引恢复速度也有好处。

    2.7 尽量使用FilterContext

    尽量使用Filter Context,利用缓存机制,减少不必要的算分
    可以结合Profile,Explain API分析慢查询问题,持续优化数据建模。【严禁使用*开头通配符的Terms查询,会对集群造成比较大的损害】

    2.8 优化分片

    • 避免Over Sharing
      • 一个查询需要访问每一个分片,分片过多,会导致不必要的查询开销
    • 结合应用场景,控制单个分片的尺寸
      • 搜索场景:20GB以内
      • 日志场景:40GB以内
    • Force-merge Read-only索引
      • 使用基于时间序列的索引,将只读的索引force merge,减少segment数量

    参考资料:
    《Elasticsearch源码解析与优化实战》

  • 相关阅读:
    【IDEA】创建Maven工程
    【Java】Input,Output,Stream I/O流 03 系统标准流 & 打印流
    【Java】Reflection 反射机制 03调用
    【Java】JDBC Part2 工具类封装实现
    【郝斌C ST】 指针入门
    【Java】Input,Output,Stream I/O流 02 文件流 & 缓冲流
    margin和padding的区别
    JAVA反射机制
    HTTP错误代码详细介绍
    关于Java继承问题
  • 原文地址:https://www.cnblogs.com/deepSleeping/p/14566024.html
Copyright © 2020-2023  润新知