赞
踩
看到一篇好文章字太多,读不下去,所以转载一字一句的读。
转载:追踪同步分片副本
elasticsearch 通过在集群中保留多个数据副本的方式提供故障转移功能,当出现网络分区或者节点挂掉时,更改操作可能无法在所有副本上完成。这篇博客展示了 elasticsearch 的内部机制之一,如何把未产生更改的 shard 副本标记出来,从而深入了解两个核心组件,一致性模型和数据复制层
:他如何保障你的数据安全
elasticsearch 中的数据复制是基于主备模式
的,这个模型会假定其中一个数据副本为权威副本
,称之为 主分片
。
所有的索引操作写主分片,完成后,主分片所在节点会负责把更改转发到活跃的备份副本,称之为 副分片
。
elasticsearch 使用副分片机制来提供故障转移能力
,扩展读取性能。如果当前主分片临时,或者永久的变为不可用状态,例如服务器维护或磁盘损坏,另一个分片副本将被提升为主分片
。
因为主分片是权威的数据副本,因此在这个模型中,只把含有最新数据的分片作为主分片是至关重要的。
例如,当集群中的一个节点离开时,如果将一个旧数据的分片作为主分片,他将作为最终副本,从而导致由这个副本之后的数据将会丢失。以下介绍了 elasticsearch V5+如何追踪到那个可以安全的被选为主分片的副本。也称之为 同步(in-sync)分片副本。
in-sync allocation IDs 感觉这个概念是抄袭kafka的irs集合,也不知道谁抄袭谁的。反正基本相似,大概就是谁与主分片的数据保持的距离越少,差异性越少,当主分片失效的时候,最容易成功主分片的副本集合。(注意只要不是和主分片一毛一样,就可能丢失数据,当然也要看有没有translog)
分片分配
就是决定哪个节点应该容纳一个活跃分片的过程
。
分片决策过程在主节点完成,并记录在 集群状态 中,该数据结构还包含了其他元数据,如索引设置及映射。
分配决策包含两部分:哪个分片应该分配到哪个节点,以及哪个分片作为主分片,哪些作为副分片
。主节点广播集群状态到集群的所有节点。这样每个节点都有了集群状态,他们就可以实现对请求的智能路由。因为每个节点都知道主副分片分配到了哪里。
每个节点都会通过检查集群状态来判断某个分片是否可用
。如果一个分片被指定为主分片
,这个节点只需要加载本地分片副本
,使之可以用于搜索,如果一个分片被分片为副分片,节点首先需要从主分片所在节点拷贝差异数据
。
当集群中可用副分片不足(在索引设置中指定:index.number_of_replicas
),主节点也可以将副分片分配到不含任何此分片副本的节点,从而指示这些节点创建主分片的完整副本。
在创建新索引时,主节点在选择哪个节点作为主分片方面有很大的灵活性,会将集群均衡
和其他约束如分片感知及过滤器考虑在内。
分配已存在的分片副本为主分片是比较少见的情况,例如集群完全重启(full restart),所有分片都是未分配状态,或者短时间内所有活跃副本都变为不可用。在这种情况下,主节点询问所有节点,找到磁盘中存在的分片副本,根据找到的副本,决定是否将其中一个作为主分片。
为了确保安全,主节点必须确保被选为主分片的副本含有最新数据
。为此,elasticsearch 使用 分配 IDs 的概念
,这是区分不同分片的唯一标识
(UUIDS)。
分配标识由主节点在分片分配时指定。并由数据节点存储在磁盘中,紧邻实际的数据分片。主节点负责追踪包含最新数据副本的子集。这些副本集合称为同步分片标识(in-sync allocation IDs)
,存储于集群状态中。
集群状态存在于集群的所有主节点和数据节点。对集群状态的更改由elasticsearch的zen discovery模块实现一致性支持
。他确保集群中有共同的理解,即哪些分片副本被认为是 同步的(in-sync)
,隐式地将那些不在同步集合中的分片副本标记为 陈旧(stale)
。
补充:理解一下上面一段话:Allocation IDs存储在 shard 级元信息中,每个 shard 都有自己的Allocation ID,同时集群级元信息中记录了一个被认为是最新shard 的Allocation ID集合,这个集合称为 in-sync allocation IDs
当分配主分片时,主节点检查磁盘中存储的 allocation ID 是否会在集群状态in-sync allocations 集合中出现,只有在这个集合中找到了,此分片才有可能被选为主分片。如果活跃副本中的主分片挂了,in-sync
集合中的活跃分片会被提升为主分片,确保集群的写入可用性不变。
在 elasticsearch 中,数据复制和元信息复制使用不同的机制,数据复制使用简单的主备方法
。这种两层的系统可以使得数据复制更简单,更快。只有在特殊情况下才需要与集群一致(consensus)层交互。处理温度写请求的基本流程如下:
主分片
所在节点。本地执行
,例如索引,更新或删除文档。转发
到副分片。 如果有多个复制目标,则将操作转发操作是并行
的回复主分片
所在节点,主分片所在节点确认成功完成对客户端的请求。当网络产生分区,节点故障,或者部分节点未启动,转发操作可能没有在一个或多个副本上执行成功,这意味着主分片中含有一些没有传播到所有分片的数据,如果这些副本仍然被认为是同步的,那么即使他们遗漏了一些变化,他们也可能稍后被选为主分片,结果丢失数据。
有两种方法解决这种问题:
elasticsearch在这种情况下选择了写入可用性:主分片所在节点命令主节点将差异分片的Allocation IDs从同步集合(in-sync set)中删除。
然后,主分片所在节点等待主节点删除成功的确认消息,这个确认消息意味着集群一致层(consensus layer)已成功更新,之后,才向客户端确认写请求。这样确保只有包含了所有已确认写入的分片副本才会被主节点选为主分片
。
为了看到这一点,我们考虑一个小型集群:一个主节点,两个数据节点
。为了保持简单的例子,我们创建只有1个主分片
和1个副分片
的索引,最初,一个数据节点拥有主分片,另一个数据节点拥有副分片。
我们使用 cluster state api 来查阅集群状态中的 in-sync 分片信息,并使用 “filter_path” query 参数来过滤出感兴趣的结果:
GET /_cluster/state?filter_path=metadata.indices.my_index.in_sync_allocations.*,routing_table.indices.my_index.*
GET /_cluster/state?filter_path=metadata.indices.my_index.in_sync_allocations.*,routing_table.indices.my_index.*
产生以下集群状态的摘录:
{ "metadata": { "indices": { "my_index": { "in_sync_allocations": { "0": [ "HNeGpt5aS3W9it3a7tJusg", "wP-Z5fuGSM-HbADjMNpSIQ" ] } } } }, "routing_table": { "indices": { "my_index": { "shards": { "0": [ { "primary": true, "state": "STARTED", "allocation_id": { "id": "HNeGpt5aS3W9it3a7tJusg" }, "node": "CX-rFmoPQF21tgt3MYGSQA", ... }, { "primary": false, "state": "STARTED", "allocation_id": { "id": "wP-Z5fuGSM-HbADjMNpSIQ" }, "node": "AzYoyzzSSwG6v_ypdRXYkw", ... } ] } } } } }
{ "metadata": { "indices": { "my_index": { "in_sync_allocations": { "0": [ "HNeGpt5aS3W9it3a7tJusg", "wP-Z5fuGSM-HbADjMNpSIQ" ] } } } }, "routing_table": { "indices": { "my_index": { "shards": { "0": [ { "primary": true, "state": "STARTED", "allocation_id": { "id": "HNeGpt5aS3W9it3a7tJusg" }, "node": "CX-rFmoPQF21tgt3MYGSQA", ... }, { "primary": false, "state": "STARTED", "allocation_id": { "id": "wP-Z5fuGSM-HbADjMNpSIQ" }, "node": "AzYoyzzSSwG6v_ypdRXYkw", ... } ] } } } } }
集群状态显示出主分片和副分片都已启动,主分片分配在数据节点 “CX-rFmo”
,副分片分配在数据节点 “AzYoyz”
。他们都有唯一的allocation id,
同时,也出现在in_sync_allocations
集合中。
让我们看看当关闭主分片所在节点时会发生什么。由于这并不改变分片上的数据,所以两个分片副本应该保持同步。在没有主分片的情况下,副分片也应该被提示为主分片,这些都会反映在集群状态中:
{ "metadata": { "indices": { "my_index": { "in_sync_allocations": { "0": [ "HNeGpt5aS3W9it3a7tJusg", "wP-Z5fuGSM-HbADjMNpSIQ" ] } } } }, "routing_table": { "indices": { "my_index": { "shards": { "0": [ { "primary": true, "state": "STARTED", "allocation_id": { "id": "wP-Z5fuGSM-HbADjMNpSIQ" }, "node": "AzYoyzzSSwG6v_ypdRXYkw", ... }, { "primary": false, "state": "UNASSIGNED", "node": null, "unassigned_info": { "details": "node_left[CX-rFmoPQF21tgt3MYGSQA]", ... } } ] } } } } }
{ "metadata": { "indices": { "my_index": { "in_sync_allocations": { "0": [ "HNeGpt5aS3W9it3a7tJusg", "wP-Z5fuGSM-HbADjMNpSIQ" ] } } } }, "routing_table": { "indices": { "my_index": { "shards": { "0": [ { "primary": true, "state": "STARTED", "allocation_id": { "id": "wP-Z5fuGSM-HbADjMNpSIQ" }, "node": "AzYoyzzSSwG6v_ypdRXYkw", ... }, { "primary": false, "state": "UNASSIGNED", "node": null, "unassigned_info": { "details": "node_left[CX-rFmoPQF21tgt3MYGSQA]", ... } } ] } } } } }
由于只有一个数据节点,副分片停留在未分配状态。如果我们再次启动第二个节点,副分片将自动分配在这个节点上。为了使这个场景更有趣,我么不启动第二个节点,相反,我们索引一个文档到新提升的主分片中。由于分片副本现在是差异的(diverging),不活跃的哪个分片副本变为陈旧的,因此他的 ID被主节点从 in-sync 集合中删除:
{ "metadata": { "indices": { "my_index": { "in_sync_allocations": { "0": [ "wP-Z5fuGSM-HbADjMNpSIQ" ] } } } }, "routing_table": { ... // same as in previous step } }
{ "metadata": { "indices": { "my_index": { "in_sync_allocations": { "0": [ "wP-Z5fuGSM-HbADjMNpSIQ" ] } } } }, "routing_table": { ... // same as in previous step } }
现在只剩下一个同步的分片副本,让我们看看如果该副本变为不可用,系统如何处理。为此,我们关闭当前唯一的数据节点,然后启动前一个拥有陈旧分片副本的数据节点,之后,cluster health api 显示cluser health 为red,集群状态显示主分片尚未分配:
{ "metadata": { "indices": { "my_index": { "in_sync_allocations": { "0": [ "wP-Z5fuGSM-HbADjMNpSIQ" ] } } } }, "routing_table": { "indices": { "my_index": { "shards": { "0": [ { "primary": true, "state": "UNASSIGNED", "recovery_source": { "type": "EXISTING_STORE" }, "unassigned_info": { "allocation_status": "no_valid_shard_copy", "at": "2017-01-26T09:20:24.054Z", "details": "node_left[AzYoyzzSSwG6v_ypdRXYkw]" }, ... }, { "primary": false, "state": "UNASSIGNED", "recovery_source": { "type": "PEER" }, "unassigned_info": { "allocation_status": "no_attempt", "at": "2017-01-26T09:14:47.689Z", "details": "node_left[CX-rFmoPQF21tgt3MYGSQA]" }, ... } ] } } } } }
{ "metadata": { "indices": { "my_index": { "in_sync_allocations": { "0": [ "wP-Z5fuGSM-HbADjMNpSIQ" ] } } } }, "routing_table": { "indices": { "my_index": { "shards": { "0": [ { "primary": true, "state": "UNASSIGNED", "recovery_source": { "type": "EXISTING_STORE" }, "unassigned_info": { "allocation_status": "no_valid_shard_copy", "at": "2017-01-26T09:20:24.054Z", "details": "node_left[AzYoyzzSSwG6v_ypdRXYkw]" }, ... }, { "primary": false, "state": "UNASSIGNED", "recovery_source": { "type": "PEER" }, "unassigned_info": { "allocation_status": "no_attempt", "at": "2017-01-26T09:14:47.689Z", "details": "node_left[CX-rFmoPQF21tgt3MYGSQA]" }, ... } ] } } } } }
让我们再看看cluster allocation explain API ,这是一个调试分配问题的好工具。 运行不带参数的explain命令将提供系统找到的第一个未分配分片的说明:
GET /_cluster/allocation/explain
GET /_cluster/allocation/explain
explain API 告诉我们为什么主分片处于未分配状态,同时还提供了基于每个节点上的更详细的分配信息。在这个例子中,主节点在集群当前可用节点中无法找到同步的(in-sync)分片副本。
{ "index" : "my_index", "shard" : 0, "primary" : true, "current_state" : "unassigned", "unassigned_info" : { "reason" : "NODE_LEFT", "at" : "2017-01-26T09:20:24.054Z", "last_allocation_status" : "no_valid_shard_copy" }, "can_allocate" : "no_valid_shard_copy", "allocate_explanation" : "cannot allocate because all found copies of the shard are either stale or corrupt", "node_allocation_decisions" : [ { "node_id" : "CX-rFmoPQF21tgt3MYGSQA", "node_name" : "CX-rFmo", "transport_address" : "127.0.0.1:9301", "node_decision" : "no", "store" : { "in_sync" : false, "allocation_id" : "HNeGpt5aS3W9it3a7tJusg" } } ] }
{ "index" : "my_index", "shard" : 0, "primary" : true, "current_state" : "unassigned", "unassigned_info" : { "reason" : "NODE_LEFT", "at" : "2017-01-26T09:20:24.054Z", "last_allocation_status" : "no_valid_shard_copy" }, "can_allocate" : "no_valid_shard_copy", "allocate_explanation" : "cannot allocate because all found copies of the shard are either stale or corrupt", "node_allocation_decisions" : [ { "node_id" : "CX-rFmoPQF21tgt3MYGSQA", "node_name" : "CX-rFmo", "transport_address" : "127.0.0.1:9301", "node_decision" : "no", "store" : { "in_sync" : false, "allocation_id" : "HNeGpt5aS3W9it3a7tJusg" } } ] }
该API还显示在节点“CY-rFmo”上可用的分片副本是陈旧的( store.in_sync = false )
。启动拥有in-sync 分片副本的那个节点将使集群重新变为 green。如果那个节点永远都回来了呢?
发生严重灾难时,集群中可能会出现只有陈旧副本可用的情况。elasticsearch 不会把这些分片自动分配为主分片,集群将持续 red 状态
。但是如果所有in-sync 副本都消失了,集群仍有可能使用陈旧副本进行恢复,但这需要管理员手工干预。
正如我们在前面的例子中看到的,理解分片问题的第一步是使用 cluster allocation explain API。他显示节点是否具有分片的副本,以及相应的副本是否处于同步集合中。在文件系统损坏的情况下,他也显示了访问磁盘信息时抛出的异常。在前面的例子中,allocation explain的输出显示在集群中的节点“CX-rFmo”上找到了现有的分片副本,但该副本未包含最新的数据(in-sync:false)
reroute API 提供了一个子命令 allocate_stale_primary ,用于将一个陈旧的分片分配为主分片。使用此命令意味着丢失给定分片副本中缺少的数据。如果同步分片只是暂时不可用,使用此命令意味着在同步副本中最近更新的数据。应该把它看作是使群集至少运行一些数据的最后一种措施。在所有分片副本都不存在的情况下,还可以强制Elasticsearch使用空分片副本分配主分片,这意味着丢失与该分片相关联的所有先前数据。 不言而喻,allocate_empty_primary 命令只能用于最糟糕的情况,其含义很好理解。
关键词
副本,指数据的一个分片,无论是 primary 还是 replica
分片:shard
主分片:primary shards
副分片:replica shards
分片分配:Shard allocation
集群状态:cluster state
分配决策:Allocation decisions
分配感知:allocation awareness
分配标识:allocation IDs
追踪:tracking
同步集合: in-sync set
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。