【Elasticsearch】AwarenessAllocationDecider 感知分片决策器的处理逻辑

Elasticsearch感知分片分配原理

本 `AllocationDecider` 根据节点配置中定义的 **感知键值对(awareness key-value pairs)** 来控制分片分配。  
**感知机制(awareness)** 的核心目的是:  
**明确控制副本应该被分配到哪些位置**,从而避免所有副本扎堆在同一物理位置(如同一个机架、同一个机房、同一个可用区),确保即使某个位置整体故障,仍至少有一个副本可用。

---

### 举个例子:按机架(rack)分配

设置:
```yaml
cluster.routing.allocation.awareness.attributes: rack_id
```
表示:**尽量把同一份分片的主副本和各个副本分配到不同的机架上**。

要让这一规则生效,节点启动时必须带上对应的属性值,例如:
```yaml
node.attr.rack_id: 1
```

这样,ES 在分配时就会统计每个机架上已有的副本数,**避免多个副本落在同一机架**,达到“机架级容灾”的效果。

---

### 再举个例子:按云端“可用区(zone)”分配

设置:
```yaml
cluster.routing.allocation.awareness.attributes: zone
```
再加上强制声明:
```yaml
cluster.routing.allocation.awareness.force.zone.values: zone1, zone2
```

表示:
- 集群当前可能只有 `zone1` 的节点在线,但你告诉 ES **未来一定会有 zone2**。
- 因此 ES 会把副本数按 **总 zone 数 = 2** 来均分计算上限,**不会因为是“当前只有 zone1”就把副本全堆在 zone1**。
- 即使 `zone2` 暂时无节点或部分故障,**也不会出现“zone1 超载”**的情况。

zone2 节点没上线前,**ES 不会把副本硬塞进一个不存在的节点**;  
它只会 **“先不分配”**(shard 状态保持 `UNASSIGNED`),直到:

1. 你启动了带有 `node.attr.zone: zone2` 的节点 → 副本立刻被分配到这些节点上。  
2. 你主动把 `force.zone.values` 里的 `zone2` 去掉 → ES 重新按 **实际只有 1 个 zone** 计算上限,原来卡住的副本才允许在 `zone1` 补完。

所以 **“不超载” ≠ “强行放过去”**,而是 **“宁可暂时少一份,也不破坏容灾布局”**。  
集群表现为 **部分分片处于未分配状态**,直到对应 zone 的节点加入。

节点侧只需照常声明自己所在的 zone:
```yaml
node.attr.zone: zone1
```

---

### 总结一句话

**感知机制 = 让 ES 按“你指定的物理维度”去均分副本,防止扎堆;  
`force.values` = 提前声明未来才会有的维度值,避免过渡期超载。**

**只要任意一个 AllocationDecider 返回 NO**,这条分片就 **不会被分配到任何节点**,状态保持 **UNASSIGNED**,直到:

- 你**调整规则/配置**(让 decider 通过),或  
- 你**增加/修复节点**(让规则自然满足),或  
- 你**手动干预**(比如调低副本数、关闭感知强制值等)。

ES 的分配流程是 **“一票否决”制**:  
**所有 decider 都 YES → 才能落地;任何一个 NO → 继续等着。**

下面把 `underCapacity` 的完整流程按“**判断顺序 + 分支走向**”拆开,让你一眼看清每一步在拦什么、怎么走。

--------------------------------------------------
1. **感知功能开没开**  
   ```java
   if (awarenessAttributes.isEmpty()) return YES_NOT_ENABLED;
   ```
   - 集群没配 `cluster.routing.allocation.awareness.attributes`  
   → **直接放行**,后面逻辑全跳过。

--------------------------------------------------
2. **索引是否“扩到全节点”**  
   ```java
   if (indexMetadata.getAutoExpandReplicas().expandToAllNodes()) return YES_AUTO_EXPAND_ALL;
   ```
   - 索引设置了 `"auto_expand_replicas" : "0-all"`  
   → **无视感知规则,直接放行**。

--------------------------------------------------
3. **逐个维度循环检查**  
   对 `awarenessAttributes` 里每一个 `awarenessAttribute`(如 zone、rack)执行下列 **4-8** 步:

--------------------------------------------------
4. **目标节点有没有这个维度**  
   ```java
   if (node.node().getAttributes().containsKey(awarenessAttribute) == false)
       return Decision.NO;   // 或 debugNoMissingAttribute
   ```
   - 节点没写 `node.attr.zone` 之类  
   → **拒绝 + 给出 missing 理由**。

--------------------------------------------------
5. **运行时 double-check**(assert,非业务分支)  
   两句 `assert`:  
   - 节点该属性值不能为 null  
   - 该值必须出现在“集群已见值 + 强制值”集合里  
   **生产默认关闭,失败抛 AssertionError,防止手抖写错。**

--------------------------------------------------
6. **统计“目标区”已有多少份该分片**  
   6a. **遍历已分配副本**  
   ```java
   for (ShardRouting assignedShard : allocation.routingNodes().assignedShards(shardId))
       if (assignedShard.started() || assignedShard.initializing())
           如果副本所在节点的维度值 == targetAttributeValue → 计数器 +1
   ```
   6b. **moveToNode 场景再补 0/1 份**  
   - 分片已迁出源节点(relocating)或未分配  
   → **跨区移动/新增** 会让目标区“净多”1 份,于是 `+1`;同区移动则不加。  

   **结果:shardsForTargetAttributeValue = 目标区当前份数 + 未来份数变化**

--------------------------------------------------
7. **计算上限**  
   ```java
   valueCount = 实际值 ∪ 强制值 去重后总数
   maximumShardsPerAttributeValue = ceil( shardCount / valueCount )
   ```
   - shardCount = 1(主)+ 副本数  
   - 即“每个感知值最多可放几份”。

--------------------------------------------------
8. **是否超标**  
   ```java
   if (shardsForTargetAttributeValue > maximumShardsPerAttributeValue)
       return Decision.NO;   // 或 debugNoTooManyCopies
   ```
   - 目标区份数 > 上限 → **拒绝**,打出当前区、上限、已份数等详情。  
   - 未超标 → **继续下一个维度**;全部维度通过 → 返回 `YES_ALL_MET`。

--------------------------------------------------
分支全景图(简化)

```
         ┌─ YES_NOT_ENABLED
         ├─ YES_AUTO_EXPAND_ALL
         │   ↓
   对每个维度 ── 缺属性 ──→ NO
         │   ↓
      assert 自检(异常抛 AssertionError)
         │   ↓
      统计目标区份数 + 移动修正
         │   ↓
      计算 ceil(总分片 / 总区数) 上限
         │   ↓
      份数>上限 ──→ NO + debug
         │   ↓
      全部维度通过 ──→ YES_ALL_MET
```

走完这张图,就完成了 **“感知维度层面是否允许把这份分片放到该节点”** 的全部判断。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值