ElasticSearch学习总结(七):性能优化总结

本文主要总结Elasticsearch性能优化方面的相关内容

1. 概述

性能优化是个涉及面非常广的问题,不同的环境,不同的业务场景可能会存在不同的优化方案,本文只对一些相关的知识点做简单的总结,具体方案可以根据场景自行尝试。

1.1 性能测试

如果需要做性能调优,性能基准测试的工具必不可少,这里可以选择Rally

1.2 热点线程

当集群缓慢,使用大量的CPU资源时,可以使用热点线程API来查看资源都执行在了哪些地方,并可以查看资源消耗的情况。

可以通过已下方式查看热点线程
1. 全局的分析:/_nodes/hot_threads
2. 某个节点的分析:/_nodes/{nodesIds}/hot_threads

3. 常规配置

3.1 部署方案

  1. 当单机容纳不下数据时,考虑多分片
  2. 当查询性能不足时,考虑多副本。
  3. 对于一些高性能的服务器,可以考虑一台服务器上部署多个实例
  4. 通过awarenss的相关配置,阻止分片及其副本部署在同一台机器上。
  5. 尽量平均分配分片和副本。
  6. 当集群较大时,考虑设计每个节点的角色。例如 节点只作为查询聚合节点,数据节点,或是主/候选主节点

3.2 避免内存交换

内存交换是指把内存页写入磁盘的过程,一般发生在物理内存不够或是某些情况下操作系统认为应该发生的时候发生。如果交换了的内存页再次被需要,操作系统会从交换区加载到内存,该过程相对于内存操作而言是是比较慢的。

为了保证ES的高效,在ES中应该避免内存交换的发生,如果达到此目的,需要进行如下配置:
1. 设置elasticserach.yml文件中的bootstrap.mlockall=true
2. 设置Xms与Xmx的值相同
3. 在/etc/security/limits.conf中添加elasticsearch -memlock unlimited
4. 在/etc/pam.d/common-session中添加session required pam_limits.so
5. 重启es

3.3 采用G1垃圾回收机制代替默认CMS

3.4 索引刷新频率(refresh_interval)

该参数基本遵循如下规律:
1. 刷新越快,数据更新越快,但是查询与索引的效率越低
2. 虽然增加该值,可以一定程度的提升效率,但是超过一定数值后,提升的效果将微乎其微。

3.5 线程池调优

如果你发现ES实例的资源没有100%饱和,但却受到了拒绝执行的错误,此时可能就需要调整ES的线程池了。可以尝试增加并发的线程数或是增加队列的长度。在调整的过程中需要注意的是,当并发线程数到一个很大的数值时,会产生大量的CPU上下文切换,进而导致性能下降。大量的队列也可能会出现队列大量积压的情况。

3.6 调整段合并过程

一般情况下,如果希望查询的速度更快,就需要更少的段。例如设置index.merge.policy.merge_factory低于默认值10,会导致更少的段,更低的RAM消耗,更快的查询执行速度但是会出现更慢的索引速度。如果设置的index.merge.policy.merge_factory较高则会出现相反的情况。

另外需要注意的是,默认情况下ES会限制合并的速度在20MB/s.如果使用的是固态硬盘或是I/O效率更高的设备,则可以适当的增加限制的速度。

4. 高查询频率下的优化

本章节主要针对高查询吞吐量场景的优化方案进行总结

4.1 查询缓存

分片查询缓存的主要目的是缓存聚合,提示词和命中数(不会缓存返回的文档)

如果想要开启mastering索引的查询缓存,可以执行类似下面的操作

PUT /mastering/_settings
{ "index.requests.cache.enable": true }

查询缓存默认使用节点堆栈的1%内存,可以通过下列方式对该值进行设置:

indices.requests.cache.size: 2%

4.2 使用doc_values优化查询

缓存有时候可以带来性能的显著提高,但是对于某些场景缓存可能不是万能的,例如:
1. 文档频繁更新
2. 查询具有唯一性,且不可重复性,例如带了时间或是id左右查询条件

对于缓存需要容纳全部数据的场景(例如,排序,聚合 等操作时),如果拥有大量的文档,很容易碰到OOM的问题,此时可以考虑使用doc_values的特性。

在 Elasticsearch 中,Doc Values 就是一种列式存储结构,默认情况下每个字段的 Doc Values 都是启用的,Doc Values 是在索引时创建的,当字段索引时,Elasticsearch 为了能够快速检索,会把字段的值加入倒排索引中,同时它也会存储该字段的 Doc Values

Elasticsearch 中的 Doc Values 常被应用到以下场景:

  1. 对一个字段进行排序
  2. 对一个字段进行聚合
  3. 某些过滤,比如地理位置过滤
  4. 某些与字段相关的脚本计算
    因为文档值被序列化到磁盘,我们可以依靠操作系统的帮助来快速访问。当 working set 远小于节点的可用内存,系统会自动将所有的文档值保存在内存中,使得其读写十分高速; 当其远大于可用内存,操作系统会自动把 Doc Values 加载到系统的页缓存中,从而避免了 jvm 堆内存溢出异常。

因为 Doc Values 默认启用,你可以选择对你数据集里面的大多数字段进行聚合和排序操作。但是如果你知道你永远也不会对某些字段进行聚合、排序或是使用脚本操作,可以禁用特定字段的 Doc Values 。这样不仅节省磁盘空间,也许会提升索引的速度。 例子如下:

PUT my_index
{
  "mappings": {
    "my_type": {
      "properties": {
        "session_id": {
          "type":       "string",
          "index":      "not_analyzed",
          "doc_values": false 
        }
      }
    }
  }
}

4.3 尽量使用过滤器

由于过滤器执行的过程中不会涉及评分的操作以及过滤器缓存的缘故,所以查询应该优先考虑使用过滤器(例如,对于静态的,不分词的字段尽量使用过滤器)。

尽量使用路由

有着相同路路由值的数据会被保存到相同的分片上,于是在查询时就可以将请求发送到指定的分片上,可以避免合并结果的开销。
因此应该尽量使用路由

4.4 字段数据缓存和断路

字段数据缓存主要用于在字段上排序或计算聚合时使用。它将所有字段值加载到内存中,以提供基于快速的基于这些值的操作。

Elasticsearch的字段数据缓存默认是没有大小限制的,尤其是当在很多字段上进行分组和排序的时候。如果这些字段的基础很高,很容易出现OOM.

为此可以采取以下措施:
1. 限制字段缓冲区的大小:indices.fielddata.cache.size
2. 使用断路器( Field data circuit breaker):配置断路器后可以在满足某些条件下抛出异常而不是OOM

如果查询大量使用了字段数据缓存(聚合和排序),且频繁的存在内存的问题,可以考虑使用doc_values进行替换。

4.5 控制size和shard_size

  • size:参数定义最后聚合结果会返回多少组数据给客户端。
  • shard_size:和size类似,其主要作用在分片上

上述两个值的增加会让聚合结果更加精准,同事也会消耗过多的资源。降低这两个值会降低精准度,但是会减少资源损耗。

5. 写数据

5.1 批量索引

如果有大量数据需要索引,可以使用批量索引取代逐个文档的索引。

因为处理批量索引的时候是在批量数据处理线程池中执行的,所以批次的量也不能太大,否则会ES也会消耗过多的内存来处理这些数据。

5.2 doc_values 与索引速度的取舍

ES为了实现排序,聚合或是分组操作需要反转字段,需要巨大的内存,doc_values可以解决这些问题。

但是在索引是doc_values也会产生一些额外的消耗,因此:
1. 如果不涉及排序,聚合或是分组操作,且对索引的吞吐量较大,可以考虑关闭doc_values
2. 如果海量数据的涉及排序,聚合或是分组操作,doc_values或许必不可少。

5.3 控制文档的字段

文档的大小在一定程度上会影响索引的速度,可以采取如下方式对大小进行控制
1. _all字段在很多场景下可能不会被使用,可以选择性的对其进行关闭。
如果关闭了可以同时指定一个默认的查询字段

curl -XPUT 'localhost:9200/my_index?pretty' -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "my_type": {
      "_all": {
        "enabled": false 
      },
      "properties": {
        "content": {
          "type": "text"
        }
      }
    }
  },
  "settings": {
    "index.query.default_field": "content" 
  }
}
'
  1. 尽量减少文档的大小和文本字段的数量。

5.4 考虑分片以及副本的数量

过多的副本会增加复制与传输的开销

5.5 控制refresh与flush

对于需要进行大批量数据写入,且在写入过程中不需要查询的场景,可以采取以下优化手段

  • 修改translog的flush方式

默认情况下每次对索引的操作都会出发一次tranlog的flush操作,对于大批量数据写入的场景,可以先改成定期flush,下列配置会根据index.translog.sync_interval的配置进行定期flush,等导完数据后,再恢复正常值

PUT test_index/_settings
{
  "index":{
    "translog.durability":"async"
  }
}
  • 修改refresh的时间

Searcher会更具refresh_interval的配置定期的更新索引,在大量数据导入的情况下,可以先将该值改为-1,等导完数据后,再恢复正常值

PUT test_index/_settings
{
  "index":{
    "refresh_interval":"-1"
  }
}

5.6 索引期间的内存缓存

增加节点的索引缓存大小(indices.memory.index_buffer_size,默认使用10%),也将有利于索引吞吐量的增加。

该配置主要是针对节点的,例如节点有20GB的内存,且有10十个分片,则每个分片大约分的200MB的内存作为索引缓存(20GB*10%/10=200MB)

猜你喜欢

转载自blog.csdn.net/sun7545526/article/details/79458416