上一篇文章中, 我們討論了群集組合、節點發現、主選擇、錯誤檢測、群集縮放等。本文將根據上一節重點分析 es 中元更新的一致性問題。為了增強我們的理解, 它還介紹了群集管理、元組合、存儲和其他信息的主方法。我們將討論以下議題:

  1. 主控管理群集的方式。
  2. 元組合、存儲和恢復。
  3. 群集狀態更新過程。
  4. 解決當前的一致性問題。
  5. 總結

主管理群集的方式

在上一篇文章中, 我們介紹了 es 集群組合、如何發現節點和主選擇。那么, 在成功選擇群集后, 主控如何管理集群呢?有幾個問題需要解決, 例如:

  1. 主控如何處理索引的創建或刪除?
  2. 如何主重新安排碎片的負載平衡?

由于需要群集管理, 因此主節點必須有某種方法來通知其他節點執行相應的操作來完成任務。例如, 在創建新索引時, 它必須將其 “碎片” 分配給某些節點。必須在節點上創建與 “碎片” 相對應的目錄, 這意味著它必須創建與內存中的 “碎片” 相對應的數據結構。

在 es 中, 主節點通過發布群集狀態通知其他節點。主節點將新的群集狀態發布到所有其他節點。當這些節點接收到新的 cluclstate 時, 它們會將其發送到相關模塊。然后, 這些模塊根據新的 ClusterState 確定要執行的操作, 例如, 創建一個碎片。這是如何通過元數據協調每個模塊的操作的示例。

當 master 進行元更改并通知所有其他節點時, 必須考慮元更改一致性問題。如果 master 在此過程中崩潰, 則只有某些節點可以根據新元執行操作。選擇新的 master 時, 需要確保所有節點都按照新的元執行操作, 并且不能回滾。由于某些節點可能已經按照新元執行了操作, 因此如果發生回滾, 則會導致不一致問題。

在 es 中, 只要在節點上提交新元, 就會執行相應的操作。因此, 我們必須確保一旦在節點上提交了新的元, 無論哪個節點是主節點, 它都必須基于此提交生成更新的元;否則, 可能會發生不一致。本文分析了這一問題以及為解決這一問題所采取的策略。

元組合、存儲和恢復

在介紹元更新過程之前, 我們將介紹元組合、存儲和恢復。如果您熟悉此主題, 則可以直接跳到下一個主題。

1. meta:cl元態、metadata、索引 metadata

元數據是用于描述數據的數據。在 es 中, 索引映射結構、配置和持久性是元數據, 群集的某些配置信息也屬于元數據。這樣的元數據非常重要。如果記錄索引的元數據丟失, 群集認為索引不再存在。在 es 中, 元數據只能由主數據更新, 因此主數據本質上是群集的大腦。

集群州

群集中的每個節點都在內存中維護當前的群集狀態, 該內存指示當前群集中的各種狀態。群集狀態包含元數據結構。存儲在元數據中的內容更符合元數據特征, 要保留的信息是元數據中的信息

群集狀態包含以下信息:

    long version: current version number, which increments by 1 for every update
    String stateUUID: the unique id corresponding to the state
    RoutingTable routingTable: routing table for all indexes
    DiscoveryNodes nodes: current cluster nodes
    MetaData metaData: meta data of the cluster
    ClusterBlocks blocks: used to block some operations
    ImmutableOpenMap<String, Custom> customs: custom configuration
    ClusterName clusterName: cluster name

如上所述, 元數據更符合元特征, 必須保持, 因此, 接下來, 我們來看看主要包含元數據的內容。

必須在元數據中保留以下內容:

    String clusterUUID: the unique id of the cluster.
    long version: current version number, which increments by 1 for every update
    Settings persistentSettings: persistent cluster settings
    ImmutableOpenMap<String, IndexMetaData> indices: Meta of all Indexes
    ImmutableOpenMap<String, IndexTemplateMetaData> templates: Meta of all templates
    ImmutableOpenMap<String, Custom> customs: custom configuration

正如您所看到的, 元數據主要包括群集配置、群集中所有索引的元數據以及所有模板的元數據。接下來, 我們將對 IndexMetaData 其進行分析, 稍后還將對其進行討論。雖然 IndexMetaData 它們也是其中的一部分 MetaData , 但它們是分開存儲的。

索引 metadata

IndexMetaData指索引的元, 如分片計數、副本計數和索引的映射。必須在中保留以下 IndexMetaData 內容:

    long version: current version number, which increments by 1 for every update.
    int routingNumShards: used for routing shard count; it can only be the multiples of numberOfShards of this Index, which is used for split.
    State state: Index status, and it is an enum with the values  OPEN or CLOSE.
    Settings settings: configurations, such as numbersOfShards and numbersOfRepilicas.
    ImmutableOpenMap<String, MappingMetaData> mappings: mapping of the Index
    ImmutableOpenMap<String, Custom> customs: custom configuration.
    ImmutableOpenMap<String, AliasMetaData> aliases: alias
    long[] primaryTerms: primaryTerm increments by 1 whenever a Shard switches the Primary, used to maintain the order.
    ImmutableOpenIntMap<Set<String>> inSyncAllocationIds: represents the AllocationId at InSync state, and it is used to ensure data consistency, which is described in later articles.

2. 元存儲

首先, 當啟動 es 節點時, 將配置一個數據目錄, 類似于以下內容。此節點只有單個碎片的索引。

$tree
.
`-- nodes
    `-- 0
        |-- _state
        |   |-- global-1.st
        |   `-- node-0.st
        |-- indices
        |   `-- 2Scrm6nuQOOxUN2ewtrNJw
        |       |-- 0
        |       |   |-- _state
        |       |   |   `-- state-0.st
        |       |   |-- index
        |       |   |   |-- segments_1
        |       |   |   `-- write.lock
        |       |   `-- translog
        |       |       |-- translog-1.tlog
        |       |       `-- translog.ckp
        |       `-- _state
        |           `-- state-2.st
        `-- node.lock

您可以看到 es 進程將元和數據寫入此目錄, 該目錄 named _state 指示該目錄存儲元文件。根據文件級別, 有三種類型的元存儲:

  • nodes/0/_state/:此目錄位于節點級別

此目錄下的第一個文件存儲 MetaData 上面提到的內容, IndexMetaData 但部件除外, 該部分包括群集級別上的一些配置和模板。

  • nodes/0/indices/2Scrm6nuQOOxUN2ewtrNJw/_state/:此目錄位于索引級別, 2scrm6noqoxun2ewtrnjw IndexId 是, 此目錄下的狀態 2. st 文件存儲上述內容 IndexMetaData 。

  • nodes/0/indices/2Scrm6nuQOOxUN2ewtrNJw/0/_state/:此目錄處于分片級別, 此目錄存儲下 ShardStateMetaData 的狀態為 0. st, 其中包含 allocationId 諸如以及是否為主目錄的信息 ShardStateMetaData . 在模塊中進行管理 IndexShard , 這與其他元目錄沒有那么相關, 因此我們不會討論詳細信息這里。

  • 正如您所看到的, 與群集相關 MetaData 的和 IndexMetaData 存儲在不同的目錄中。此外, 與群集相關的元數據存儲在所有 MasterNodes DataNodes IndexMeta , 而存儲在所有 MasterNodesDataNodes 已存儲此索引數據。

    這里的問題是 MetaData , 因為是由師父管理的, 為什么 MetaData 也存儲在 DataNode 上面?這主要是為了數據安全: 許多用戶不考慮主服務器和高數據可靠性的高可用性要求, 并且僅在 MasterNode 部署 es 群集時配置一個。在此情況下, 如果節點出現故障, 元可能會丟失, 這可能會對他們的業務產生巨大影響。

    3. 元恢復

    如果 es 群集重新啟動, 則需要一個角色來恢復元數據, 因為所有進程都丟失了以前的元信息;這個角色是師父。首先, 主選舉必須在 es 集群中進行, 然后可以開始故障恢復。

    選擇 master 時, master 進程將等待滿足特定條件, 例如群集中有足夠的當前節點, 這可以防止由于某些數據節點斷開連接而產生不必要的數據恢復情況。

    當 master 進程決定恢復元數據時, 它會將請求發送到 masternode 和 MetaData, 以獲取其計算機上的 metadata。對于群集, 將選擇具有最新版本號的元。同樣, 對于每個索引, 將選擇具有最新版本號的元。然后, 群集的元和每個索引組合在一起, 形成最新的元。

    群集狀態更新過程

    現在, 我們將了解群集狀態更新過程, 以了解 es 如何在基于此過程的群集狀態更新過程中保證一致性。

    1. 主過程中不同線程所做的群集狀態更改的原子性保證

    首先, 當不同的線程在主進程中更改 clusterstate 時, 必須保證原子性。假設有兩個線程正在修改群集狀態, 并且它們正在進行自己的更改。如果沒有并發保護, 則最后一個線程提交提交的修改將覆蓋第一個線程提交的修改或導致無效狀態更改。

    若要解決此問題, es 在需要群集狀態更新時將任務提交到 masterservice, 以便 masterservice 僅使用一個線程按順序處理任務。當前 clusterstate 在處理任務時用作任務的執行函數的參數

    2. 確保隨后的變化都是相應的承諾, 而不是在進行集群國家變化后收回

    如您所知, 一旦在節點上提交了新的 meta, 該節點將執行相應的操作, 如刪除分片, 并且無法回滾這些操作。但是, 如果主節點在實現更改時崩潰, 則必須基于新的 meta 更改新生成的 master 節點, 并且不能發生回滾。否則, 盡管無法回滾操作, 但 meta 可能會回滾。實質上, 這意味著不再與元更新保持一致。

    在早期的 es 版本中, 此問題未得到解決。后來引入了兩階段提交 (添加對群集狀態發布的兩個階段提交)。兩階段提交將發布群集狀態進程的 master 拆分為兩個步驟: 首先, 將最新的 clusterstate 發送到所有節點;其次, 一旦超過一半的主節點返回 ack, 提交請求將被發送, 要求節點提交接收到的群集狀態。如果一半以上的節點未返回 ack, 則發布將失敗。此外, 它將丟失主狀態并執行重新聯接以重新加入群集。

    兩階段提交可以解決一致性問題, 例如:

    1. 節點 a 最初是主節點, 但由于某種原因, 節點 b 成為新的主節點。由于心跳或檢測問題, node a 沒有發現更改。
    2. 因此, node a 仍然認為自己是大師, 并像往常一樣發布新的群集狀態。
    3. 但是, 由于節點 b 現在是主節點, 這表明超過一半的主節點認為節點是新的主節點, 因此它們不會返回 ack 到節點。
    4. 由于 node a 無法接收到足夠的 ack, 因此發布將失敗, 并且 node 將丟失主狀態。
    5. 而且, 由于新的群集狀態未在任何節點上提交, 因此不存在不一致之處。

    然而, 這種方法有許多不一致問題, 我們接下來將討論這些問題。

    3. 一致性問題分析

    es 中的原則是, 如果超過一半的 masternode (主符合條件的節點) 接收到新的群集狀態, 則主發送提交請求。如果一半以上的節點被認為已收到新的群集狀態, 則肯定可以提交群集狀態, 并且在任何情況下都不會回滾。

    第1期

    在第一階段, 主節點發送一個新的 clusterstate, 接收它的節點只需在內存中排隊, 然后返回 ack。此過程不會保留, 因此, 即使主節點接收到一半以上的 ack, 我們也不能假定新的 clusterstate 位于所有節點上。

    第2期

    如果在主提交階段只提交幾個節點, 并且將主節點和這些節點劃分為同一個分區時發生網絡分區, 則其他節點可以相互訪問。此時, 其他節點占大多數, 并將選擇一個新的主節點。由于這些節點都沒有提交新的 clusterstate, 因此新的主機將繼續使用更新前設置的 clusterstate, 從而導致元不一致。

    es 仍在跟蹤此錯誤:

    https: //www.elastic.co/guide/en/elasticsearch/resiliency/current/index.html
    
    Repeated network partitions can cause cluster state updates to be lost (STATUS: ONGOING)
    ... This problem is mostly fixed by #20384 (v5.0.0), which takes committed cluster state updates into account during master election. This considerably reduces the chance of this rare problem occurring but does not fully mitigate it. If the second partition happens concurrently with a cluster state update and blocks the cluster state commit message from reaching a majority of nodes, it may be that the in flight update will be lost

    修復最后一種情況需要做大量的工作。我們目前正在研究這個問題, 但還沒有 e t a。

    在什么情況下會出現問題?

    在兩階段提交中, 最重要的條件之一是 “如果包含一半以上的節點, 則返回 ack, 指示已收到此群集狀態”, 這并不能保證任何情況。一方面, 接收集群國家不會持續存在;另一方面, 接收集群國家不會對未來的主選舉產生任何影響, 因為只會考慮承諾的集群國家。

    在兩階段過程中, 只有當主機在一半以上的 masterode (符合主節點) 上提交新的群集狀態, 并且這些節點都已完成新群集狀態的持久性時, 主服務器才會處于安全狀態。如果主機在提交開始和此狀態之間有任何發送失敗, 則可能會發生元不一致。

    解決現有的一致性問題

    由于 es 在元更新方面存在一些一致性問題, 因此這里有一些解決這些問題的方法。

    1. 實現標準化一致性算法, 如 raft

    第一種方法是實現標準化的一致性算法, 如木筏。在上一篇文章中, 我們解釋了 es 選擇算法和木筏算法之間的異同?,F在, 我們將繼續比較 es 元更新過程和木筏日志復制過程。

    相似 之 處:

    提交是在收到來自一半以上節點的響應后執行的。

    差異:

    1. 對于木筏算法, 跟隨器將接收到的日志保留在磁盤上。對于 es, 節點接收 clucerstate, 將其放入內存中的隊列中, 然后立即返回。群集狀態不會持續。
    2. 木筏算法可確保在一半以上的節點響應后可以提交日志。這在 es 中不能保證, 因此可能會出現一些一致性問題。

    上面的比較顯示了 es 和木筏的元更新算法的相似性。然而, 木筏有額外的機制來確保一致性, 而 es 有一些一致性問題。es 正式表示, 需要做大量工作來解決這些問題。

    2. 通過使用其他組件確保元一致性

    例如, 我們可以使用 zookeeper 保存元保存元并確保元一致性。這確實解決了一致性問題, 但性能仍需考慮。例如, 評估當元卷太大時, 元數據是否保存在 zookeeper 中, 或者是否每次都需要請求完整的元數據或差異數據。

    3. 使用共享存儲保存元空間

    首先, 確保不會發生分裂大腦, 然后將元保存在共享存儲中, 以避免任何一致性問題。這種方法需要高度可靠和可用的共享存儲系統。

    總結

    作為本系列關于彈性搜索分布一致性原理的第二篇文章, 本文主要展示 es 集群中的主節點如何發布元更新并分析潛在的一致性問題。本文還介紹了元數據的組成以及元數據的存儲方式。下面的文章介紹了用于確保 es 中數據一致性的方法, 以及數據編寫過程和算法模型。

    引用

    Comments are closed.