三篇文章瞭解 TiDB 技術內幕 —— 談排程
任何一個複雜的系統,使用者感知到的都只是冰山一角,資料庫也不例外。
前兩篇文章介紹了 TiKV、TiDB 的基本概念以及一些核心功能的實現原理,這兩個元件一個負責 KV 儲存,一個負責 SQL 引擎,都是大家看得見的東西。在這兩個元件的後面,還有一個叫做 PD(Placement Driver)的元件,雖然不直接和業務接觸,但是這個元件是整個叢集的核心,負責全域性元資訊的儲存以及 TiKV 叢集負載均衡排程。
本篇文章介紹一下這個神祕的模組。這部分比較複雜,很多東西大家平時不會想到,也很少在其他文章中見到類似的東西的描述。我們還是按照前兩篇的思路,先講我們需要什麼樣的功能,再講我們如何實現,大家帶著需求去看實現,會更容易的理解我們做這些設計時背後的考量。
為什麼要進行排程
先回憶一下第一篇文章提到的一些資訊,TiKV 叢集是 TiDB 資料庫的分散式 KV 儲存引擎,資料以 Region 為單位進行復制和管理,每個 Region 會有多個 Replica(副本),這些 Replica 會分佈在不同的 TiKV 節點上,其中 Leader 負責讀/寫,Follower 負責同步 Leader 發來的 raft log。瞭解了這些資訊後,請思考下面這些問題:
- 如何保證同一個 Region 的多個 Replica 分佈在不同的節點上?更進一步,如果在一臺機器上啟動多個 TiKV 例項,會有什麼問題?
- TiKV 叢集進行跨機房部署用於容災的時候,如何保證一個機房掉線,不會丟失 Raft Group 的多個 Replica?
- 新增一個節點進入 TiKV 叢集之後,如何將叢集中其他節點上的資料搬過來?
- 當一個節點掉線時,會出現什麼問題?整個叢集需要做什麼事情?如果節點只是短暫掉線(重啟服務),那麼如何處理?如果節點是長時間掉線(磁碟故障,資料全部丟失),需要如何處理?
- 假設叢集需要每個 Raft Group 有 N 個副本,那麼對於單個 Raft Group 來說,Replica 數量可能會不夠多(例如節點掉線,失去副本),也可能會 過於多(例如掉線的節點又回覆正常,自動加入叢集)。那麼如何調節 Replica 個數?
- 讀/寫都是通過 Leader 進行,如果 Leader 只集中在少量節點上,會對叢集有什麼影響?
- 並不是所有的 Region 都被頻繁的訪問,可能訪問熱點只在少數幾個 Region,這個時候我們需要做什麼?
- 叢集在做負載均衡的時候,往往需要搬遷資料,這種資料的遷移會不會佔用大量的網路頻寬、磁碟 IO 以及 CPU?進而影響線上服務?
這些問題單獨拿出可能都能找到簡單的解決方案,但是混雜在一起,就不太好解決。有的問題貌似只需要考慮單個 Raft Group 內部的情況,比如根據副本數量是否足夠多來決定是否需要新增副本。但是實際上這個副本新增在哪裡,是需要考慮全域性的資訊。整個系統也是在動態變化,Region 分裂、節點加入、節點失效、訪問熱點變化等情況會不斷髮生,整個排程系統也需要在動態中不斷向最優狀態前進,如果沒有一個掌握全域性資訊,可以對全域性進行排程,並且可以配置的元件,就很難滿足這些需求。因此我們需要一箇中心節點,來對系統的整體狀況進行把控和調整,所以有了 PD 這個模組。
排程的需求
上面羅列了一大堆問題,我們先進行分類和整理。總體來看,問題有兩大類:
作為一個分散式高可用儲存系統,必須滿足的需求,包括四種:
- 副本數量不能多也不能少
- 副本需要分佈在不同的機器上
- 新加節點後,可以將其他節點上的副本遷移過來
- 節點下線後,需要將該節點的資料遷移走
作為一個良好的分散式系統,需要優化的地方,包括:
- 維持整個叢集的 Leader 分佈均勻
- 維持每個節點的儲存容量均勻
- 維持訪問熱點分佈均勻
- 控制 Balance 的速度,避免影響線上服務
- 管理節點狀態,包括手動上線/下線節點,以及自動下線失效節點
滿足第一類需求後,整個系統將具備多副本容錯、動態擴容/縮容、容忍節點掉線以及自動錯誤恢復的功能。滿足第二類需求後,可以使得整體系統的負載更加均勻、且可以方便的管理。
為了滿足這些需求,首先我們需要收集足夠的資訊,比如每個節點的狀態、每個 Raft Group 的資訊、業務訪問操作的統計等;其次需要設定一些策略,PD 根據這些資訊以及排程的策略,制定出儘量滿足前面所述需求的排程計劃;最後需要一些基本的操作,來完成排程計劃。 排程的基本操作
我們先來介紹最簡單的一點,也就是排程的基本操作,也就是為了滿足排程的策略,我們有哪些功能可以用。這是整個排程的基礎,瞭解了手裡有什麼樣的錘子,才知道用什麼樣的姿勢去砸釘子。
上述排程需求看似複雜,但是整理下來最終落地的無非是下面三件事:
- 增加一個 Replica
- 刪除一個 Replica
- 將 Leader 角色在一個 Raft Group 的不同 Replica 之間 transfer
剛好 Raft 協議能夠滿足這三種需求,通過 AddReplica、RemoveReplica、TransferLeader 這三個命令,可以支撐上述三種基本操作。
資訊收集
排程依賴於整個叢集資訊的收集,簡單來說,我們需要知道每個 TiKV 節點的狀態以及每個 Region 的狀態。TiKV 叢集會向 PD 彙報兩類訊息:
每個 TiKV 節點會定期向 PD 彙報節點的整體資訊
TiKV 節點(Store)與 PD 之間存在心跳包,一方面 PD 通過心跳包檢測每個 Store 是否存活,以及是否有新加入的 Store;另一方面,心跳包中也會攜帶這個 [Store 的狀態資訊][1],主要包括:
- 總磁碟容量
- 可用磁碟容量
- 承載的 Region 數量
- 資料寫入速度
- 傳送/接受的 Snapshot 數量(Replica 之間可能會通過 Snapshot 同步資料)
- 是否過載
- 標籤資訊(標籤是具備層級關係的一系列 Tag)
每個 Raft Group 的 Leader 會定期向 PD 彙報資訊
每個 Raft Group 的 Leader 和 PD 之間存在心跳包,用於彙報這個 [Region][2] 的狀態,主要包括下面幾點資訊:
- Leader 的位置
- Followers 的位置
- 掉線 Replica 的個數
- 資料寫入/讀取的速度
PD 不斷的通過這兩類心跳訊息收集整個叢集的資訊,再以這些資訊作為決策的依據。除此之外,PD 還可以通過管理介面接受額外的資訊,用來做更準確的決策。比如當某個 Store 的心跳包中斷的時候,PD 並不能判斷這個節點是臨時失效還是永久失效,只能經過一段時間的等待(預設是 30 分鐘),如果一直沒有心跳包,就認為是 Store 已經下線,再決定需要將這個 Store 上面的 Region 都排程走。但是有的時候,是運維人員主動將某臺機器下線,這個時候,可以通過 PD 的管理介面通知 PD 該 Store 不可用,PD 就可以馬上判斷需要將這個 Store 上面的 Region 都排程走。
排程的策略
PD 收集了這些資訊後,還需要一些策略來制定具體的排程計劃。
一個 Region 的 Replica 數量正確
當 PD 通過某個 Region Leader 的心跳包發現這個 Region 的 Replica 數量不滿足要求時,需要通過 Add/Remove Replica 操作調整 Replica 數量。出現這種情況的可能原因是:
- 某個節點掉線,上面的資料全部丟失,導致一些 Region 的 Replica 數量不足
- 某個掉線節點又恢復服務,自動接入叢集,這樣之前已經補足了 Replica 的 Region 的 Replica 數量多過,需要刪除某個 Replica
- 管理員調整了副本策略,修改了 [max-replicas][3] 的配置
一個 Raft Group 中的多個 Replica 不在同一個位置
注意第二點,『一個 Raft Group 中的多個 Replica 不在同一個位置』,這裡用的是『同一個位置』而不是『同一個節點』。在一般情況下,PD 只會保證多個 Replica 不落在一個節點上,以避免單個節點失效導致多個 Replica 丟失。在實際部署中,還可能出現下面這些需求:
- 多個節點部署在同一臺物理機器上
- TiKV 節點分佈在多個機架上,希望單個機架掉電時,也能保證系統可用性
- TiKV 節點分佈在多個 IDC 中,向單個機房掉電時,也能保證系統可用
這些需求本質上都是某一個節點具備共同的位置屬性,構成一個最小的容錯單元,我們希望這個單元內部不會存在一個 Region 的多個 Replica。這個時候,可以給節點配置 [lables][4] 並且通過在 PD 上配置 [location-labels][5] 來指名哪些 lable 是位置標識,需要在 Replica 分配的時候儘量保證不會有一個 Region 的多個 Replica 所在結點有相同的位置標識。
副本在 Store 之間的分佈均勻分配
前面說過,每個副本中儲存的資料容量上限是固定的,所以我們維持每個節點上面,副本數量的均衡,會使得總體的負載更均衡。
Leader 數量在 Store 之間均勻分配
Raft 協議要讀取核寫入都通過 Leader 進行,所以計算的負載主要在 Leader 上面,PD 會盡可能將 Leader 在節點間分散開。
訪問熱點數量在 Store 之間均勻分配
每個 Store 以及 Region Leader 在上報資訊時攜帶了當前訪問負載的資訊,比如 Key 的讀取/寫入速度。PD 會檢測出訪問熱點,且將其在節點之間分散開。
各個 Store 的儲存空間佔用大致相等
每個 Store 啟動的時候都會指定一個 Capacity 引數,表明這個 Store 的儲存空間上限,PD 在做排程的時候,會考慮節點的儲存空間剩餘量。
控制排程速度,避免影響線上服務
排程操作需要耗費 CPU、記憶體、磁碟 IO 以及網路頻寬,我們需要避免對線上服務造成太大影響。PD 會對當前正在進行的運算元量進行控制,預設的速度控制是比較保守的,如果希望加快排程(比如已經停服務升級,增加新節點,希望儘快排程),那麼可以通過 pd-ctl 手動加快排程速度。
支援手動下線節點
當通過 pd-ctl 手動下線節點後,PD 會在一定的速率控制下,將節點上的資料排程走。當排程完成後,就會將這個節點置為下線狀態。
排程的實現
瞭解了上面這些資訊後,接下來我們看一下整個排程的流程。
PD 不斷的通過 Store 或者 Leader 的心跳包收集資訊,獲得整個叢集的詳細資料,並且根據這些資訊以及排程策略生成排程操作序列,每次收到 Region Leader 發來的心跳包時,PD 都會檢查是否有對這個 Region 待進行的操作,通過心跳包的回覆訊息,將需要進行的操作返回給 Region Leader,並在後面的心跳包中監測執行結果。注意這裡的操作只是給 Region Leader 的建議,並不保證一定能得到執行,具體是否會執行以及什麼時候執行,由 Region Leader 自己根據當前自身狀態來定。 總結
本篇文章講的東西,大家可能平時很少會在其他文章中看到,每一個設計都有背後的考量,希望大家能瞭解到一個分散式儲存系統在做排程的時候,需要考慮哪些東西,如何將策略、實現進行解耦,更靈活的支援策略的擴充套件。
至此三篇文章已經講完,希望大家能夠對整個 TiDB 的基本概念和實現原理有了解,後續我們還會寫更多的文章,從架構以及程式碼級別介紹 TiDB 的更多內幕。如果大家有問題,歡迎發郵件到 shenli@pingcap.com 進行交流。(文/申礫)
相關文章
- TiDB 技術內幕 - 談排程TiDB
- 三篇文章瞭解 TiDB 技術內幕——說儲存TiDB
- TiDB 技術內幕 - 說儲存TiDB
- 技術分享| 淺談排程平臺設計
- WebKit技術內幕WebKit
- 一篇文章瞭解爬蟲技術現狀爬蟲
- 魅族C++協程框架(Kiev)技術內幕C++框架
- newsql新品TiDB之排程SQLTiDB
- 以TiDB熱點問題來談Region的排程流程TiDB
- Cloud + TiDB 技術解讀CloudTiDB
- ShowMeBug 核心技術內幕
- 讀《etcd 技術內幕》
- 揭祕《Arduino技術內幕》UI
- 併發技術4:同步排程
- 簡述Spring技術內幕Spring
- MFC技術內幕簡結 (轉)
- 「NGW」前端新技術賽場:Serverless SSR 技術內幕前端Server
- 三分鐘,快速瞭解區塊鏈技術!區塊鏈
- 《深入分析JavaWeb技術內幕》之讀書筆記(篇三)JavaWeb筆記
- 技術解讀 | SD-WAN的多樣性策略排程
- Mybatis技術內幕(2.3.3):反射模組-InvokerMyBatis反射
- Mybatis技術內幕(2.3.4):反射模組-ObjectFactoryMyBatis反射Object
- Mybatis技術內幕(2.3.1):反射模組-ReflectorMyBatis反射
- Mybatis技術內幕(1):Mybatis簡介MyBatis
- [Mysql技術內幕]Innodb儲存引擎MySql儲存引擎
- PostgreSQL技術內幕(七)索引掃描SQL索引
- Mysql技術內幕之InnoDB鎖探究MySql
- [React技術內幕] setState的祕密React
- 深入分析java web技術內幕JavaWeb
- MySQL技術內幕:InnoDB儲存引擎MySql儲存引擎
- Kotlin Coroutine(協程): 三、瞭解協程Kotlin
- 一篇文章帶你瞭解——Kotlin協程Kotlin
- Mysql技術內幕InnoDB儲存引擎讀書筆記--《三》檔案MySql儲存引擎筆記
- 深度解讀昇騰CANN模型下沉技術,提升模型排程效能模型
- 也談goroutine排程器Go
- 閃回技術全瞭解
- Mybatis技術內幕(2.3.6):反射模組-WrapperMyBatis反射APP
- Mybatis技術內幕(2.3.7):反射模組-TypeParameterResolverMyBatis反射