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

PolarDB是阿里雲基於MySQL推出的雲原生數據庫(Cloud Native Database)產品,經過將數據庫中計算和存儲分離,多個計算節點訪問同一份存儲數據的方式來解決目前MySQL數據庫存在的運維和擴展性問題;經過引入RDMA和SPDK等新硬件來改造傳統的網絡和IO協議棧來極大提高數據庫性能。表明了將來數據庫發展的一個方向。本系列共2篇文章,主要分析爲何會出現PolarDB以及其技術實現。html


因爲PolarDB並不開源,所以只能基於阿里雲公開的技術資料進行解讀。這些資料包括從去年下半年開始陸續在阿里雲棲社區、雲棲大會等場合發佈的PolarDB相關資料,以及今年以來公開的PolarDB後端共享存儲PolarFS相關文章。node


PolarDB出現背景mysql

MySQL雲服務遇到的問題算法


首先來了解下爲何會出現PolarDB。阿里雲數據庫團隊具有國內領先的技術能力,爲MySQL等數據庫在國內的推廣起到了很大的做用。在阿里雲上也維護了很是龐大的MySQL雲服務(RDS)集羣,但也遇到了不少棘手的問題。舉例以下:sql


  • 實例數據量太大,單實例幾個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)實例,跟ext四、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+款雲產品!

更多網易研發、產品、運營經驗分享請訪問網易雲社區

相關文章:
【推薦】 3分鐘掌握一個有數小技能:收入貢獻分析
【推薦】 手把手帶你打造一個 Android 熱修復框架
【推薦】 kudu 存儲引擎簡析

相關文章
相關標籤/搜索