天天看點

阿裡雲PolarDB及其共享存儲PolarFS技術實作分析(上)

PolarDB是阿裡雲基于MySQL推出的雲原生資料庫(Cloud Native Database)産品,通過将資料庫中計算和存儲分離,多個計算節點通路同一份存儲資料的方式來解決目前MySQL資料庫存在的運維和擴充性問題;通過引入RDMA和SPDK等新硬體來改造傳統的網絡和IO協定棧來極大提升資料庫性能。代表了未來資料庫發展的一個方向。本系列共2篇文章,主要分析為什麼會出現PolarDB以及其技術實作。

由于PolarDB并不開源,是以隻能基于阿裡雲公開的技術資料進行解讀。這些資料包括從去年下半年開始陸續在阿裡雲栖社群、雲栖大會等場合釋出的PolarDB相關資料,以及今年以來公開的PolarDB後端共享存儲PolarFS相關文章。

PolarDB出現背景

MySQL雲服務遇到的問題

首先來了解下為什麼會出現PolarDB。阿裡雲資料庫團隊具備國内領先的技術能力,為MySQL等資料庫在國内的推廣起到了很大的作用。在阿裡雲上也維護了非常龐大的MySQL雲服務(RDS)叢集,但也遇到了很多棘手的問題。舉例如下:

  • 執行個體資料量太大,單執行個體幾個TB的資料,這樣即使使用xtrabackup實體備份,也需要很長的備份時間,且備份期間寫入量大的話可能導緻redo日志被覆寫引起備份失敗;
  • 大執行個體故障恢複需要重建時,耗時太長,影響服務可用性(此時存活節點也挂了,那麼完蛋了)。時間長有2個原因,一是備份需要很長時間,二是恢複的時候回放redo也需要較長時間;
  • 大執行個體做隻讀擴充麻煩,因為隻讀執行個體的資料是單獨一份的,是以也需要通過備份來重建;
  • RDS執行個體叢集很大,包括成千上萬個執行個體,可能同時有很多執行個體同時在備份,會占用雲服務巨大的網絡和IO帶寬,導緻雲服務不穩定;
  • 雲服務一般使用雲硬碟,導緻資料庫的性能沒有實體機執行個體好,比如IO延時過高;
  • 主庫寫入量大的時候,會導緻主從複制延遲過大,semi-sync/半同步複制也沒法徹底解決,這是由于mysql基于binlog複制,需要走完整的mysql事務處理流程。
  • 對于需要讀寫分離,且要求部署多個隻讀節點的使用者,最明顯的感覺就是每增加一個隻讀執行個體,成本是線性增長的。

其實不僅僅是阿裡雲RDS,網易雲上的RDS服務也有數千個執行個體,同樣遇到了類似的問題,我們是親身經曆而非感同身受。應該說就目前的MySQL技術實作方案,要解決上述任何一個問題都不是件容易的事情,甚至有幾個問題是無法避免的。

現有解決方案及不足

那麼,跳出MySQL,是否有解決方案呢,分析目前業界的資料庫和存儲領域技術,可以發現基于共享存儲是個可選的方案,所謂資料庫共享存儲方案指的是RDS執行個體(一般指一主一從的高可用執行個體)和隻讀執行個體共享同一份資料,這樣在執行個體故障或隻讀擴充時就無需拷貝資料了,隻需簡單得把故障節點重新拉起來,或者建立個隻讀計算節點即可,省時省力更省錢。共享存儲可通過快照技術(snapshot/checkpoint)和寫時拷貝(copy-on-write,COW)來解決資料備份和誤操作恢複問題,将所需備份的資料量分攤到較長的一段時間内,而不需要瞬時完成,這樣就不會導緻多執行個體同時備份導緻網絡和IO資料風暴。下圖就是一個典型的資料庫共享存儲方案,Primary節點即資料庫主節點,對外提供讀寫服務,Read Only節點可以是Primary的災備節點,也可以是對外提供隻讀服務的節點,他們共享一份底層資料。

阿裡雲PolarDB及其共享存儲PolarFS技術實作分析(上)

理想很豐滿,但現實卻很骨感,目前可用的共享存儲方案寥寥無幾,比如在Hadoop生态圈占統治地位的HDFS,以及在通用存儲領域風生水起的Ceph,隻是如果将其作為線上資料處理(OLTP)服務的共享存儲,最終對使用者呈現的性能是不可接受的。除此之外,還存在大量與現有資料庫實作整合适配等問題。

PolarDB實作方案

雲原生資料庫

說道雲原生資料庫,就不得不提Aurora。其在2014年下半年釋出後,轟動了整個資料庫領域。Aurora對MySQL存儲層進行了大刀闊斧的改造,将其拆為獨立的存儲節點(主要做資料塊存儲,資料庫快照的伺服器)。上層的MySQL計算節點(主要做SQL解析以及存儲引擎計算的伺服器)共享同一個存儲節點,可在同一個共享存儲上快速部署新的計算節點,高效解決服務能力擴充和服務高可用問題。基于日志即資料的思想,大大減少了計算節點和存儲節點間的網絡IO,進一步提升了資料庫的性能。再利用存儲領域成熟的快照技術,解決資料庫資料備份問題。被公認為關系型資料庫的未來發展方向之一。截止2018年上半年,Aurora已經實作了多個計算節點同時提供寫服務的能力,繼續在雲原生資料庫上保持領先的地位。

不難推斷,在Aurora釋出3年後推出的PolarDB,肯定對Aurora進行了深入的研究,并借鑒了很多技術實作方法。關于Aurora的分析,國内外,包括公司内都已進行了深入分析,本文不再展開描述。下面着重介紹PolarDB實作。我們采用先存儲後計算的方式,先講清楚PolarFS共享存儲的實作,再分析PolarDB計算層如何适配PolarFS。

PolarDB架構

阿裡雲PolarDB及其共享存儲PolarFS技術實作分析(上)

上圖為PolarFS視角看到的PolarDB實作架構。一套PolarDB至少包括3個部分,分别為最底層的共享存儲,與使用者互動的MySQL節點,還有使用者進行系統管理的PolarCtrl。而其中PolarFS又可進一步拆分為libpfs、PolarSwitch和ChunkServer。下面進行簡單說明:

  • MySQL節點,即圖中的POLARDB,負責使用者SQL解析、事務處理等資料庫相關操作,扮演計算節點角色;
  • libpfs是一個使用者空間檔案系統庫,提供POSIX相容的檔案操作API接口,嵌入到PolarDB負責資料庫IO(File IO)接入;
  • PolarSwitch運作在計算節點主機(Host)上,每個Host部署一個PolarSwitch的守護程序,其将資料庫檔案IO變換為塊裝置IO,并發送到具體的後端節點(即ChunkServer);
  • ChunkServer部署在存儲節點上,用于處理塊裝置IO(Block IO)請求和節點内的存儲資源分布;
  • PolarCtrl是系統的控制平面,PolarFS叢集的控制核心,所有的計算和存儲節點均部署有PolarCtrl的Agent。

PolarFS的存儲組織

與大多數存儲系統一樣,PolarFS對存儲資源也進行了多層封裝和管理,PolarFS的存儲層次包括:Volume、Chunk和Block,分别對應存儲領域中的資料卷,資料區和資料塊,在有些系統中Chunk又被成為Extent,均表示一段連續的塊組成的更大的區域,作為配置設定的基本機關。一張圖可以大緻表現各層的關系:

阿裡雲PolarDB及其共享存儲PolarFS技術實作分析(上)

Volume

當使用者申請建立PolarDB資料庫執行個體時,系統就會為該執行個體建立一個Volume(卷,本文後續将這兩種表達混用),每個卷都有多個Chunk組成,其大小就是使用者指定的資料庫執行個體大小,PolarDB支援使用者建立的執行個體大小範圍是10GB至100TB,滿足絕大部分雲資料庫執行個體的容量要求。

跟其他傳統的塊裝置一樣,卷上的讀寫IO以512B大小對齊,對卷上同個Chunk的修改操作是原子的。當然,卷還是塊裝置層面的概念,在提供給資料庫執行個體使用前,需在卷上格式化一個PolarFS檔案系統(PFS)執行個體,跟ext4、btrfs一樣,PFS上也會在卷上存放檔案系統中繼資料。這些中繼資料包括inode、directory entry和空閑塊等對象。同時,PFS也是一個日志檔案系統,為了實作檔案系統的中繼資料一緻性,中繼資料的更新會首先記錄在卷上的Journal(日志)檔案中,然後才更新指定的中繼資料。

跟傳統檔案系統不一樣的是PolarFS是個共享檔案系統即一個卷會被挂載到多個計算節點上,也就是說可能存在有多個用戶端(挂載點)對檔案系統進行讀寫和更新操作,是以PolarFS在卷上額外維護了一個Paxos檔案。每個用戶端在更新Journal檔案前,都需要使用Paxos檔案執行Disk Paxos算法實作對Journal檔案的互斥通路。更詳細的PolarFS中繼資料更新實作,後續單獨作為一個小節。

Chunk

前面提到,每個卷内部會被劃分為多個Chunk(區),區是資料分布的最小粒度,每個區都位于單塊SSD盤上,其目的是利于資料高可靠和高可用的管理,詳見後續章節。每個Chunk大小設定為10GB,遠大于其他類似的存儲系統,例如GFS為64MB,Linux LVM的實體區(PE)為4MB。這樣做的目的是減少卷到區映射的中繼資料量大小(例如,100TB的卷隻包含10K個映射項)。一方面,全局中繼資料的存放和管理會更容易;另一方面,中繼資料可以全都緩存在記憶體中,避免關鍵IO路徑上的額外中繼資料通路開銷。

當然,Chunk設定為10GB也有不足。當上層資料庫應用出現區域級熱點通路時,Chunk内熱點無法進一步打散,但是由于每個存儲節點提供的Chunk數量往往遠大于節點數量(節點:Chunk在1:1000量級),PolarFS支援Chunk的線上遷移,其上服務着大量資料庫執行個體,是以可以将熱點Chunk分布到不同節點上以獲得整體的負載均衡。

在PolarFS上,卷上的每個Chunk都有3個副本,分布在不同的ChunkServer上,3個副本基于ParallelRaft分布式一緻性協定來保證資料高可靠和高可用。

Block

在ChunkServer内,Chunk會被進一步劃分為163,840個Block(塊),每個塊大小為64KB。Chunk至Block的映射資訊由ChunkServer自行管理和儲存。每個Chunk除了用于存放資料庫資料的Block外,還包含一些額外Block用來實作預寫日志(Write Ahead Log,WAL)。

需要注意的是,雖然Chunk被進一步劃分為塊,但Chunk内的各個Block在SSD盤是實體連續的。PolarFS的VLDB文章裡提到“Blocks are allocated and mapped to a chunk on demand to achieve thin provisioning”。thin provisioning就是精簡配置,是存儲上常用的技術,就是使用者建立一個100GB大小的卷,但其實在卷建立時并沒有實際配置設定100GB存儲空間給它,僅僅是邏輯上為其建立10個Chunk,随着使用者資料不斷寫入,PolarFS不斷配置設定實體存儲空間供其使用,這樣能夠實作存儲系統按需擴容,大大節省存儲成本。

那麼為何PolarFS要引入Block這個概念呢,其中一個是跟卷上的具體檔案相關,我們知道一個檔案系統會有多個檔案,比如InnoDB資料檔案*.ibd。每個檔案大小會動态增長,檔案系統采用預配置設定(fallocate())為檔案提前配置設定更多的空間,這樣在真正寫資料的時無需進行檔案系統中繼資料操作,進而優化了性能。顯然,每次給檔案配置設定一個Chunk,即10GB空間是不合理的,64KB或其倍數才是合适的值。上面提到了精簡配置和預配置設定,看起來是沖突的方法,但其實是統一的,精簡配置的粒度比預配置設定的粒度大,比如精簡配置了10GB,預配置設定了64KB。這樣對使用者使用沒有任何影響,同時還節省了存儲成本。

PolarFS元件解析

首先展示一張能夠更加清晰描述與資料流相關的各個元件作用的示意圖,并逐一對其進行解釋。

阿裡雲PolarDB及其共享存儲PolarFS技術實作分析(上)

libpfs

libpfs是一個使用者空間檔案系統(即上圖User Space File System)庫,負責資料庫IO(File IO)接入。更直覺點,libpfs提供了供計算節點/PolarDB通路底層存儲的API接口,進行檔案讀寫和中繼資料更新等操作,如下圖所示:

阿裡雲PolarDB及其共享存儲PolarFS技術實作分析(上)

pfs_mount()用于将指定卷上檔案系統挂載到對應的資料庫計算節點上,該操作會擷取卷上的檔案系統中繼資料資訊,将其緩存在計算節點上,這些中繼資料資訊包括目錄樹(the directory tree),檔案映射表(the file mapping table)和塊映射表(the block mapping table)等,其中目錄樹描述了檔案目錄層級結構資訊,每個檔案名對應的inode節點資訊(目錄項)。inode節點資訊就是檔案系統中唯一辨別一個檔案的FileID。檔案映射表描述了該檔案都有哪些Block組成。通過上圖我們還發現了pfs_mount_growfs(),該API可以讓使用者友善得進行資料庫擴容,在對卷進行擴容後,通過調用該API将增加的空間映射到檔案系統層。

阿裡雲PolarDB及其共享存儲PolarFS技術實作分析(上)

上圖右側的表描述了目錄樹中的某個檔案的前3個塊分别對應的是卷的第348,1500和201這幾個塊。假如資料庫操作需要回刷一個髒頁,該頁在該表所屬檔案的偏移位置128KB處,也就是說要寫該檔案偏移128KB開始的16KB資料,通過檔案映射表知道該寫操作其實寫的是卷的第201個塊。這就是lipfs發送給PolarSwitch的請求包含的内容:volumeid,offset和len。其中offset就是201*64KB,len就是16KB。

PolarSwitch

PolarSwitch是部署在計算節點的Daemon,即上圖的Data Router&Cache子產品,它負責接收libpfs發送而來的檔案IO請求,PolarSwitch将其劃分為對應的一到多個Chunk,并将請求發往Chunk所屬的ChunkServer完成通路。具體來說PolarSwitch根據自己緩存的volumeid到Chunk的映射表,知道該檔案請求屬于那個Chunk。請求如果跨Chunk的話,會将其進一步拆分為多個塊IO請求。PolarSwitch還緩存了該Chunk的三個副本分别屬于那幾個ChunkServer以及哪個ChunkServer是目前的Leader節點。PolarSwitch隻将請求發送給Leader節點。

ChunkServer

ChunkServer部署在存儲節點上,即上圖的Data Chunk Server,用于處理塊IO(Block IO)請求和節點内的存儲資源分布。一個存儲節點可以有多個ChunkServer,每個ChunkServer綁定到一個CPU核,并管理一塊獨立的NVMe SSD盤,是以ChunkServer之間沒有資源競争。

ChunkServer負責存儲Chunk和提供Chunk上的IO随機通路。每個Chunk都包括一個WAL,對Chunk的修改會先寫Log再執行修改操作,保證資料的原子性和持久性。ChunkServer使用了3D XPoint SSD和普通NVMe SSD混合型WAL buffer,Log會優先存放到更快的3DXPoint SSD中。

前面提到Chunk有3副本,這三個副本基于ParallelRaft協定,作為該Chunk Leader的ChunkServer會将塊IO請求發送給Follow節點其他ChunkServer)上,通過ParallelRaft一緻性協定來保證已送出的Chunk資料不丢失。

PolarCtrl

PolarCtrl是系統的控制平面,相應地Agent代理被部署到所有的計算和存儲節點上,PolarCtrl與各個節點的互動通過Agent進行。PolarCtrl是PolarFS叢集的控制核心,後端使用一個關系資料庫雲服務來管理PolarDB的中繼資料。其主要職責包括:

  • 監控ChunkServer的健康狀況,包括剔除出現故障的ChunkServer,維護Chunk多個副本的關系,遷移負載過高的ChunkServer上的部分Chunk等;
  • Volume建立及Chunk的布局管理,比如Volume上的Chunk應該配置設定到哪些ChunkServer上;
  • Volume至Chunk的中繼資料資訊維護;
  • 向PolarSwitch推送元資訊緩存更新,比如因為計算節點執行DDL導緻卷上檔案系統中繼資料更新,這些更新可通過PolarCtrl推送給PolarSwitch;
  • 監控Volume和Chunk的IO性能,根據一定的規則進行遷移操作;
  • 周期性地發起副本内和副本間的CRC資料校驗。

本篇主要是介紹了PolarDB資料庫及其後端共享存儲PolarFS系統的基本架構群組成子產品,是最基礎的部分。下一篇重點分析PolarFS的資料IO流程,中繼資料更新流程,以及PolarDB資料庫節點如何适配PolarFS這樣的共享存儲系統。

本文來自網易雲社群 ,經作者溫正湖授權釋出。

網易雲免費體驗館,0成本體驗20+款雲産品!

更多網易研發、産品、營運經驗分享請通路網易雲社群。

相關文章:

【推薦】 Vue架構核心之資料劫持

【推薦】 vue生态圈

繼續閱讀