报错:

org.elasticsearch.action.search.SearchPhaseExecutionException: all shards failed
image20230821145626740.png

1. 检查健康状态

curl --user 用户名:密码  -XGET http://本机ip:端口/_cluster/health\?pretty
{
  "cluster_name" : "elasticsearch",
  "status" : "red",
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  "active_primary_shards" : 59,
  "active_shards" : 59,
  "relocating_shards" : 0,
  "initializing_shards" : 3,
  "unassigned_shards" : 33,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 7,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 615,
  "active_shards_percent_as_number" : 62.10526315789474
}

green为健康状态,red为不健康,yellow在两者之间,但仍不健康。

从上面的 unassigned_shards 可以存在大量分片没有被分配,

  1. 如果是集群环境,可以考虑使用 POST /_cluster/reroute 强制把问题分片分配到其中一个节点上了

  2. 但是对于目前的单机环境,从上面截图可以看出存在5个 unassigned 的分片,新建索引时候,分片数为5,副本数为1,新建之后集群状态成为yellow,其根本原因是因为集群存在没有启用的副本分片。

    解决办法就是,在单节点的elasticsearch集群,删除存在副本分片的索引,新建索引的副本都设为0。然后再查看集群状态

    通过如果下命令,设置number_of_replicas=0,将副本调整为0. 如下图所示,es变成了“绿色”

curl --user 用户名:密码 -XPUT 'http://127.0.0.1:9200/_settings' -H 'content-Type:application/json' -d'
{
  "number_of_replicas": 0
}'

再次查看健康状态:

{
  "cluster_name" : "elasticsearch",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  "active_primary_shards" : 95,
  "active_shards" : 95,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}

green!

查看每个分片的状态

http://localhost:9200/_cat/shards?h=index,shard,prirep,state,unassigned.reason

根据es官网,各种副本分片创建失败原因如下:

1)INDEX_CREATED:由于创建索引的API导致未分配。
2)CLUSTER_RECOVERED :由于完全集群恢复导致未分配。
3)INDEX_REOPENED :由于打开open或关闭close一个索引导致未分配。
4)DANGLING_INDEX_IMPORTED :由于导入dangling索引的结果导致未分配。
5)NEW_INDEX_RESTORED :由于恢复到新索引导致未分配。
6)EXISTING_INDEX_RESTORED :由于恢复到已关闭的索引导致未分配。
7)REPLICA_ADDED:由于显式添加副本分片导致未分配。
8)ALLOCATION_FAILED :由于分片分配失败导致未分配。
9)NODE_LEFT :由于承载该分片的节点离开集群导致未分配。
10)REINITIALIZED :由于当分片从开始移动到初始化时导致未分配(例如,使用影子shadow副本分片)。
11)REROUTE_CANCELLED :作为显式取消重新路由命令的结果取消分配。
12)REALLOCATED_REPLICA :确定更好的副本位置被标定使用,导致现有的副本分配被取消,出现未分配。

es 集群里面的分片是分配在多台 node 上的,为的就是高可用,比如你的某台机器 crash 了,那么集群就会让其他副本顶上来,免得出现某个分片不能提供服务的情况。

既然这样,难免还是会出现 UNASSIGNED shards 的错误,今天我们来探究下原因和解决之道。

如果你的数据无关紧要(比如监控数据,丢了就丢了,因为你只关注当前的), 就可以直接简单粗暴的删掉出问题的分片,当然你的数据很重要的时候,就不能这样干了,下面我们来看下几种情况。

  • Shard allocation 过程中的延迟机制
  • nodes 数小于分片副本数
  • 检查是否开启 cluster.routing.allocation.enable 参数
  • 分片的历史数据丢失了
  • 磁盘不够用了
  • es 的版本问题

一、发现 unassigned 的分片

es 有 api 可以查看所有的 unassigned 的分片

	curl -XGET localhost:9200/_cat/shards?h=index,shard,prirep,state,unassigned.reason| grep UNASSIGNED

这样你就可以清楚的看到 index, 分片数, 主分片还是副本分片,是否处于 unassigned 的状态, 甚至还有 unassigned 的原因,比如

	constant-updates        0 p UNASSIGNED NODE_LEFT node_left[NODE_NAME]

如果这个 index 已经不用了, 直接删除 index, 这样这些 unassigned 的分片也会被干掉, 集群恢复正常

	curl -XDELETE 'localhost:9200/index_name/'

二、Shard allocation 过程中的延迟机制

当一个 点从集群中下线了, es 有一个延迟拷贝机制, 默认是等一分钟之后再开始处理 unassigned 的分片, 该做 rebalance 的去 rebalance,只所以这样, 是因为 es 担心如果一个点只是中断了片刻, 或者临时下线某台机器,就立马大动干戈,就尴尬了,比如下面这种情形

  • Node(节点) 19 在网络中失联了(某个家伙踢到了电源线)
  • Master 立即注意到了这个节点的离线,它决定在集群内提拔其他拥有 Node 19 上面的主分片对应的副本分片为主分片
  • 在副本被提拔为主分片以后,master 节点开始执行恢复操作来重建缺失的副本。集群中的节点之间互相拷贝分片数据,网卡压力剧增,集群状态尝试变绿。
  • 由于目前集群处于非平衡状态,这个过程还有可能会触发小规模的分片移动。其他不相关的分片将在节点间迁移来达到一个最佳的平衡状态

与此同时,那个踢到电源线的倒霉管理员,把服务器插好电源线进行了重启,现在节点 Node 19 又重新加入到了集群。不幸的是,这个节点被告知当前的数据已经没有用了, 数据已经在其他节点上重新分配了。所以 Node 19 把本地的数据进行删除,然后重新开始恢复集群的其他分片(然后这又导致了一个新的再平衡)

如果这一切听起来是不必要的且开销极大,那就对了。是的,不过前提是你知道这个节点会很快回来。如果节点 Node 19 真的丢了,上面的流程确实正是我们想要发生的。

这个默认的延迟分配分片的实际是 1 分钟, 当然你可以设置这个时间

	curl -XPUT 'localhost:9200/<INDEX_NAME>/_settings' -d '

	{

	    "settings": {

	      "index.unassigned.node_left.delayed_timeout": "30s"

	    }

	}'

三、nodes 数小于分片副本数

当一个 nodes 被下掉之后, master 节点会重新 reassigns 这台 nodes 上的所有分片, 尽可能的把同一个分片的不同副本分片和主分片分配到不同的 node 上,但是如果你设置的一个分片的 副本数目太多, 导致根本没法一个 node 上分配一个,就会出现问题, 会导致 es 没法进行 reassign, 这样就会出现 unassigned 的分片。

从一开始创建 index 的时候就要保证 N >= R + 1 这里 N 代表 node 的个数, R 代表你 index 的副本数目。

怎么解决呢, 要么增加 nodes 个数,要么减少副本数

	curl -XPUT 'localhost:9200/<INDEX_NAME>/_settings' -d '{"number_of_replicas": 2}'

我们上个例子中,就把 副本数目减少到 2 个, 问题解决。

四、检查是否开启 cluster.routing.allocation.enable 参数

Shard allocation 功能默认都是开启的, 但是如果你在某个时刻关闭了,这个功能(比如滚动重启的情形, https://www.elastic.co/guide/en/elasticsearch/guide/current/_rolling_restarts.html ), 后面忘了开启了,也会导致问题, 你可以使用下面这个命令开开启下

	curl -XPUT 'localhost:9200/_cluster/settings' -d

	'{ "transient":

	  { "cluster.routing.allocation.enable" : "all" 

	  }

	}'

恢复之后, 你可以从监控上,看到 unassigned shards 逐渐恢复

看监控中,几个 index 都恢复了,好像还有 constant-updates 这个 index 没有好,我们看下是否还有其他原因

五、分片的历史数据丢失了

我们现在的问题是这样, constant-updates 这个 index 的第 0 个分片处于 unassigned 状态, 创建这个 index 的时候 每个分片只有 一个 主分片,没有其他副本, 数据没有副本, 集群检测到这个分片的 全局状态文件,但是没有找到原始数据, 就没法进行恢复。

还有一种可能是这样, 当一个 node 重启的时候, 会重新连接集群, 然后把自己的 disk 文件信息汇报上去, 这时候进行恢复,如果这个过程出现了问题,比如存储坏掉了,那么当前分片还是没法恢复正常。

这个时候,你可以考虑下,是继续等待原来的那台机器恢复然后加入集群,还是重新强制分配 这些 unassigned 的分片, 重新分配的时候也可以使用备份数据。

如果你打算重新强制分配主分片,可以使用下面的命令 , 记得带上 "allow_primary": "true"

	curl -XPOST 'localhost:9200/_cluster/reroute' -d '{ "commands" :

	  [ { "allocate" : 

	      { "index" : "constant-updates", "shard" : 0, "node": "<NODE_NAME>", "allow_primary": "true" }

	  }]

	}'

如果你没有带上 "allow_primary": "true", 就会报错

	{"error":{"root_cause":[{"type":"remote_transport_exception","reason":"[NODE_NAME][127.0.0.1:9301][cluster:admin/reroute]"}],"type":"illegal_argument_exception","reason":"[allocate] trying to allocate a primary shard [constant-updates][0], which is disabled"},"status":400}

因为没有当前分配的分片是没有主分片了。

当然你在重新强制分配主分片的时候,可以创建一个 empty 的主分片,也就是老数据我不要了, 这个时候,如果失联的 node 重新加入集群后, 就把自己降级了, 分片的数据也会使用 这个 empty 的主分片覆盖, 因为它已经变成过时的版本了。

	POST _cluster/reroute  

	{

	  "commands" : [ {

	        "allocate_empty_primary" :

	            {

	              "index" : "constant-updates", "shard" : 0, "node" : "<NODE_NAME>", "accept_data_loss" : true

	            }

	        }

	  ]

	}

这个命令就可以创建一个 empty 的主分片。

六、磁盘不够用了

如果很多 nodes 的磁盘都触及 low disk watermark, 没有足够的磁盘空间用来分配分片, 这时候同样会出现 unassigned 的分片。 一般这个 磁盘使用超过 85 % , 就会触及 low disk watermark。

你可以使用下面的 api 来查看当前磁盘使用情况

	curl -s 'localhost:9200/_cat/allocation?v'

你可以参考 这篇文章来 应对磁盘不够用的情况, https://www.datadoghq.com/blog/elasticsearch-performance-scaling-problems/#toc-problem-2-help-data-nodes-are-running-out-of-disk-space1。

这个触及 low disk watermark 的磁盘使用比例也是可以设置的,

	curl -XPUT 'localhost:9200/_cluster/settings' -d

	'{

	    "transient": {  

	      "cluster.routing.allocation.disk.watermark.low": "90%"    

	    }

	}'

七、es 的版本问题

哦,差点忘了还有一种极端情况, 就是你升级了某个 node 的版本, master node 会不认这个跟它版本不同的的 node, 也不会在上面分配分片。

如果你手动强制往上面分配分片,会报错。

	[NO(target node version [XXX] is older than source node version [XXX])]

大体就这几种情况,你可以根据自己的观察到的现象去判断。

知识点#

副本分片 主要目的就是为了故障转移,如果持有主分片的节点挂掉了,一个副本分片就会晋升为主分片的角色。

所以副本分片和主分片是不能放到一个节点上面的,可是在只有一个节点的集群里,副本分片没有办法分配到其他的节点上,所以出现所有副本分片都unassigned得情况。因为只有一个节点,如果存在主分片节点挂掉了,那么整个集群理应就挂掉了,不存在副本分片升为主分片的情况。