深度乾貨 | OceanBase 主動切主技術解讀

OceanBase資料庫發表於2021-11-10
OceanBase(以下以 OB 簡稱)中有一個總控節點服務稱之為 RootService,用於管理整個叢集中的負載均衡等操作。該服務啟動在  __all_core_table  的主副本(Leader)所在的伺服器上。本文主要介紹 RootService(以下以 RS 簡稱)對切主的管理。主要分為以下五個部分:

  • 涉及到的基礎概念

  • 負載均衡場景描述

  • 對主動切主的管理

  • 切主是如何觸發的

  • 切主是如何執行的


涉及到的基礎概念


我們先來認識或回顧下 5 個本文會涉及到的基本概念:

1.1. 主副本(Leader)

OB 通過 Multi-Paxos 選舉協議來保證叢集內資料的高可用。 選舉以分割槽為單位,每個分割槽的多個資料副本形成一個獨立的 Paxos 組,其中只有一個分割槽副本會被選舉為主副本(Leader,可讀可寫),其餘通常情況下是從副本(Follower)。

切主,指的是資料分割槽副本角色在主副本和從副本間的切換,即 OB 對各個分割槽的主副本位置的調整。

1.2. 均衡組

租戶內的切主是以均衡組為維度進行的,而不是簡單的將該租戶下的所有分割槽的leader打散。

目前,均衡組有以下 3 種存在形態:

  • PARTITION_TABLE_BALANCE_GROUP

針對不在表組(TableGroup)中的多分割槽表,每個分割槽表自己是一個均衡組。

  • TABLE_GROUP_BALANCE_GROUP

針對在表組(TableGroup)中的多分割槽表,每個表組是一個均衡組。

  • NON_PARTITION_TABLE_BALANCE_GROUP

如果是隻有一個分割槽的分割槽表、單分割槽表或非分割槽表,會統一歸入一個均衡組。

1.3. 租戶(Tenant)

OB 實現了租戶級別的資源隔離,每個資料庫租戶例項不會感知其他例項的存在,並通過綜合的許可權控制確保租戶資料的安全性。

1.4. 首選區(Primary Zone)

如果你還不太瞭解 OB 關於區(Zone)的概念,請查閱 OB 官方手冊。

首選區可用以指定分割槽主副本在選擇 Zone 時的優先順序順序。OB 可在租戶級別指定資料副本 Leader 在 Zone 間的分佈策略。此外,向下遞進,OB 允許在 Scheme 級別(MySQL 中術語是 Database 級別)、TableGroup級別、直至表級別上配置首選區。建立租戶時,屬性  primary_zone  預設為  RANDOM ;其它級別該屬性預設為依次向上繼承,直到發現非空屬性:表 ➡︎(TableGroup 或 Schema)➡︎ 租戶。如果對該屬性有顯式制定,以顯式指定為準。

以下例來具體解讀 Zone 的優先策略:

    primary_zone = zone1,zone2;zone3,zone4;zone5

    注意標點符號的文法含義:逗號代表兩邊優先順序相同;分號代表前者優先順序更高、後者次之。

    這個例子中,五個 Zone 優先順序從高到低被分號劃分為三個級別:前兩個  zone1  和  zone2  (以逗號隔開)的優先順序相同且優先順序最高, zone3  和  zone4  (以逗號隔開)的優先順序並列,最後面的  zone5  的優先順序最低。

    1.5. 首選域(Primary Region)

    OB 預設將 Primary Zone 所在的地區(Region,例如北京或上海)指定為 Primary Region。每個 Primary Region 至少要有兩個 Zone,這是出於可用和效能考慮 OB 做出的硬性限制,這有助於避免跨 Region 切主導致的效能劣化。

    舉例,若三個 Zone 分別屬於不同的地區(如杭州、上海和深圳),那麼,

    不可取的設定是隻將一個 Zone 設定成 Primary Region:

    • zone1; zone2; zone3

    • zone1; zone2, zone3


    可行的設定為:

    • random  (預設設定)

    • zone1,zone2,zone3  (效果和  random  相同)

    • zone1,zone2;zone3


    負載分攤場景描述


    我們通過代入一個具體場景來簡單介紹資料副本 Leader 的負載分攤。

    在同一 Region,新建一個擁有 3 個 Zone 的叢集,每個 Zone 中都包含 3 臺伺服器(OBServer)。

    建立一個資料庫租戶,該租戶的資料根據資源配置允許分佈在全部 9 臺伺服器上。

    假設建立租戶時 Primary Zone 沒有指定,則其預設採用 RANDOM 策略。

    隨後,使用者在該租戶下建立了一張擁有 9 個分割槽的分割槽表,未指定 Primary Zone。

    此時分割槽表的 primary_zone 屬性繼承租戶的 RANDOM 預設配置。

    RS 會將表的 9 個分割槽的 Leader 均勻打散到 3 個 Zone 的 9 臺伺服器上。

    分割槽的 Leade r 分佈如下:


    當然,如果業務需要,使用者也可選擇將 Leader 都集中布在某個 Zone 上。比如,我們選擇把這張表所有分割槽 Leader 都布在 Zone1,具體做法:在建立表時直接指定  primary_zone='zone1' ;或,在已存在的租戶上調整  primary_zone  配置。

    新的分割槽 Leader 分佈情況如下:


    對主動切主的管理


    RS 是根據當前叢集的狀態來主動決定各個租戶的 Leader 分佈。其中叢集的狀態包括但不限於 Zone 狀態、伺服器狀態、是否發生遷移、租戶是否發生 Locality 變更、管理員主動發起切主等。OB 中不同租戶相互間資源隔離、相互不影響,同樣切主行為也是各個租戶相互獨立的,因此 RS 對切主的管理是以租戶為維度進行的。

    OB 中強讀和寫都是在 Leader上進行的。若分割槽 Leader 沒有打散而是聚集在個別幾臺伺服器上,大量的 IO 會導致這些伺服器的效能很快達到瓶頸。有時使用者希望利用所有的伺服器來避免達到瓶頸,將 
    primary_zone  設定成  RANDOM ,語義就是將 Leader 均勻地打散到該租戶下所有可用  Zone  的所有可用伺服器上。當然,有時使用者就是希望所有的 Leader 都集中在一兩個 Zone 上,以避免發生跨城市切主,造成訪問延遲,對業務產生不良影響。因此,RS 對切主的管理,首要還是根據使用者的需要,結合當前叢集的狀態,來調整各個租戶下所有分割槽的 Leader 分佈。

    切主是如何觸發的


    OB 中有一套完整的無主選舉的邏輯,當網路不穩定、伺服器當機等非預期的情況導致的各種無主場景,會由選舉(Election)模組進行無主選舉。本文並不討論無主選舉的場景,只討論預期內的切主,即所有的切主情況都是在叢集正常情況下。

    RS 管理切主有一個重要的配置項,叫做  enable_auto_leader_switch ,該配置項決定是否執行 RS 的後臺切主執行緒。

      Alter system set enable_auto_leader_switch = true/false;

      下面的操作 4.1 和 4.2 都不依賴這個開關,即使開關關閉,操作都是可以正常進行的,但其餘的操作都依賴這個開關,當開關關閉時,都不能正常執行切主操作。

      4.1. 手動切主

      管理員可手動觸發對某個分割槽的切主,指定其 Leader 或 Follower 的位置。

      參考命令:

        alter system switch replica leader/follower partition = '$partition_id' server = '$server_ip:$server_port';alter system switch replica leader/follower zone = '$zone_name' tenant = '$tenant_name';alter system switch replica leader/follower server = '$server_ip:$server_port' tenant = '$tenant_name';

        ?注意:該操作不依賴配置項  enable_auto_leader_switch

        4.2. 容災操作

        容災操作包括 locality 變更、資源(resource unit)遷移、緊急停機(stop server/zone)及伺服器隔離(isolate/fence)。其中對於 locality 變更和資源遷移,當操作的資料副本是 Leader 時,會先行切主,繼而進行相關的操作,當變更和遷移完成後再根據當前環境來決定是否需要將主切回。緊急停機及伺服器隔離都會先行切主,該命令是阻塞的,即需要等待全部 Leader 都切走後,才能完成相關操作。

        注意:上述操作也不依賴配置項  enable_auto_leader_switch

        4.3. 修改 Primary Zone

        在物件建立時或建立好後, primary_zone  屬性都是可以動態調整的。

        參考命令:
        alter tenant/database/tablegroup/table $name primary_zone = '$new_pz_strategy';

        4.4. 自動均衡

        以均衡組為維度對租戶進行自動均衡,OB 中均衡的含義是不同 Zone、不同伺服器之間的 Leader 數相差不超過 1。由於均衡是以均衡組為維度,因此具體到一個租戶下的 Leader 分佈可能不是完全均衡的。

        舉例說明,叢集有 3 個 Zone,每個 Zone 只有一臺伺服器。某租戶現有 3 張分割槽表,每張表擁有 10 個分割槽。每個表自己是一個均衡組,那麼每個表的 10 個分割槽副本 Leader 均衡分佈為4:3:3。如果每個均衡組將 4 個 Leader 的 Zone 都指定為Zone1,那麼雖然每個均衡組自己內部是均衡的,但租戶層面上 Leader 的分佈是不均衡的,因為該租戶分割槽副本 Leader 的分佈為 12:9:9。

        當然內部進行了一些優化,在建立表的時候,會指定好每個表的 Leader 分佈。對於非分割槽表均衡組的指定是根據當前的非分割槽表分佈,來決定新表的 Leader 分佈。而對於分割槽表,在建立時是根據表的 ID 編號,計算出其 Leader 的分佈,如果是連續的表,那麼其 Leader 位置是錯開的,比如 1 號表的起始 Leader 在 Zone1 上,那麼 2 號表的起始 Leader 是在 Zone2 上,依次類推。從而儘量避免在整體上出現 Leader 分佈不均衡的問題。

        4.5. 輪轉合併

        當開啟輪狀合併開關時,對於合併目標 Zone,會首先將該 Zone 上的 Leader 都切走,然後才開始對該 Zone 的合併。

        切主是如何執行的

        5.1. Leader 的分配原則

        首先介紹一下 RS 的切主原則。

        叢集中可能出現的一種場景是雖然設定了 Primary Zone,但當前的 Leader 分佈卻與設定不符,這是由於 RS 決定 Leader 分佈不僅僅只參考 Primary Zone 資訊。

        RS 會獲取每個分割槽的每個副本的相關資訊,綜合分析後決定 Leader 職責到底分配在哪個資料副本上。

        下面是幾個比較關鍵的參考資訊:

        • 伺服器或 Zone 的可用狀態

        • Primary Region 的設定

        • 合併狀態

        • 候選數量(candidate_cnt)

        • 拉黑列表(In_blacklist)

        • Primary Zone 的設定

        這些副本的資訊從上到下優先順序逐漸降低。

        1、最先看這個副本所在的伺服器或 Zone 是否可用,若處於停止狀態,則不能將 Leader 切到該副本上;

        2、Primary Region 就是 Primary Zone 所在的地區,RS 會優先將 Leader 切到 Primary Region 中;

        3、檢視副本是否處於合併狀態,RS 不傾向於將副本的 Leader 切到處於合併狀態的 Zone 上;

        4、候選數量(candidate_cnt)是由 clog 層返回給 RS 分割槽候選 Leader 的伺服器列表,RS 根據列表計算出每個伺服器能夠作為候選分割槽的數量,數量越大,則該伺服器上的副本越有可能成為 Leader;

        5、拉黑列表(in_blacklist)代表該伺服器是否在黑名單中,當該分割槽發生無故卸任時,clog 會將原來 Leader 所在的伺服器放入到黑名單中,RS 會根據這份黑名單拉黑對應伺服器的 Leader 候選資格;

        6、最後才是 Primary Zone。需要通過前面的 5 項判斷,RS 才會聽取 Primary Zone 的建議。

        這裡沒有完整羅列 RS 的全部檢查,但已經很清楚,有各種各樣的因素會導致 RS 沒有按照 Primary Zone 去分配 Leader。

        5.2. Leader 的切換流程

        在啟動 RS 服務時,會建立一個切主管理的執行緒。當自動切主開關開啟時,該執行緒會一直在後臺輪詢遍歷所有的租戶,每個租戶的切主流程是一致的,下面以一個租戶為例,介紹一下租戶的切主流程。

        首先通過遍歷內部表的方式,獲取這個租戶下所有的分割槽資訊,包括比較優先順序時需要的各種副本資訊。RS 是基於一個時間點獲取的租戶下所有分割槽資訊,由於叢集可能是在變化的,因此獲取的資訊可能會過期失效,根據過期的資訊得到的切主方案可能也是不準確的。幸運的是 RS 也考慮到這一點,切主管理執行緒在後臺不斷輪詢、更新資訊,即使有變化,但當叢集最終達到穩態時,也可保證 Leader 分佈是符合預期的。

        隨後根據獲取到的資訊,按照均衡組,比較分割槽各個副本的優先順序,決定每個均衡組中的分割槽是否需要進行切主。如果通過比較沒有找到需要切主的分割槽,那麼後續的所有操作都跳過,直接遍歷下一個租戶。否則,根據 RS 生成的切主資訊,向底層傳送 RPC 訊息,來獲取和確認當前真實準確的副本資訊,從而進一步判斷是否真的需要進行切主:如果不需要切,那麼直接結束;否則,就根據需要進行切主的資訊,給當前的 Leader 傳送切主命令 —— 命令其卸任。

        最後等待所有的分割槽切主完成,等待的方法就是檢查內部表,檢查待切主分割槽的 Leader 是否在預期的位置,如果與預期相符,則認為該分割槽切主成功。否則,繼續等待。限定每個租戶最長只會等待 64 秒,若超過這個時間還沒有檢查到對應分割槽的 Leader 在預期的伺服器上,就會強行結束該租戶的等待,進行下一個租戶的遍歷。失敗的租戶,等待下一輪遍歷到該租戶時再重複進行同樣的切主操作。

        來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69909943/viewspace-2841484/,如需轉載,請註明出處,否則將追究法律責任。

        相關文章