腾讯云Elasticsearch集群运维常用命令详解三(索引篇)

2021-12-23 20:02:25 浏览数 (1)

在前两篇文章里,我们分别从集群和节点层面向大家介绍了日常集群运维工作中常用到的一些命令。接下来我们继续从索引层面来介绍几个常用到的集群运维API。

索引相关命令

1、查看集群索引基本信息

代码语言:javascript复制
GET _cat/indices?v

返回Response:

代码语言:javascript复制
health status index                           uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   wr_index_1                      cdI0seyjTF2tgV0qqSiFyg   3   1          2            0     13.9kb          6.9kb
green  open   hbase2es                        fK0TeN9zQemltXq3D29z7Q   1   1    1187578       100000    418.6mb        209.3mb
green  open   gcba0_202108                    eRkEpPP9RNeM30A7KBITzA  18   0          5            0     20.5kb         20.5kb
green  open   dcba0_202108                    q0Sq_wqEQc--K7fURhmmow  18   0          5            0     24.5kb         24.5kb

通常我们会使用该API来查看集群中有哪些索引,以及索引的主分片个数、容量大小、doc数量,是否设置了副本等。如果集群中索引比较多的话,那么该API返回的数据将非常多,不便于查看。这时候我们可以借助kibana或者cerebro等可视化界面来查看。另外该API还支持指定某个索引,如:

代码语言:javascript复制
GET _cat/indices/hbase2es?v

以及在Linux控制台通过grep来做一些条件筛选的操作,如过滤red的索引:

代码语言:javascript复制
GET _cat/indices?v | grep red

2、查看索引settings

代码语言:javascript复制
GET /{index_name}/_settings

返回Response:

代码语言:javascript复制
{
  "hbase2es" : {
    "settings" : {
      "index" : {
        "routing" : {
          "allocation" : {
            "include" : {
              "_tier_preference" : "data_content"
            }
          }
        },
        "number_of_shards" : "1",
        "translog" : {
          "sync_interval" : "5m",
          "durability" : "async"
        },
        "provided_name" : "hbase2es",
        "merge" : {
          "policy" : {
            "auto_merge_enabled" : "true",
            "inactive_merge_enabled" : "true"
          }
        },
        "creation_date" : "1630254239062",
        "number_of_replicas" : "1",
        "uuid" : "fK0TeN9zQemltXq3D29z7Q",
        "version" : {
          "created" : "7100199"
        }
      }
    }
  }
}

通过查看索引的settings信息,我们也能获取到索引的主分片个数以及设置的副本个数。不仅如此,我们还能直观的看到该索引创建的版本,创建时间,以及refresh_interval时间、translog的设置等。其实最主要的是可以通过该API能获取到对该索引设置的一些属性信息,如分片allocation的策略,是否关联了ILM策略,是否设置了只读、是否设置了自动merge等,通过该API的查看,对排查索引red问题有很好的参考意义。

3、设置索引settings

上一个命令中我们介绍了如何查看索引的settings,以及通过该API可以获取到很多有参考意义的信息。而如果我们需要设置索引settings的话,则需要使用下面的API:

代码语言:javascript复制
PUT /{index_name}/_settings

在该API中,我们可以设置很多索引的属性,下面重点介绍几个我们常用到的属性设置API:

1)设置索引副本数

代码语言:javascript复制
PUT /{index_name}/_settings
{
  "index": {
    "number_of_replicas": 0
  }
}

通过该API我们可以快速的对某一个、或一批设置索引索引进行去副本,或者打开副本操作。

2)解除索引只读设置

代码语言:javascript复制
PUT /{index_name}/_settings
{
  "index": {
    "blocks": {
      "read_only": "true"
    }
  }
}

索引被设置为只读有这么几种场景:第一种就是索引关联了ILM,在ILM的Action中被设置为了只读;第二种就是当该集群中某个节点的磁盘使用率超过水位线后,该节点上分配的所有索引都会被自动设置为只读。对于第二种的只读,这种策略是为了防止数据持续往索引中写入,导致集群稳定性被破环。遇到这种情况,业务上常常会看到某个索引的写入突然就掉零了。解决措施就是需要立马扩容磁盘容量。ES在7.x版本之后是会随着容量的释放自动解除索引只读设置的,但是7.x版本之前,则需要手工来执行解除只读API。但是如何快速查找到集群中哪些索引被设置为了只读呢,通过在kibana上执行下面的API来过滤哪些索引被设置为了只读索引:

代码语言:javascript复制
GET /_cluster/state/blocks/indices

返回Response:

代码语言:javascript复制
{
  "cluster_name" : "es-xxx",
  "cluster_uuid" : "2LLChSPGRgqZr1cz3b8cXw",
  "blocks" : {
    "indices" : {
      "wr_index_1" : {
        "5" : {
          "description" : "index read-only (api)",
          "retryable" : false,
          "levels" : [
            "write",
            "metadata_write"
          ]
        }
      }
    }
  }
}

另外,如果索引被设置了read_only后,我们对索引的任何settings设置都会失败,并且报如下错误。

代码语言:javascript复制
{
  "error" : {
    "root_cause" : [
      {
        "type" : "cluster_block_exception",
        "reason" : "index [wr_index_1] blocked by: [FORBIDDEN/5/index read-only (api)];"
      }
    ],
    "type" : "cluster_block_exception",
    "reason" : "index [wr_index_1] blocked by: [FORBIDDEN/5/index read-only (api)];"
  },
  "status" : 403
}

在这种状态下既不能删除索引,也不能关闭索引,更无法通过设置index.blocks.read_only:false 来恢复,那我们应该如何将索引恢复为可读写状态呢?遇到这种情况试试下面的API就ok了:

代码语言:javascript复制
PUT _settings
{
  "index": {
    "blocks": {
      "read_only": "false"
    }
  }
}

从我们大量的集群运维经验来看,有时候我们还可以手动将索引设置为禁读来解决一些集群性能问题。比如我们之前遇到过一个客户节点规模特别大的日志集群,写入大概在600w/s。然后运营同学持续的在查询数据直接把集群cpu和负载打的很高,导致大量的读写拒绝和熔断,集群快到崩溃的边缘。但是有不知道是哪些人在查询。因此也无法直接通知到相关人员停止操作。此时最快的止损方式就是立即对当前正在读的索引设置为禁读。可执行如下API:

代码语言:javascript复制
PUT /{index_name}/_settings
{
  "index.blocks.read": true
}

这时候再对相关索引查询时,就会报如下异常了:

代码语言:javascript复制
{
  "error" : {
    "root_cause" : [
      {
        "type" : "cluster_block_exception",
        "reason" : "index [wr_index_1] blocked by: [FORBIDDEN/7/index read (api)];"
      }
    ],
    "type" : "cluster_block_exception",
    "reason" : "index [wr_index_1] blocked by: [FORBIDDEN/7/index read (api)];"
  },
  "status" : 403
}

3)打开索引bulk_routing属性

代码语言:javascript复制
PUT /{index_name}/_settings
{
  "index.bulk_routing.enabled": true
}

打开索引的bulk_routing属性通常适用于日志分析和时序类、并且不指定doc_id写入以及查询时不指定doc_id进行查询的场景。打开该属性后,写入性能通常能有20%左右的提升,详情可参考腾讯云ES官方文档。

4)打开索引自动merge策略

代码语言:javascript复制
PUT /{index_name}/_settings
{
  "merge.policy.auto_merge_enabled":"true",
  "merge.policy.inactive_merge_enabled":"true"
}

开启该属性后,能够自动触发segment段合并。另外,也可以设置merge的最大线程数。

代码语言:javascript复制
PUT /{index_name}/_settings
{
    "index.merge.scheduler.max_thread_count": 2
}

5)设置索引分片分配策略

代码语言:javascript复制
PUT /{index_name}/_settings
 {
   "index.routing.allocation.include._tier_preference": "data_hot,data_warm,data_cold"
 }

7.x版本的集群引入了tier_preference特性,通过对节点data_roles进行定义data_hot,data_warm等属性来标记节点的类型。而在索引settings中指定相关的tier_preference,可以控制索引分片的分配策略。另外tier_preference如果设置的是多个值,如data_hot,data_warm,则优先会分配在data_hot节点上,如果集群中没有data_hot节点,或data_hot节点均不可用,则会尝试在data_warm节点上分配。

6)限制索引分片在节点上分配个数

代码语言:javascript复制
PUT /{index_name}/_settings
{
    "index": {
        "routing.allocation.total_shards_per_node": 2
    }
}

通过知道索引的完整数据是通过多个分片构成的,通过对每个节点上分配的分片进行限制,可以避免热点问题的出现。因为ES在分配分片的时候是优先将分片分配到磁盘空间较大、总分片个数较少的节点上。这种分配策略随着索引规划不合理以及集群规模的扩大,很容易出现同一个索引的多个分片分配在一个节点上,甚至部分节点上没有分配到分片的情况。而通过限制每个节点上分片分配的个数,则能够很好的规避这个问题,防止出现节点不均的情况。

7)索引关联ILM策略

代码语言:javascript复制
PUT /{index_name}/_settings
{
    "index.lifecycle.name": "{ilm_policy_name}"
}

很多日志分析类的集群随着集群数据量的上涨,都会尝试引入索引生命周期管理来动态维护集群。首先我们需要定义一个ILM的Policy,然后在索引模版中指定该Policy名称。这样新创建出来的索引就会自动被ILM进行管理,也就是说新索引自动关联上了定义好了的Policy了。但是对于没有定义Policy之前的索引,如果也想把这部分存量索引关联到Policy的话,则需要手动对存量索引进行关联。关联方法就是执行上面的API,在settings里面指定具体的Policy名称即可。详细的索引生命周期管理用法可以参考腾讯云Elasticsearch索引生命周期管理原理及实践。

8)修改索引压缩算法

代码语言:javascript复制
PUT /{index_name}/_settings
{
    "index.codec": "best_compression"
}

4、冻结/解冻索引

代码语言:javascript复制
POST /{index_name}/_freeze

冻结索引最大的特点就是该索引不再占用内存空间,只占用硬盘空间,但是依然能够被查询到,只不过查询的延时相对较高。从我们大量的集群运维经验来看,冻结索引这种操作通常是运用中快速缓解集群内存使用率高导致的熔断问题上。比如我们之前遇到过一个IoT的客户集群。他们集群中存储的是每一种传感器上报的指标数据,由于每个索引字段高达两三千,并且数据量非常大,导致集群的内存使用率经常飙到80%左右,频繁触发OldGC,并且造成大量的读写熔断异常。当时客户提工单过来后希望能够尽快修复集群,缓解Kafka消息堆积和查询超时问题。当时我的做法就是建议客户先将热节点上1个月之前的索引全部冻结。这样能够快速降低热节点的内存使用率和OldGC频率。待集群稳定后,再进行降冷和扩容等操作。同时再对之前冻结的索引执行解冻操作。执行API如下:

代码语言:javascript复制
POST /{index_name}/_unfreeze

5、Reindex重构索引

代码语言:javascript复制
POST _reindex
{
  "source": {
    "index": "{source_index_name}"
  },
  "dest": {
    "index": "{dest_index_name}"
  }
}

Reindex操作常用在字段类型变更、主分片个数变更、索引迁移等场景。例如由于源索引的某个字段被自动映射成了keyword类型,但是我们期望它是一个text类型时,则可以对源索引执行Reindex操作,操作之前需要先定义好目标索引的mapping。再比如我们经常有一些客户,要把自己集群迁移到腾讯云上,那么有可以直接通过Reindex方式来实现索引的迁移。这里需要注意的是在执行Reindex操作时,该索引是需要停写的,否则会出现数据不一致的情况。如果源索引数据比较大,一时半会操作不完,则可以在API中加上wait_for_completion=false参数,这样调用Reindex API时就异步执行并返回一个taskId。我们可以通过该taskId来查看Reindex状态甚至取消该task。

另外,如果源索引没有打开_source属性,则是不能执行Reindex操作的。也就是说源索引必须存储了全部的原始文档才可以。

6、索引别名相关

1)添加索引别名

代码语言:javascript复制
POST /_aliases
{
    "actions": [
        {
            "add": {
                "index": "{index_name}", 
                "alias": "{alias_name}"
            }
        }
    ]
}

2)移除索引别名

代码语言:javascript复制
POST /_aliases
{
    "actions": [
        {
            "remove": {
                "index": "{index_name}", 
                "alias": "{alias_name}"
            }
        }
    ]
}

给索引设置别名通常是在客户端层面希望能够无感知的对集群进行读写数据的时候。例如在Logstash中通过别名方式向集群中写入数据,但是该数据最终写入到哪个具体索引其实不是很关心。另外在我们集群运维时候还有一种场景就是在做Reindex时候,就是在Reindex时希望对业务端透明。这样就可以分别对源索引和目标索引加上同样的别名即可。

另外,我们还可以通过如下API来查看集群中目前有多少个别名,以及这些别名都关联了哪些索引,以及通过is_write_index参数来判断当前哪个别名指向的索引是写入的索引。

代码语言:javascript复制
GET /_cat/aliases?v

返回Response:

代码语言:javascript复制
alias          index                 filter routing.index routing.search is_write_index
ilm-history-3  ilm-history-3-000001  -      -             -              false
.slm-history-3 .slm-history-3-000001 -      -             -              false
.security      .security-7           -      -             -              -
.slm-history-3 .slm-history-3-000002 -      -             -              true
ilm-history-3  ilm-history-3-000002  -      -             -              true

例如,上面的例子中,别名ilm-history-3当前共关联了2个索引,分别是ilm-history-3-000001ilm-history-3-000002,当前通过别名写入的数据是直接写入到索引ilm-history-3-000002中的。 我们不仅可以直接对某个索引进行指定别名,还可以在索引模版中设置索引别名。这样索引在创建出来后就自动关联了一个别名。通过该别名进行查询就会自动去查询这些索引了:

代码语言:javascript复制
PUT _template/{template_name}
{
    "index_patterns" : ["index_name*"],
    "settings" : {
        "number_of_shards" : 10
    },
    "aliases" : {
        "alias_index_1" : {},
        "alias_index_2" : {
            "filter" : {
                "term" : {"key_name" : "value_name" }
            },
            "routing" : "value_name"
        },
        "{index}-alias" : {} 
    }
}

集群运维常用命令总结

本文详细介绍了我们在给腾讯云ES客户日常集群运维工作中常用的一些命令。通过该命令,我们能够快速的定位、分析及解决集群性能问题。下面以表格的形式汇总如下:

命令

API命令说明

GET _cat/indices?v

查看集群索引基本信息

GET /{index_name}/_settings

查看索引的settings信息

GET /{index_name}/_settings

动态设置索引的settings属性,如副本分片、refresh刷新时间等

POST /{index_name}/_freeze

冻结索引

POST _reindex

重建索引数据

POST /_aliases

为索引设置别名

0 人点赞