解决Elasticsearch分片未分配的问题「译」
在Elasticsearch中,健康的群集是一个平衡的群集:主分片和副本分布在所有节点上,以保证有节点故障时的持久可靠性。
但是当你看到分片是UNASSIGNED
状态的时候该怎么办?
在深入探讨一些解决方案之前,我们先来验证一下未分配的碎片是否包含我们需要保存的数据(如果没有,删除这些碎片是解决这个问题的最直接的方法)。如果你已经知道数据值得保存,那么就看如下解决方案:
- 分片有目的的延迟分配
- 分片太多,节点不够
- 您需要重新启用分片分配
- 分片数据不再存在于集群中
- 磁盘低水平位
- 多个Elasticsearch版本
这篇文章中的命令默认端口(9200)上运行每个Elasticsearch实例的HTTP服务的格式。他们也被引导到localhost,它假定你在本地提交请求; 否则,请替换localhost您的节点的IP地址。
定位有问题的分片
Elasticsearch的cat API会告诉你哪些分片是未分配的,以及为什么:
curl -XGET localhost:9200/_cat/shards?h=index,shard,prirep,state,unassigned.reason| grep UNASSIGNED
每行列出了索引的名称,分片号,它是主分片还是副本分片,以及未分配的原因:
constant-updates 0 p UNASSIGNED NODE_LEFT node_left[NODE_NAME]
如果您运行的是Elasticsearch的5+版本,则还可以使用Cluster Allocation Explain API来尝试获取有关分片分配问题的更多信息:
curl -XGET localhost:9200/_cluster/allocation/explain?pretty
结果输出将提供一些有用的细节,说明为什么集群中的某些分片仍未分配:
{
"index" : "testing",
"shard" : 0,
"primary" : false,
"current_state" : "unassigned",
"unassigned_info" : {
"reason" : "INDEX_CREATED",
"at" : "2018-04-09T21:48:23.293Z",
"last_allocation_status" : "no_attempt"
},
"can_allocate" : "no",
"allocate_explanation" : "cannot allocate because allocation is not permitted to any of the nodes",
"node_allocation_decisions" : [
{
"node_id" : "t_DVRrfNS12IMhWvlvcfCQ",
"node_name" : "t_DVRrf",
"transport_address" : "127.0.0.1:9300",
"node_decision" : "no",
"weight_ranking" : 1,
"deciders" : [
{
"decider" : "same_shard",
"decision" : "NO",
"explanation" : "the shard cannot be allocated to the same node on which a copy of the shard already exists"
}
]
}
]
}
在这种情况下,API清楚地解释了为什么复制分片仍然没有分配:“分片不能分配给已存在分片副本的同一节点”。要查看关于这个特定问题的更多细节,以及如何解决这个问题,可以查看文后介绍的此情况的篇幅。
如果看起来未分配的分片属于您认为已经删除的索引或已不再需要的过期索引,则可以删除索引以将群集状态恢复为绿色:
curl -XDELETE 'localhost:9200/index_name'
如果这不能解决问题,请继续阅读文章后面介绍的解决方案。
原因1:分片有目的延迟分配
当有节点离开集群时,主节点会暂时延迟分配重新分配以避免在重新平衡分配中不必要地浪费资源,原因是原始节点能够在一段时间内(默认情况下为一分钟)重新加入。如果是这种情况,你的日志应该是这样的:
[TIMESTAMP][INFO][cluster.routing] [MASTER NODE NAME] delaying allocation for [54] unassigned shards, next check in [1m]
你可以动态修改延迟时间,如下所示:
curl -XPUT 'localhost:9200/<INDEX_NAME>/_settings' -d
'{
"settings": {
"index.unassigned.node_left.delayed_timeout": "30s"
}
}'
如果需要修改所有索引的阀值,则可以使用_all
替换<INDEX_NAME>
原因2:分片太多,节点不够
当节点加入和离开集群时,主节点会自动重新分配分片,以确保分片的多个副本不会分配给同一个节点。换句话说,主节点不会将主分片分配给与其副本相同的节点,也不会将同一分片的两个副本分配给同一个节点。如果没有足够的节点相应地分配分片,分片可能会处于未分配状态。
要避免此问题,请按照以下公式,确保每个主分片中的每个索引的初始化次数少于群集中节点的数量:
N >= R +1
其中N是群集中节点的数量,R是群集中所有索引中最大的分片复制因子。
如上图,many-shards
索引存储在四个主分片上,每个主分片有四个副本。索引的20个分片中有8个未分配,因为我们的集群只包含三个节点。
由于三个节点中的每一个已经包含该分片的副本,所以尚未分配每个主分片的两个副本。
解决此问题,可以将更多数据节点添加到群集或减少副本数量。在我们的例子中,我们需要在集群中添加至少两个以上的节点,或者将复制因子减少到两个,如下所示:
curl -XPUT 'localhost:9200/<INDEX_NAME>/_settings' -d '{"number_of_replicas": 2}'
在减少副本数量后,查看Kop是否已经分配了所有分片:
原因3:重新启用分片分配
在下面的Kopf屏幕截图中,一个节点刚刚加入了群集,但尚未分配任何分片。
默认启用碎片分配在所有节点上,但有可能禁用了分片分配(例如,为了执行滚动重启),并且忘记重新启用分片分配。
重新启用分片分配,参考官网Cluster Update Settings API:
curl -XPUT 'localhost:9200/_cluster/settings' -d
'{ "transient":
{ "cluster.routing.allocation.enable" : "all"
}
}'
如果问题得以解决,则在Kopf仪表板应该显示未分配的分片数量在减少,因为它们已成功分配给节点,如下图所示:
在重新启用分片分配后Kopf仪表板中可以看出,许多之前未分配的分片已分配
原因4:分片数据不再存在于集群中
从原因3段落中最后一张Kopf图片可以看出,constant-updates
索引的主分片0是未分配的。它可能在没有任何副本的节点上创建(一种用于加速初始索引过程的技术),并且节点在可以复制数据之前离开集群。Master在全局集群状态文件中检测到shard,但是无法在集群中找到分配的数据。
另一种可能性是节点在重新启动时可能遇到问题。通常,当一个节点恢复到集群的连接时,它会将有关其磁盘分片的信息转发给主节点,然后主节点将这些分片从“未分配”转换为“已分配/已启动”。当由于某种原因(例如,节点的存储已被损坏)导致此进程失败时,分片可能保持未分配状态。
在这种情况下,你必须决定如何处理:尝试获得原始节点恢复并重新加入集群(并没有强制分配主分片),或者力分配使用的碎片重新路由API使用和重新索引丢失数据原始数据源或备份。
在这种情况下,你必须决定如何处理:
- 尝试让原始节点恢复并重新加入集群(并没有强制分配主分片)
- 使用分片重新路由API强制分配分片
- 从备份数据中使用原始数据源重建索引丢失的数据
使用Reroute API
强制重分配分片请求如下:
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}
强制分配主分片的警告是你将分配一个“空的”分片。如果包含原始主要分片数据的节点稍后要重新加入集群,则其数据将被新创建的(空)主分片覆盖,因为它将被视为数据的”较新”版本。
如果你需要reindex丢失的数据,或使用快照和还原API从备份快照中尽可能多地进行还原。
原因5:磁盘低水平位
如果没有足够的磁盘空间的节点(它不会将分片分配给磁盘使用率超过85%的节点),主节点可能无法分配分片。一旦一个节点达到了这个磁盘使用级别,或者Elasticsearch称之为“低磁盘水位”,它就不会被分配更多的分片。
您可以通过查询cat API来检查集群中每个节点上的磁盘空间(并查看每个节点上存储了哪些分片):
curl -s 'localhost:9200/_cat/allocation?v'
如果任何特定节点的磁盘空间不足(如删除过期数据并将其存储在群集外,添加更多节点,升级硬件等),可以参考如何解决5个Elasticsearch性能和缩放问题获取有关如何执行操作的选项。
如果您的节点具有较大的磁盘容量,则85%的低水位置可能太低。您可以使用群集更新设置API进行更改,cluster.routing.allocation.disk.watermark.low
或 cluster.routing.allocation.disk.watermark.high
。
例如,这个堆栈溢出线程指出,如果您的节点具有5TB的磁盘容量,则可以安全地将低磁盘水位增加到90%:
curl -XPUT 'localhost:9200/_cluster/settings' -d
'{
"transient": {
"cluster.routing.allocation.disk.watermark.low": "90%"
}
}'
如果希望配置更改在群集重新启动时保持不变,请将”transient”替换为”persistent”,或更新配置文件中的这些值。您可以选择使用字节或百分比值来更新这些设置,但请务必记住Elasticsearch文档中的这一重要提示:百分比值是指已用磁盘空间,而字节值是指可用磁盘空间。
原因6:过个Elasticsearch版本
这个问题只出现在运行多个版本的Elasticsearch的集群上(可能在滚动升级的过程中)。根据Elasticsearch文档,主节点不会将主分片副本分配给任何运行旧版本的节点。例如,如果主分片在版本1.4上运行,则主分区将无法将该分片的副本分配给运行1.4之前任何版本的任何节点。
如果你尝试将新版本节点的分片手动重新路由到旧版节点,则会看到类似下面的错误:
[NO(target node version [XXX] is older than source node version [XXX])]
Elasticsearch不支持回滚到以前的版本,只支持升级。升级运行旧版本的节点版本应该可以解决问题,如果这确实是问题所在。
你试过把它关掉再打开吗?
如果上述方案都不适用于您的情况,那么您仍可以选择从原始数据源重新索引缺失的数据,或从旧快照恢复受影响的索引,如此处所述。
监视好未分配的分片
尽快修复未分配的碎片很重要,因为它们表明数据丢失/不可用,或者您的集群未配置为获得最佳可靠性。您将立即开始监控未分配的分片和其他关键的Elasticsearch性能和运行状况指标。如何监控Elastisearch的性能,可以参考此处。
英文链接:https://www.datadoghq.com/blog/elasticsearch-unassigned-shards/#reason-2-too-many-shards-not-enough-nodes
- SpringBoot开发案例之mail中文附件乱码
- SpringBoot开发案例之整合Swagger篇
- SpringBoot开发案例之整合mail队列进阶篇
- SpringBoot开发案例之集成SSL证书
- SpringBoot开发案例之整合mail队列篇
- SpringBoot开发案例之整合日志管理
- SpringBoot开发案例之奇技淫巧
- SpringBoot开发案例之整合Spring-data-jpa
- SpringBoot开发案例之整合定时任务(Scheduled)
- SpringBoot开发案例之整合mail发送服务
- SpringBoot开发案例之整合mongoDB
- Docker学习之CentOS 7安装配置
- Docker学习之搭建JavaWeb环境
- Docker学习之搭建JavaWeb环境进阶篇
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法