elasticsearch common command - yaokun123/php-wiki GitHub Wiki

一、磁盘篇

当客户端向 Elasticsearch 写入文档时候报错:cluster_block_exception [FORBIDDEN/12/index read-only / allow delete (api)]; 在 elasticsearch 的日志文件中报错如下:flood stage disk watermark [95%] exceeded ... all indices on this node will marked read-only

出现如上问题多半是:磁盘使用量超过警戒水位线,索引存在 read-only-allow-delete 索引块数据。

报错表明数据节点的磁盘空间严重不足,并且已达到磁盘洪泛警戒水位线(磁盘使用率95%+,洪水泛滥的意思)。

为防止磁盘变满,当节点达到洪泛警戒水位线时,Elasticsearch 会阻止向该节点的任何索引分片写入数据,如果该数据块影响到相关的系统索引,可能会导致 Kibana 或者其他 Elastic Stack 功能不可用。

1.1、基础认知:磁盘三个警戒水位线

属性名 属性值 含义
cluster.routing.allocation.disk.watermark.low 85% 低警戒水位线
cluster.routing.allocation.disk.watermark.high 90% 高警戒水位线
cluster.routing.allocation.disk.watermark.flood_stage 95% 洪泛警戒水位线

1.2、修复指南

寻找到无法分配的索引分片

GET _cat/shards?v=true
GET /_cat/shards?v&s=state:asc

查询分片未分配的理由

GET _cluster/allocation/explain?pretty  // 这个命令只会展示出一条无法分配索引的分片的信息,包括无法分配的理由
GET _cluster/allocation/explain
{
    "index": "my-index",
    "shard": 0,
    "primary": false,
    "current_node": "my-node"
}
index:对应索引
shard:分片号
primary:是否主分片
current_node:节点名称

恢复写入,可以上调磁盘警戒水位线

PUT _cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.disk.watermark.low": "90%",
    "cluster.routing.allocation.disk.watermark.high": "95%",
    "cluster.routing.allocation.disk.watermark.flood_stage": "97%"
  }
}

恢复写入,应急使用(本质上是破除磁盘洪泛警戒水位线 95% 的 index.blocks.read_only_allow_delete 的限制,让索引继续可以写入数据)

PUT */_settings?expand_wildcards=all
{
  "index.blocks.read_only_allow_delete": null
}

PUT _all/_settings
{
  "index.blocks.read_only_allow_delete":null
}

重新分配(简单重试)

POST /_cluster/reroute?retry_failed=true

查询恢复情况

GET _cat/recovery/indexName?v

长期解决方案

作为长期解决方案,我们建议您将节点添加到受影响的数据层或升级现有节点实现节点磁盘扩容以增加磁盘空间。

要释放额外的磁盘空间,你可以使用删除索引 API 删除不需要的索引。DELETE my-index

重置磁盘警戒水位线操作

当长期解决方案到位时,可使用如下命令行重置磁盘警戒水位线。

PUT _cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.disk.watermark.low": null,
    "cluster.routing.allocation.disk.watermark.high": null,
    "cluster.routing.allocation.disk.watermark.flood_stage": null
  }
}

二、CPU篇

2.1、诊断 Elasticsearch 高 CPU 使用率

核查 CPU 使用率

GET _cat/nodes?v=true&s=cpu:desc

核查热点线程

GET _nodes/my-node,my-other-node/hot_threads

2.2、降低 CPU 使用率的实操方案

1、扩展集群

繁重的数据写入(indexing)和搜索负载会耗尽较小的线程池。为了更好地处理繁重的工作负载,向集群添加更多节点或升级(扩容)现有节点以增加容量。

2、分散批量请求

批量请求虽然比单个请求效率更高,但大型批量写入或多搜索请求需要大量 CPU 资源。

如果可能,提交较小的请求并在它们之间留出更多时间。

3、取消长时间运行的搜索

长时间运行的搜索会阻塞搜索线程池中的线程。

// 要检查这些搜索,请使用任务管理 API。
GET _tasks?actions=*search&detailed   // 其中:running_time_in_nanos 显示搜索运行了多长时间

// 可以使用 _cancel API 取消任务以释放资源:
POST _tasks/oTUltX4IQMOUUVeiohTt8A:464/_cancel

4、避免耗费资源的搜索

1、避免脚本 script 检索
2、少使用:fuzzy、regexp、prefix、wildcard检索
3、避免将 range 检索应用到 text 和 keyword 类型。
4、避免多表关联 Join 类型
5、使用 index.max_result_window 索引设置降低大小限制
6、使用 search.max_buckets 集群设置降低允许的聚合桶的最大数量
7、使用 search.allow_expensive_queries 集群设置禁用耗费资源的查询

三、断路器篇

3.1、啥是断路器

断路器(circuit breakers)都指定了它可以使用内存的限制。

Elasticsearch 包含多个断路器,用于防止操作导致内存溢出错误(OutOfMemoryError)。

此外,还有一个父级断路器(parent-level breaker),规定了所有断路器可以使用的内存总量。

如果Elasticsearch估计某项操作会导致内存使用率超过断路器设置的上限,它会停止操作并返回错误。

默认情况下,父级断路器在 JVM 内存使用率达到 95% 时触发。为了防止错误,官方建议在使用率持续超过 85% 的情况下,采取措施减少内存压力。

3.2、Elasticsearch 断路器报错示例

客户端请求报 429 错误

如果一个请求触发了一个断路器,Elasticsearch会返回一个错误,其 HTTP 状态代码为429。

{
  'error': {
    'type': 'circuit_breaking_exception',
    'reason': '[parent] Data too large, data for [<http_request>] would be [123848638/118.1mb], which is larger than the limit of [123273216/117.5mb], real usage: [120182112/114.6mb], new bytes reserved: [3666526/3.4mb]',
    'bytes_wanted': 123848638,
    'bytes_limit': 123273216,
    'durability': 'TRANSIENT'
  },
  'status': 429
}

熟悉Http 协议的同学都知道:在HTTP协议中,响应状态码 429 Too Many Requests 表示在一定的时间内用户发送了太多的请求,即超出了“频次限制”。

日志报错 Data too large

elasticsearch.log 也会记录断路器错误。例如:分片的过程中会触发断路器。可能的报错如下:

Caused by: org.elasticsearch.common.breaker.CircuitBreakingException: [parent] Data too large, data for [<transport_request>] would be [num/numGB], which is larger than the limit of [num/numGB], usages [request=0/0b, fielddata=num/numKB, in_flight_requests=num/numGB, accounting=num/numGB]

3.3、检查JVM的内存使用情况

使用命令行查看 JVM 使用率

GET _cat/nodes?v=true&h=name,node*,heap*
GET _nodes/stats?filter_path=nodes.*.jvm.mem.pools.old          // 堆内存使用率为:used_in_bytes / max_in_bytes

获得每个断路器的 JVM 内存使用量

GET _nodes/stats/breaker

3.4、如何防止断路器出错

降低JVM的内存压力

高的 JVM 内存压力经常导致断路器错误。可能导致 JVM 使用率暴增的原因列举如下:

原因 1:分片大小设置不合理,存在过多小分片。因为每个分片都会有内存的使用。官方建议分片大小 30GB-50GB之间。
原因 2:复杂的检索或查询操作。举例:wildcard 查询、设置很大分桶数的聚合操作都是非常“吃”内存的,要避免。
原因 3:存在映射“爆炸”现象。定义太多的字段或将字段嵌套得太深,会导致使用大量内存的映射“爆炸”。
原因 4:存在大型批量请求。大型的批量索引或多重搜索请求会造成 JVM 的内存压力。
原因 5:节点硬件资源受限。物理内存本身就很小,这种是“硬伤”,为避免后患,需要整个团队知悉并想办法协调解决。

避免在 text 类型字段上使用 fielddata

本质原因:需要对 text 字段进行聚合操作,默认 text 是做分词操作的,无法实现聚合和排序,只有 fielddata:true 开启后才可以。
但,开启 fielddate:true 会使用大量的 JVM 内存。为了避免这种情况,建议 Elasticsearch 默认在文本字段上禁用 fielddata。
官方建议:如果你已经启用了 fielddata 并触发了 fielddata 断路器,请考虑禁用它并使用关键字字段 keyword 代替。

清除 fielddata 缓存

如果你已经触发了 fielddata 断路器并且不能禁用 fielddata,需要使用清除缓存 API 来清除 fielddata 缓存。清理缓存的命令如下:

POST _cache/clear?fielddata=true

如何检查 “429 拒绝请求”错误

GET /_cat/thread_pool?v=true&h=id,name,active,rejected,completed

四、其他

1、设置索引最大查询条数

PUT company_all/_settings
{
  "index":{
    "max_result_window":30000
  }
}

2、关闭近实时

PUT /company_all/_settings
{
  "index": {
    "refresh_interval": "60s" 
  }
}

3、修改副本分片

PUT /company_all/_settings
{
  "index": {
    "number_of_replicas": 0
  }
}

4、添加字段

PUT company_all_new/_mapping/_doc
{
  "properties":{
    "location":{
      "type": "geo_point"
    }
  }
}

5、获取索引mapping和settings

GET company_all_new/_mapping
GET company_all_new/_settings

6、打开或关闭索引以节约内存和CPU

POST company_all_new/_close
POST company_all_new/_open

7、别名

查看:
GET _cat/aliases?v
GET company_all_new/_alias
GET */_alias/company_es
GET company_all_new/_alias/*
设置:
PUT company_all_new/_alias/company_es(别名)
删除:
DELETE company_all_new/_alias/company_es

8、索引监控和信息

GET company_all_new/_stats
GET company_all_new/_segments

9、索引状态和管理

POST company_all_new/_cache/clear
POST company_all_new/_refresh
POST company_all_new/_flush
POST company_all_new/force merge
POST company_all_new/_upgrade?pretty&human

10、测试分词

GET /company_all_new/_analyze
{
  "analyzer": "ik_max_word",//aliws
  "text":"小哈有限公司"
}

11、手动移动分片

POST _cluster/reroute
{
  "commands": [
    {
      "move": {
        "index": "company_all_new",
        "shard": 0,
        "from_node": "node1",
        "to_node": "node2"
      }
    },
    {
      "allocate": {
        "index": "company_all_new",
        "shard": 1,
        "node": "node3"
      }
    }
  ]
}

12、聚合优化

// 启用eager_global_ordinals提升高基数(高区分度)聚合性能(将构建全局序号的成本从搜索阶段移到了数据索引化阶段,会影响写入性能,本质是以空间换时间)
PUT company_all_new/_mapping/_doc
{
  "properties":{
    "Tags":{
      "type": "keyword",
      "eager_global_ordinals": true
    }
  }
}

// 拆分聚合,使聚合并行化
msearch拆分聚合查询

// 将聚合中的查询条件移动到query子句部分

// 深度优先与广度优先
"collect_mode" : "breadth_first" //默认为深度优先,修改为广度优先

13、范围查询和排序优化

// Index sorting(索引排序)可用于在插入时对索引进行预排序,而不是在查询时再对索引进行排序,提高范围查询和排序操作的性能(会影响写入性能)
PUT my-index-000001
{
  "settings":{
    "sort.field":"cur_time",
    "sort.order":"desc"
  }
}

14、节点查询缓存(node query cache-LRU)节点查询缓存适用于Term 查询和 filter 查询

// 默认情况下,节点查询缓存最多可容纳10000个查询,最多占总堆空间的10%。
PUT my_index_0003
{
  "settings":{
    "indices.queries.cache.size":10%(默认)
    "index.queries.cache.enabled":false
  }
}

// 分片请求缓存(chard request cache)
当对一个索引或多个索引运行搜索请求时,每个涉及的分片都会在本地执行搜索并将其本地结果返回到协调节点,协调节点将这些分片级结果合并为一个“全局”结果集。
分片级请求缓存在每个分片上缓存本地结果,这使得频繁使用的搜索请求几乎立即返回结果。
分片请求缓存非常适合日志用例场景,在这种情况下,数据不会在旧索引上更新,并且可以将常规聚合保留在高速缓存中以供重用。
请求缓存将仅缓存 size = 0 的搜索请求的结果,因此将不缓存hits,但将缓存hits.total,aggregations(聚合)和suggestions。
大多数使用 now 的查询无法缓存。可以使用 clear_cache API手动使缓存过期
PUT my_index_0003
{
  "settings":{
    "indices.requests.cache.size":1%(默认)
    "index.requests.cache.enable":false
  }
}

// 监控:
GET _stats/request_cache?human
GET _node/stats/indices/request_cache?human

// Field data缓存
indices.breaker.fielddata.limit:堆内存40%。
indices.breaker.fielddata.overhead:默认:1.03

indices.fielddata.cache.size
监控:
GET _node/stats
GET _cat/fielddata


// 查询与清理
全局查看缓存方法:GET _cat/nodes?v&h=id,queryCacheMemory,queryCacheEvictions,requestCacheMemory,requestCacheHitCount,requestCacheMissCount,flushTotal,flushTotalTime
POST /twitter/_cache/clear?query=true
POST /twitter/_cache/clear?request=true
POST /twitter/_cache/clear?fielddata=true
POST /my_index_0003/_cache/clear
POST /_cache/clear

15、索引生命周期管理(ILM)

# 索引模版
PUT _template/user_click_log_template
{
  "index_patterns":["user-click-log-date-*"],
  "settings":{
    "number_of_shards":5,
    "number_of_replicas":1
  }
}
DELETE _template/user_click_log_template
GET _template
PUT user-click-log-date-2022-02-11
{
  "aliases": {
    "user-click-log-alias": {
      "is_write_index":true
    }
  }
}
# 获取生命周期
GET _ilm/policy/user_click_log_policy

16、删除老索引注意事项

1、删除旧索引前要先删除索引别名再重新建立目前水滴需要维护的索引别名为:company_es
DELETE /company_new_index/_alias/company_es
PUT /company_new_index_shuidi/_alias/company_es

17、重启集群需要注意事项

在我们重启机群的时候,会产生分片的迁移,导致大量资源被占用,重启变慢
禁止es进行自动索引分片分配cluster.routing.allocation.disable_allocation参数设置为true
1、停止增量脚本(10.8.8.24上supervisorctl控制)
2、分配分片设置-启用或禁用特定种类的分片的分配
PUT /_cluster/settings
{
 "transient" : {
 "cluster.routing.allocation.enable" : "none"
 }
}
//all - (默认值)允许为所有类型的分片分配分片。
//primaries - 仅允许分配主分片的分片。
//new_primaries -仅允许为新索引的主分片分配分片。 
//none - 任何索引都不允许任何类型的分配分片。
3、平衡分片设置-为特定类型的分片启用或禁用重新平衡(如果需要再平衡,这步可以省略)
PUT /_cluster/settings
{
 "transient" : {
 "cluster.routing.rebalance.enable" : "none"
 }
}
//验证一下:GET _cluster/settings?pretty
3、最好执行一次_flush
4、集群重启后再开启
PUT /_cluster/settings
{
 "transient" : {
 "cluster.routing.allocation.enable" : "all"
 }
}
PUT /_cluster/settings
{
 "transient" : {
 "cluster.routing.rebalance.enable" : "all"
 }
}

18、设置分片延迟分配策略

PUT _all/_settings
{
  "settings": {
    "index.unassigned.node_left.delayed_timeout": "5m"
  }
}

19、

/_cat/allocation
/_cat/shards
/_cat/shards/{index}
/_cat/master
/_cat/nodes
/_cat/indices
/_cat/indices/{index}
/_cat/segments
/_cat/segments/{index}
/_cat/count
/_cat/count/{index}
/_cat/recovery
/_cat/recovery/{index}
/_cat/health
/_cat/pending_tasks
/_cat/aliases
/_cat/aliases/{alias}
/_cat/thread_pool
/_cat/plugins
/_cat/fielddata
/_cat/fielddata/{fields}
/_cat/nodeattrs
/_cat/repositories
/_cat/snapshots/{repository}

> 每个命令都支持使用?v参数,来显示详细的信息(verbose)

> 每个命令都支持使用help参数,来输出可以显示的列(help)

> 通过h参数,可以指定输出的字段(headers)

> 很多的命令都支持返回可读性的大小数字,比如使用mb或者kb来表示






{
  "query": {
    "bool": {
      "filter": {
        "regexp": {
          "content": {
            "value": ".{1000,}"
          }
        }
      }
    }
  }
}


{
    "script" : {
        "script" : "doc['Digest'][0].length() == 32"
    }
}