当前位置: 首页 > article >正文

Elasticsearch优化汇总

文章目录

  • 引言
  • 硬件设置优化
    • 禁用swap
    • 给系统留足够的内存
    • JVM配置
    • 使用更快的硬件,使用 SSD
  • 参数优化
    • 分片设计
    • 增加Buffer大小(增加吞吐量)
    • 预热文件系统 cache
  • es查询设计优化
    • 预索引数据
    • 使用filter代替query
    • 字段映射
    • 避免使用脚本
    • 优化日期搜索
    • 为只读索引执行 force-merge
    • 预热全局序号(global ordinals)
    • 深度优先还是广度优先
    • 限制搜索请求的分片数
    • 利用自适应副本选择(ARS)提升 ES响应速度
    • 调节搜索请求中的 batched\_reduce\_size
  • 慢日志设置、跟踪
    • 慢日志设置
    • 查询分析工具
    • IO分析
    • 安装 `iotop`
      • 基本用法
      • 常用选项
      • 示例
      • 注意事项
  • 生产必要关注配置
    • 数据、日志路径设置(重要)
    • 集群名称
    • 节点名称
    • 主机网络
    • 发现设置
    • 堆大小
    • 堆储存路径
    • GC 日志
    • 临时文件
  • dokcer容器资源控制
  • 推荐Java开发快速上手es集成框架
  • 遗留问题

引言

从多个维度:硬件、软件参数、查询等,收集的官方以及互联网上的别人实战的博客进行汇总。在实际中官方其实已经给了很多优化建议,很多人可能没去读过,建议过一遍官方文档。

硬件设置优化

禁用swap

大多数操作系统尝试将尽可能多的内存用于文件系统缓存,并急切地换掉未使用的应用程序内存。这可能导致部分 JVM 堆甚至其可执行页面被换出到磁盘。

查看当前的交换使用情况:

sudo swapon --show

关闭所有交换空间:

sudo swapoff -a

永久禁用交换:

编辑/etc/fstab文件,注释掉所有与swap相关的行。
创建一个新文件/etc/sysctl.d/99-elasticsearch.conf,并添加以下内容:

vm.swappiness = 1

应用更改:

sudo sysctl --system

给系统留足够的内存

Lucene的数据的fsync是发生在OS cache的,要给OS cache预留足够的内存大小。
搜索操作很依赖对系统cache的命中,标准的建议是把50%的可用内存作为ES的堆内存,为Lucene 保留剩下的50%,用作系统 cache。

export ES_HEAP_SIZE=10g

或者启动命令行参数设置

./bin/elasticsearch -Xmx10g -Xms10g 

参考官方文档:堆内存:大小和交换

JVM配置

ES 不建议为 JVM 配置超过 32GB 的内存,超过 32GB 时,Java内存指针压缩失效,浪费一些内存,降低了CPU性能,GC压力也较大。因此推荐设置为31GB:

-Xmx31g -Xms3lg

参考博客:ElasticSearch CPU和内存占用高的优化记录

使用更快的硬件,使用 SSD

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

参数优化

一次看完28个关于ES的性能调优技巧,很赞,值得收藏

分片设计

初始依据:为避免庞大的分片问题,一个节点可以容纳的分片数跟可用空间成正比。通常情况下,每 1GB 的分片数最好少于 20 个。设置的分片数量越少,效果就越好。

对于高效搜索(热数据),建议“内存-磁盘”比率在1:30为佳,也就是60GB内存可以支撑的检索数据量约1.8TB。对于温数据或冷数据则不受此限制,甚至可以拓展到1:180。

1. 将单个分片存储存储索引数据的大小控制在20G左右;绝对不要超过50G , 否则性能很差 最终分片数量 = 数据总量/20G

number_of_shards
每个索引的主分片数,默认值是 5 。这个配置在索引创建后不能修改

number_of_replicas
每个主分片的副本数,默认值是 1 。对于活动的索引库,这个配置可以随时修改

{
	"settings":{                
		"number_of_shards":3,    
		"number_of_replicas":1   
	},
	"mappings":{
		...
	}
}

使用前需要确定大概的数据量设置分片数量,过多分片也会导致查询缓慢,需要迁移参考下面文章

关于ES分片设置的6个建议
Elasticsearch 使用误区之三——分片设置不合理

增加Buffer大小(增加吞吐量)

本质也是减小refresh的时间间隔,因为导致segment文件创建的原因不仅有时间阈值,还有buffer空间大小,写满了也会创建。 默认最小值 48MB< 默认值 JVM 空间的10% < 默认最大无限制

indices.memory.index_buffer_size: 20% # 默认是10%

index_buffer_size的大小直接影响到何时触发一个新的segment文件的创建。当buffer被填满时,会触发refresh操作,产生新的Lucene segment。
增加buffer大小可以减少因buffer满而触发的refresh操作,从而减少segment的创建和后续的merge操作。

些用户认为这个buffer越大吞吐量越高,因此见过有用户将其设置为40%的。到了极端的情况,写入速度很高的时候,40%都被占用,导致OOM。

Elasticesearch内存详解(四)——indexing buffer

预热文件系统 cache

如果ES主机重启,则文件系统缓存将为空,此时搜索会比较慢。可以使用index.store.preload设置,通过指定文件扩展名,显式地告诉操作系统应该将哪些文件加载到内存中,
例如,配置到elasticsearch.yml文件中:
index.store.preload:["nvd","dvd"]
或者在索引创建时设置:
PUT /my index "settings":{ "index.store.preload":["nvd","dvd"] } }
如果文件系统缓存不够大,则无法保存所有数据,那么为太多文件预加载数据到文件系统缓存中会使搜索速度变慢,应谨慎使用。

如果文件系统缓存不够大,无法保存所有数据,那么为太多文件预加载数据到文件系统缓存中可能会适得其反,导致搜索速度变慢。这是因为:

缓存置换策略:当文件系统缓存满了之后,操作系统会使用某种缓存置换策略(如最近最少使用(LRU)算法)来决定哪些数据应该被置换出去。如果预加载了太多文件,那么这些文件可能会很快被置换出缓存,导致实际需要的数据不在缓存中,从而增加了磁盘I/O,降低了搜索性能。

内存资源竞争:文件系统缓存是操作系统管理的内存资源,如果预加载了太多文件,可能会占用过多的内存资源,影响系统的其他部分,如ES的堆内存和JVM性能,从而影响整体性能。

磁盘I/O增加:如果预加载的文件太多,那么在文件系统缓存中置换出去的文件可能会频繁地被重新加载,这会增加磁盘I/O,降低性能。

es查询设计优化

预索引数据

还可以针对某些查询的模式来优化数据的索引方式。

例如,如果所有文档都有一个price字段,并且大多数査询在一个固定的范围上运行range聚合,那么可以通过将范围“pre-indexing到索引中并使用terms聚合来加快聚合速度。(新增字段做索引)

查询范围为1-10,新建一个字段为1-10直接全匹配即可

使用filter代替query

query和filter的主要区别在: filter是结果导向的而query是过程导向。query倾向于“当前文档和查询的语句的相关度”而filter倾向于“当前文档和查询的条件是不是相符”。即在查询过程中,query是要对查询的每个结果计算相关性得分的,而filter不会。另外filter有相应的缓存机制,可以提高查询效率。

字段映射

  1. 显式设置映射(Explicit Mapping)

    • 显式设置映射可以帮助确保字段类型准确,优化存储和性能,并避免不必要的映射更新。例如,对于不需要分词的字段,应使用keyword类型,而对于需要分词的字段,则使用text类型。这样可以确保查询和聚合操作的正确性,并降低存储空间,提高查询性能。
  2. 使用合适的字段类型

    • 选择合适的字段类型对于优化搜索性能和准确性至关重要。例如,对于文本字段,应使用textkeyword类型;对于数字类型的数据,可以选择integerfloatscaled_float类型;对于日期和时间,使用date类型;对于唯一标识符,使用keyword类型。
  3. null_value参数

    • 使用null_value参数为字段指定一个默认值,当字段在文档中为null或缺失时,Elasticsearch将使用这个默认值。这对于在索引时处理空值非常有用,可以确保查询和聚合的一致性。
  4. 避免使用嵌套(Nested)类型

    • 嵌套类型虽然在某些场景下很有用,但它们也带来了一定的性能影响。查询嵌套字段速度较慢,并且需要额外的处理步骤。为了避免嵌套类型带来的性能影响,可以考虑将嵌套字段转换为扁平化的数据结构。
  5. 优化存储和性能

    • 通过显式设置映射,可以降低存储空间,提高查询性能。例如,对于不需要doc_values的字段,可以禁用doc_values以减少磁盘使用,但要注意这可能会影响性能。
  6. 自定义分析器

    • 在创建索引时,可以设置自定义分析器,用于索引时处理文本。这样可以更精确地控制文本的分词和搜索行为,提高搜索的相关性和性能。
  7. 字段数据(Fielddata)

    • 对于需要排序和聚合的字段,启用fielddata。但要注意,这可能会消耗大量内存,因此需要根据实际情况权衡内存使用和性能需求。
  8. 动态映射(Dynamic Mapping)

    • 动态映射允许Elasticsearch自动实现每个字段的类型检测,并进行映射设置。但为了更好的性能和控制,建议关闭动态映射,显式定义字段类型和属性。

避免使用脚本

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

优化日期搜索

在使用日期范围检索时,使用now的查询通常不能缓存,因为匹配到的范围一直在变化。但是,从用户体验的角度来看,切换到一个完整的日期通常是可以接受的,这样可以更好地利用查询缓存。(将时间范围精确到分钟或者更多(过大影响使用,根据业务调整))
举例:

GET index/ search
"query":{
    "constant score": {
        "filter": {
            "range": {
                "my_date": {
                    "gte": "now-lh",
                    "lte": "now"
                }
            }
        }
    }
}

修改为:

GET index/search

"query":{
    "constant score": {
        "filter": {
            "range": {
                "my_date": {
                    "gte": "now-lh/m",
                    "lte": "now/m"
                }
            }
        }
    }
}

为只读索引执行 force-merge

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

基于日期进行轮询的索引的旧数据一般都不会再更新。此前的章节中说过,应该避免持续地写一个固定的索引,直到它巨大无比,而应该按一定的策略,例如,每天生成一个新的索引,然后用别名关联,或者使用索引通配符。这样,可以每天选一个时间点对昨天的索引执行force-merge、Shrink等操作。

预热全局序号(global ordinals)

全局序号(Global Ordinals)是Elasticsearch中用于提高聚合查询性能的一种数据结构。它主要用于在keyword字段上运行terms聚合。以下是对全局序号的详细解释:

1. **全局序号的基本概念**:
   - 全局序号通过为字段中的每个唯一字符串值分配一个数值(ordinal)来工作,这个数值是基于字符串的字典顺序分配的。
   - 在聚合操作中,Elasticsearch使用这些ordinals来收集文档到bucket中,而不是直接使用原始的字符串值。

2. **构建过程**:
   - 每个索引段(segment)定义了自己的ordinal映射,但是聚合操作需要跨整个分片(shard)收集数据。为了在分片级别使用ordinals进行聚合操作,Elasticsearch创建了一个统一的映射,称为全局序号(global ordinals)。
   - 全局序号是基于段(segment)序号构建的,通过维护一个从全局序号到每个段的局部序号的映射来工作。

3. **加载全局序号**:
   - 默认情况下,全局序号映射在搜索时首次需要时加载。如果优化索引速度是主要考虑因素,这是一种合适的方法。但如果搜索性能是优先考虑的,建议在聚合字段上预先加载全局序号。
   - 可以通过配置映射来告诉Elasticsearch在刷新(refresh)时预先加载全局序号。这可以通过设置`eager_global_ordinals`为`true`来实现。
   - 当启用`eager_global_ordinals`时,全局序号会在分片刷新时构建,Elasticsearch总是在暴露索引内容变化之前加载它们。这将构建全局序号的成本从搜索阶段转移到索引时间。

4. **对性能的影响**:
   - 启用`eager_global_ordinals`可以提高搜索性能,因为它减少了在搜索时构建全局序号的需要。然而,这也意味着每次执行刷新操作时都会构建全局序号,这可能会影响写入性能。
   - 为了减少由于频繁刷新而建立全局序号导致的额外开销,可以增加索引的刷新间隔(`refresh_interval`)。

5. **配置示例**:
   - 以下是如何在创建索引时启用`eager_global_ordinals`的示例:
     ```json
     PUT my-index-000001/_mapping
     {
       "properties": {
         "tags": {
           "type": "keyword",
           "eager_global_ordinals": true
         }
       }
     }
     ```
   - 如果需要禁用`eager_global_ordinals`,可以更新设置:
     ```json
     PUT my-index-000001/_mapping
     {
       "properties": {
         "tags": {
           "type": "keyword",
           "eager_global_ordinals": false
         }
       }
     }
     ```
   - 调整刷新频率的示例:
     ```json
     PUT my-index-000001/_settings
     {
       "index": {
         "refresh_interval": "30s"
       }
     }
     ```
   - 这将构建全局序号的成本从搜索阶段转移到了数据索引化(写入)阶段,是一种以空间换时间的策略。

全局序号是一种优化聚合查询性能的数据结构,通过预先加载全局序号,可以显著提高搜索速度,尤其是在高基数字段上运行`terms`聚合时。然而,这也可能会影响写入性能,因此需要根据具体的使用场景来权衡是否启用`eager_global_ordinals`。

深度优先还是广度优先

ES有两种不同的聚合方式:深度优先和广度优先。深度优先是默认设置,先构建完整的树,然后修剪无用节点。大多数情况下深度聚合都能正常工作,但是有些特殊的场景更适合广度优先,先执行第一层聚合,再继续下一层聚合之前会先做修剪。

根据不同情况优化

官方有一个例子可以参考

限制搜索请求的分片数

一个搜索请求涉及的分片数量越多,协调节点的CPU和内存压力就越大。默认情况下,ES会拒绝超过1000个分片的搜索请求。

我们应该更好地组织数据,让搜索请求的分片数更少。如果想调节这个值,则可以通过 action.search.shard count配置项进行修改。

虽然限制搜索的分片数并不能直接提升单个搜索请求的速度,但协调节点的压力会间接影响搜索速度,例如,占用更多内存会产生更多的GC压力,可能导致更多的stop-the-world 时间等,因此间接影响了协调节点的性能,所以我们仍把它列作本章的一部分。

对整个集群调整max_concurrent_shard_requests参数

PUT /_cluster/settings
{
    "persistent" : {
        "max_concurrent_shard_requests : "24"
    }
}

参考博客:在单个节点的查询并发限制是多少?增加分片一定会提高聚合/查询速度吗

利用自适应副本选择(ARS)提升 ES响应速度

为了充分利用计算资源和负载均衡,协调节点将搜索请求轮询转发到分片的每个副本,轮询策略是负载均衡过程中最简单的策略,任何一个负载均衡器都具备这种基础的策略,缺点是不考虑后端实际系统压力和健康水平。
ARS从6.1版本开始支持,但是默认关闭,可以通过下面的命令动态开启,从ES7.0开始,ARS将默认开启。

PUT /cluster/settings

{
"transient":{
    "cluster.routing.use adaptive replica selection": true
    }
}

调节搜索请求中的 batched_reduce_size

该字段是搜索请求中的一个参数。默认情况下,聚合操作在协调节点需要等所有的分片都取回结果后才执行,使用batchedreduce size 参数可以不等待全部分片返回结果,而是在指定数量的分片返回结果之后就可以先处理一部分(reduce)。这样可以避免协调节点在等待全部结果的过程中占用大量内存,避免极端情况下可能导致的OOM。该字段的默认值为512,从ES 5.4 开始支持。

{
    "size": 0,
    "aggs":{
        "test_aggs":{
            "terms":{
                "target_col": "target_file"
            },
            "meta":{
                "batched_reduce_size": 10
            }
        }
        
    }
    
}

慢日志设置、跟踪

慢日志设置

查询日志需要单独设置

参考文章:Elasticsearch 日志能否把全部请求打印出来?

日志记录

设置打印日志查询:

  1. 设置日志等级
-- 这里只设置了搜索日志等级,其余参考文档

put http://xxxx:9200/_cluster/settings

{
    "transient" : {
        "logger.index.search.slowlog" : "DEBUG", 
    }
}
  1. 设置slowlog的debug等级的阈值
-- 这里只设置了搜索日志等级,其余参考文档

put http://xxxxx:9200/索引名称/_settings

{
  "index.search.slowlog.threshold.query.debug": "0s"
}

索引慢日志和查询慢日志,根据不同情况设置

Elasticsearch:Elasticsearch 中的慢日志

查询分析工具

深入解密 Elasticsearch 查询优化:巧用 Profile 工具/API 提升性能

IO分析

查看磁盘情况:

iostat -xd 1
  • iops,由r/s(每秒读次数)和w/s(每秒写次数)组成。
  • await,平均 IO 等待时间,包括硬件处理 I0的时间和在队列中的等待时间。
  • %util,设备的繁忙比,是设备执行的I0时间与所经过的时间百分比。当值接近100%时设备产生饱和。在设备具有并行处理能力的情况下,utl达到100%不代表设备没有余力处理更多 I/O 请求。

iotop 是一个用于监视 Linux 系统中磁盘 I/O 使用情况的命令行工具,类似于 top,但专注于显示实时的磁盘读写活动。以下是 iotop 的基本使用方法和一些常用选项:

安装 iotop

在大多数 Linux 发行版中,iotop 可能不是预装的。可以使用包管理器来安装它:

  • 对于基于 Debian 的系统(如 Ubuntu):
    sudo apt-get install iotop
    
  • 对于基于 RPM 的系统(如 Fedora):
    sudo dnf install iotop
    
  • 对于 CentOS/RHEL:
    sudo yum install iotop
    

基本用法

要使用 iotop 监视每个正在运行的进程的磁盘使用情况,可以直接在命令行中输入:

sudo iotop

这将打开一个交互窗口,显示当前所有进程的磁盘 I/O 使用情况。

常用选项

  • -o--only:只显示正在执行 I/O 操作的进程。
  • -b:以非交互模式运行,适合记录日志。
  • -n NUM:设置刷新次数。
  • -d SEC:设置刷新间隔(秒)。
  • -p PID:只监视指定进程的 I/O。
  • -u USER:只显示指定用户的进程的 I/O。
  • -a:显示累积 I/O,而不是实时速率。
  • -k:以千字节(KB)为单位显示数据大小。
  • -t:显示时间戳。

示例

  1. 仅显示正在进行 I/O 操作的进程:
    sudo iotop -o
    
  2. 以非交互模式运行并刷新 5 次,每次间隔 5 秒:
    sudo iotop -b -n 5 -d 5
    
  3. 监视特定用户的 I/O:
    sudo iotop -u username
    

注意事项

  • iotop 通常需要以 root 权限运行,以便查看所有进程的 I/O 活动。
  • 确保系统已加载必要的内核模块,如 CONFIG_TASK_DELAY_ACCTCONFIG_TASK_IO_ACCOUNTING,否则 iotop 可能无法正常工作。
  • 频繁刷新可能会对系统性能产生影响,特别是在繁忙的系统上。

iotop 是一个非常有用的工具,可以帮助系统管理员快速识别导致高磁盘 I/O 的进程,从而进行相应的优化和调整。

生产必要关注配置

官方建议关注

数据、日志路径设置(重要)

data 和 logs 目录在都在 $ES_HOME 下。如果这些比较重要的文件夹保留在默认位置,则 Elasticsearch 升级到新版本时,很有可能被删除。

在生产环境中,你可以通过下面这种方式修改数据和日志文件所在的位置:

path:
  logs: /var/log/elasticsearch
  data: /var/data/elasticsearch

RPM 和 Debian 发行版已经为 data 和 logs 使用了自定义路径。

path.data 可以设置为多个路径,在这种情况下,所有路径将用于存储数据 (属于单个分片的文件将全部存储在同一数据路径上):

path:
  data:
  - /mnt/elasticsearch_1 
  - /mnt/elasticsearch_2 
  - /mnt/elasticsearch_3

集群名称

当节点与集群中的所有其他节点共享 cluster.name 时,该节点就能加入集群。
默认名称是elasticsearch,但是你应该将其更改为描述集群用途的适当名称。

cluster.name: logging-prod

确保不要在不同的环境中重复使用相同的集群名称,否则最终可能会导致节点加入错误的集群

节点名称

Elasticsearch 使用node.name作为 Elasticsearch 特定实例的可读标识符,因此它被包含在许多 API 的响应中。

它默认机器在 Elasticsearch 启动时的主机名,但可以在 elasticsearch.yml 中更改配置,如下所示:

node.name: prod-data-2

主机网络

发现设置

堆大小

重要,直接影响性能

设置位置参考

堆储存路径

Elasticsearch 配置 JVM 的默认配置,将内存不足异常时生成的 heap dump 文件保存到 data 目录 ( 当以 RPM 和 Debian 的方式安装时,在/var/lib/elasticsearch 目录下。 当以 tar 和 zip 压缩包的方式安装时,data 目录在 Elasticsearch 的根目录下)。

修改存储路径 heap dump 文件,你应该修改 jvm.options 的配置项 -XX:HeapDumpPath=...

GC 日志

默认情况下,Elasticsearch 启用 GC 日志。这些是在jvm.options中配置的,默认为与 Elasticsearch 日志相同的默认位置

默认配置每 64 MB 轮换一次日志,最多可消耗 2 GB 磁盘空间。

临时文件

dokcer容器资源控制

目前是用docker管理es,所以记录一下docker的资源管理

Docker容器:docker的资源控制及docker数据管理

推荐Java开发快速上手es集成框架

推荐理由:新手友好,对于对es语法不熟悉的人来说非常简单,会mybatis plus就能看懂这套使用。

从功能上讲,对目前所有的es结构也有很好的支持,且支持原生的RestHighLevelClient查询(底层也是使用的RestHighLevelClient)

easy-es

遗留问题

因为现有业务是一个聚合数据的业务(大数据平台),从多个业务中需要筛选出有用的数据然后进行导出。

索引设计,目前是用的复杂的类型,全部表直接丢到索引的结构,非常暴力。

业务的复杂性,各个业务关联设计想要做出整合搜索,如何去设计索引目前我没看到比较好的方案或者思路(目前看到的基本都是针对单一业务),希望有朋友有相关经验可以评论一下


http://www.kler.cn/a/419665.html

相关文章:

  • jmeter 压测常用静默参数解释应用
  • 装饰器—购物打折
  • VSCode如何关闭Vite项目本地自启动
  • 行为型设计模式之《责任链模式》实践
  • 无分类编址的IPv4地址
  • 【C++】STL——map和set
  • phpmyadmin导出wordpress数据教程
  • 在 CentOS 上安装 Docker:构建容器化环境全攻略
  • 《Python基础》之正则表达式--re模块
  • 【QNX+Android虚拟化方案】124 - VLAN配置 之 QNXAndroid 修改简单记录
  • java原生反序列化利用链7u21的学习
  • CCNA_SEC 第五天作业
  • 一款适用于教育行业的免费word插件
  • Stable Diffusion介绍
  • ARM 嵌入式处理器内核与架构深度剖析:解锁底层技术逻辑
  • Java中Logger定义的三种方式
  • 多点DMALL启动招股:将在港交所上市,聚焦数字零售服务
  • 在anaconda中为jupyter安装Nbextensions扩展
  • C++初阶——动态内存管理
  • android studio引用so库
  • 【Linux】文件操作的艺术——从基础到精通
  • 深度学习-52-AI应用实战之基于Yolo8的目标检测自动标注
  • 剖析go协程池实现原理
  • (简单5步实现)部署本地AI大语言模型聊天系统:Chatbox AI + grok2.0大模型
  • 6.824/6.5840 Lab 2: Key/Value Server
  • 农业强国助农平台:科技赋能,助力乡村振兴