1. 程式人生 > >阿裏雲PolarDB及其共享存儲PolarFS技術實現分析(上)

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

並發 存儲層 操作 先來 相關操作 關於 vld lan 負載均衡

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的災備節點,也可以是對外提供只讀服務的節點,他們共享一份底層數據。

技術分享圖片

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


PolarDB實現方案

雲原生數據庫

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

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

PolarDB架構

技術分享圖片

上圖為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,均表示一段連續的塊組成的更大的區域,作為分配的基本單位。一張圖可以大致表現各層的關系:

技術分享圖片

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組件解析

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

技術分享圖片

libpfs

libpfs是一個用戶空間文件系統(即上圖User Space File System)庫,負責數據庫IO(File IO)接入。更直觀點,libpfs提供了供計算節點/PolarDB訪問底層存儲的API接口,進行文件讀寫和元數據更新等操作,如下圖所示:

技術分享圖片

pfs_mount()用於將指定卷上文件系統掛載到對應的數據庫計算節點上,該操作會獲取卷上的文件系統元數據信息,將其緩存在計算節點上,這些元數據信息包括目錄樹(the directory tree),文件映射表(the file mapping table)和塊映射表(the block mapping table)等,其中目錄樹描述了文件目錄層級結構信息,每個文件名對應的inode節點信息(目錄項)。inode節點信息就是文件系統中唯一標識一個文件的FileID。文件映射表描述了該文件都有哪些Block組成。通過上圖我們還發現了pfs_mount_growfs(),該API可以讓用戶方便得進行數據庫擴容,在對卷進行擴容後,通過調用該API將增加的空間映射到文件系統層。

技術分享圖片

上圖右側的表描述了目錄樹中的某個文件的前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生態圈

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