三大云原生數(shù)據(jù)庫:Aurora, PolarDB 與 Socrates
1.何謂「云原生數(shù)據(jù)庫」
云計(jì)算的出現(xiàn),加速了企業(yè)信息技術(shù)的發(fā)展。云計(jì)算供應(yīng)商將海量的計(jì)算、存儲、通信資源放入「池子」中,企業(yè)或個(gè)人用戶按需購買計(jì)算資源,快速、低成本搭建信息系統(tǒng)。在系統(tǒng)的 workload 發(fā)送變化時(shí),還可以按需擴(kuò)縮計(jì)算機(jī)資源。對于與計(jì)算供應(yīng)商來說,所有用戶使用的海量的資源統(tǒng)一管理,規(guī)模效應(yīng)顯著,邊際成本低。對于云計(jì)算用戶來說,資源獲取快捷、方便,按需使用比自建機(jī)房、搭建和運(yùn)維基礎(chǔ)系設(shè)施成本更低。站在全社會的角度來看,整體資源的利用率更加高,environmentally-friendly。
數(shù)據(jù)庫是最常用的基礎(chǔ)軟件之一,它通常能提供 計(jì)算 和 存儲 的能力。存儲當(dāng)然是數(shù)據(jù)的基礎(chǔ)能力。計(jì)算能力對外體現(xiàn)在:數(shù)據(jù)庫可以完成用戶發(fā)出的復(fù)雜分析與計(jì)算請求(用 SQL 可以寫出強(qiáng)大的計(jì)算邏輯),對內(nèi)體現(xiàn)在:查詢優(yōu)化、事務(wù)處理、索引維護(hù)等內(nèi)部計(jì)算。
單機(jī)數(shù)據(jù)庫部署在普通主機(jī)上,其存儲和計(jì)算能力受限于主機(jī)的硬件,難以擴(kuò)展。分布式數(shù)據(jù)庫可以通過適當(dāng)增加機(jī)器,來拓展數(shù)據(jù)庫的容量和計(jì)算能力,但是集群節(jié)點(diǎn)的能力依舊受到機(jī)器資源的限制。如果將它們簡單的搬遷到云上,不做任何改造,普通主機(jī)換成云主機(jī)。
第一,數(shù)據(jù)庫直接部署在云上,可能會有大量新的問題出現(xiàn),例如網(wǎng)絡(luò)瓶頸、可能會帶來嚴(yán)重的寫放大問題等;再比如:即使給單機(jī)數(shù)據(jù)庫掛一個(gè)無限大的存儲,當(dāng)數(shù)據(jù)量極大時(shí)性能會很差。第二,無法完全發(fā)揮云計(jì)算最大的優(yōu)勢「資源彈性管理、按需使用」,也無法靈活運(yùn)用琳瑯滿目的云存儲產(chǎn)品。第三,信息化、數(shù)字化的高速發(fā)展,對數(shù)據(jù)庫提出了更多要求:更高的性能、更低的成本、更好的靈活性,這些也是僅僅將單機(jī)數(shù)據(jù)庫搬上云解決不了的。
雖然分布式數(shù)據(jù)庫也做到了很好的擴(kuò)展性,但它不能算是「云原生」。第一,概念上,它彈性擴(kuò)展的單位是「機(jī)器」,而非更加細(xì)粒度的「計(jì)算和存儲資源」。第二,它在設(shè)計(jì)的時(shí)候,并不會考慮云平臺的特征,沒有相應(yīng)的優(yōu)化,以達(dá)到最優(yōu)性能和成本。第三,引入分布式事務(wù)、分布式集群管理等模塊,也讓系統(tǒng)變得更加復(fù)雜。(這里沒有拉踩的意味,分布式數(shù)據(jù)庫這條路也有巨大的優(yōu)勢,例如數(shù)據(jù)量極大、需要寫擴(kuò)展性、需要全球部署等場景)
「云原生數(shù)據(jù)庫」的核心,是要設(shè)計(jì) 一種更加符合「資源彈性管理」這一理念、充分利用云平臺池化資源、適配云平臺已有的基礎(chǔ)設(shè)置的數(shù)據(jù)庫架構(gòu)。
由于云計(jì)算平臺的存儲和計(jì)算資源是可以分開擴(kuò)展的,所以云原生數(shù)據(jù)庫必定是存儲計(jì)算分離架構(gòu)。
2. Aurora
2.1 關(guān)鍵問題
Aurora 是 AWS 推出的 OLTP 云數(shù)據(jù)庫先驅(qū),在 MySQL 代碼基礎(chǔ)上改造出存儲計(jì)算分離架構(gòu)。AWS 認(rèn)為在云上構(gòu)建數(shù)據(jù)庫,存儲資源很容易擴(kuò)展,那么系統(tǒng)的瓶頸就落在網(wǎng)絡(luò)上了,因?yàn)閿?shù)據(jù)庫實(shí)例與所有存儲節(jié)點(diǎn)的交互都需要通過網(wǎng)絡(luò)。因此 Aurora 最核心的理念就是要減少數(shù)據(jù)的網(wǎng)絡(luò)傳輸量。
Aurora 論文里舉了這樣一個(gè) MySQL 直接搬遷到云上的例子:
單機(jī) MySQL 事務(wù)提交需要日志落盤,同時(shí)后臺線程會異步刷臟頁,為了避免頁斷裂,刷臟頁時(shí)還需要將數(shù)據(jù)頁寫入 double write 區(qū)域。如下圖所示,如果再考慮到生產(chǎn)環(huán)境中需要主備復(fù)制,AZ1 和 AZ2 各部署一個(gè)MySQL 實(shí)例同步鏡像復(fù)制(這里應(yīng)該是 DRBD 的方案?),底層存儲采用 Elastic Block Store(EBS),每個(gè)EBS還有自己的一份鏡像,另外部署Simple Storage Service(S3) 歸檔 redo 日志和 binlog 日志,以支持基于時(shí)間點(diǎn)的恢復(fù)。
以上寫入同步,每個(gè)步驟都需要傳遞5種類型的數(shù)據(jù):redo log,binlog,data page,double write 和 frm 元數(shù)據(jù)。由于是基于鏡像的同步復(fù)制,因此圖中的1,3,5步驟是順序的,這種模型響應(yīng)時(shí)間非常糟糕,要進(jìn)行4次網(wǎng)絡(luò)IO,且其中3次是同步串行的。從存儲角度看,數(shù)據(jù)在 EBS 上存 4 份,需要 4 份都寫成功才能返回。 所以在這種架構(gòu)下,無論是 IO 量還是串行化模型都會導(dǎo)致性能非常糟糕。

Aurora 為了減少 IO 的數(shù)量,所有節(jié)點(diǎn)之間的數(shù)據(jù)傳輸,只有 redo。為了做到這一點(diǎn),需要將部分?jǐn)?shù)據(jù)庫的能力下推到存儲節(jié)點(diǎn)。當(dāng)然除了減少 IO,Aurora 的設(shè)計(jì)還帶來了不少其他好處。
2.2 核心技術(shù)
2.2.1 存儲計(jì)算分離架構(gòu)
Aurora 下推到存儲的主要功能主要和 redo 相關(guān),包括:日志回放、故障恢復(fù)和備份還原。技術(shù)層保留了查詢處理、事務(wù)處理、緩存管理、鎖管理、訪問控制等大部分功能。

Aurora 的整體由跨 AZ 的一個(gè)主實(shí)例、多個(gè)副本(最多15個(gè))實(shí)例及多個(gè)存儲節(jié)點(diǎn)組成。主實(shí)例與只讀實(shí)例/存儲節(jié)點(diǎn)之間只傳遞 redo log 和元信息。主實(shí)例與副本實(shí)例共享一套分布式存儲,因此增加副本實(shí)例是零存儲成本的,這大大提高了 Aurora 的讀擴(kuò)展性。
主實(shí)例會異步向副本實(shí)例發(fā)送 redo log,副本實(shí)例收到后開始回放。如果日志對應(yīng)的 page 不在 本地的 page cache,該 redo log 可以直接丟棄,因?yàn)榇鎯?jié)點(diǎn)擁有所有的 page 及 redo log,有需要時(shí)直接從存儲節(jié)點(diǎn)請求即可。
存儲節(jié)點(diǎn)收到 redo log 后會持久化,而回放日志和回收舊版本數(shù)據(jù)頁的工作,可以一直異步在后臺執(zhí)行。存儲節(jié)點(diǎn)可以較為靈活的分配資源做前臺/后臺的工作。同時(shí)相較于傳統(tǒng)數(shù)據(jù)庫,Aurora 不用后臺去推進(jìn) checkpoint(這個(gè)動作往往會影響前臺請求),分離的存儲層不斷地推進(jìn) checkpoint,對數(shù)據(jù)庫實(shí)例絲毫沒有影響,而且推進(jìn)的越快,對讀請求越有利。
存儲層不斷推進(jìn) checkpoint 也能加快故障恢復(fù)的速度,一般情況下,在 10w TPS 壓力下,Aurora 可以在 10s 恢復(fù)完成。在故障恢復(fù)過程中,
詳細(xì)來說,Aurora 寫流程如下:

(1) 存儲節(jié)點(diǎn)接收主實(shí)例的日志,追加到其內(nèi)存隊(duì)列。
(2) 存儲節(jié)點(diǎn)持久化日志后應(yīng)答主實(shí)例。
(3) 按分片歸類日志,確認(rèn)是否有日志丟失。
(4) 與其它存儲節(jié)點(diǎn)交互,填充丟失日志。
(5) 回放日志,生成數(shù)據(jù)頁。
(6) 定期備份數(shù)據(jù)和日志到 S3。
(7) 定期回收過期數(shù)據(jù)頁版本。
(8) 定期 CRC 校驗(yàn)數(shù)據(jù)頁。
上述所有操作,只有第 (1) (3)是串行同步的,會直接影響請求響應(yīng)時(shí)間,其它均為異步操作。
為了保證可用性,Aurora 持久化采用 Quorum 協(xié)議。假設(shè)復(fù)制集合包含 V 個(gè)節(jié)點(diǎn),讀請求需要得到 Vr 個(gè)節(jié)點(diǎn)響應(yīng),寫請求需要得到 Vw 個(gè)節(jié)點(diǎn)詳細(xì),為了保證讀寫一致性,Quorum 協(xié)議主要有兩個(gè)條件(1)Vr + Vw > V ;(2)Vw > V/2。
主實(shí)例的每次寫入會發(fā)送到位于 3 個(gè) AZ 的 6 個(gè)存儲節(jié)點(diǎn),收到 4 個(gè)持久化成功的回復(fù)便認(rèn)為寫入成功。因此 Aurora 的 Quorum 協(xié)議中,V = 6,Vw = 4,Vr = 3。當(dāng)然在實(shí)際讀請求中,不用真的去 3 個(gè)存儲節(jié)點(diǎn)查詢,只需要查詢擁有最新數(shù)據(jù)的那個(gè)存儲節(jié)點(diǎn)就行 。
Quorum 協(xié)議可以保證,只要 AZ 級別故障和節(jié)點(diǎn)故障不同時(shí)發(fā)生,數(shù)據(jù)庫的可用性就能得到保證。同時(shí) Aurora 采用分片管理的策略,每個(gè)分片 10G,6 個(gè) 10G 的副本組成一個(gè) Protected Group,每個(gè)分片是一個(gè)故障恢復(fù)的單位,在 10G/s 網(wǎng)絡(luò)下,一個(gè)分片可以在 10s 內(nèi)恢復(fù)。因此只有當(dāng) 10s 內(nèi)同時(shí)發(fā)生超過2個(gè)分片的故障,可用性才會受到影響,這幾乎是不可能發(fā)生的。
sysbench write only 測試,Aurora是基于鏡像MySQL吞吐能力的35倍,每個(gè)事務(wù)的日志量比基于鏡像MySQL日志量要少7.7倍。
總體來講,Aurora 存儲計(jì)算分離的架構(gòu)帶來了如下好處:(1)在云上部署,所有節(jié)點(diǎn)之間只傳輸 redo,網(wǎng)絡(luò)壓力小。(2)前后臺線程互不干擾,后臺任務(wù)可以不停歇的異步執(zhí)行。(3)存儲節(jié)點(diǎn)跨 AZ 高可用。(4)讀副本、存儲節(jié)點(diǎn)可線性擴(kuò)展(有上限)。(5)故障恢復(fù)時(shí)間快。
2.2.2 一致性保證
從 MySQL 到 Aurora,單機(jī)系統(tǒng)進(jìn)化為分布式系統(tǒng),那么存儲節(jié)點(diǎn)與數(shù)據(jù)庫實(shí)例之間就需要保證數(shù)據(jù)一致性(consistency)。 Aurora 強(qiáng)調(diào),傳統(tǒng)保證分布式一致性的方法 2PC 協(xié)議復(fù)雜且容錯(cuò)性非常差,Aurora 依賴Quorum + Gossip 協(xié)議及基于 LSN 的算法來保證一致性。
我其實(shí)不太理解 Aurora 為啥要在論文中 cue 一下 2PC,雖然 2PC 能解決它的一致性問題,但是一般人肯定不會在這種場景下使用 2PC。一來,事務(wù)提交管理這種會改變數(shù)據(jù)庫狀態(tài)的操作主實(shí)例控制,也就意味著整個(gè)系統(tǒng)的一致性完全由主實(shí)例單機(jī)決定,存儲節(jié)點(diǎn)只需要告知主實(shí)例其持久化結(jié)果,這一點(diǎn)和和單機(jī)數(shù)據(jù)庫沒有區(qū)別,看不出來哪里需要分布式的兩階段提交協(xié)議。二來,相較于分布式事務(wù)提交,Aurora 存儲節(jié)點(diǎn)只有 redo 持久化這一個(gè)簡單需求,沒有其他資源的要求(例如:鎖、事務(wù)上下文創(chuàng)建等),也沒有提交結(jié)束釋放資源的需求,完全沒必要上 2PC。
說白了,要不要用 2PC 就是看數(shù)據(jù)庫的狀態(tài)變化由誰控制,存儲節(jié)點(diǎn)雖然持久化了,但是數(shù)據(jù)庫狀態(tài)的變化,完全由事務(wù)管理器來控制。需要兩階段提交的情況是,事務(wù)處理器分布在不同節(jié)點(diǎn)(這樣大大提高可了寫擴(kuò)展性),大家就事務(wù)應(yīng)該提交還是回滾做出協(xié)商。
比較有意思的對比倒是在大部分系統(tǒng)都傾向于使用 Paxos/Raft 這樣的共識算法,在底層實(shí)現(xiàn)一個(gè)強(qiáng)一致的存儲系統(tǒng)來保證高可用時(shí)(例如 PolarDB, Spanner, OB...),Aurora 只是使用簡單的 Quorum ,計(jì)算層和存儲層結(jié)合起來實(shí)現(xiàn)高可用強(qiáng)一致。
以上是個(gè)人思考,其實(shí)在 Aurora 這種事務(wù)邏輯和單機(jī)數(shù)據(jù)庫幾乎沒有區(qū)別的數(shù)據(jù)庫,正常情況下,一致性保證沒什么區(qū)別。主要的區(qū)別在于,數(shù)據(jù)庫實(shí)例宕機(jī)重啟時(shí),需要這些宕機(jī)前未執(zhí)行完的事務(wù),決定哪些數(shù)據(jù)頁中未完成的事務(wù),應(yīng)該提交還是回滾。因?yàn)閱螜C(jī)數(shù)據(jù)庫恢復(fù)依據(jù)的是單份日志,而 Aurora 需要從多個(gè)存儲節(jié)點(diǎn)獲得的多份日志,Aurora 需要決定哪些日志需要回放,哪些需要截?cái)唷?/p>
Aurora 事務(wù)都是由主實(shí)例發(fā)起的,因此主實(shí)例可以按時(shí)間順序?yàn)槊恳粋€(gè) redo 日志分配一個(gè) Log Sequence Number(LSN)。為了保證一致性,Aurora 定義了如下幾個(gè)關(guān)鍵日志點(diǎn)位。
Volume Complete LSN(VCL),表示存儲層擁有 VCL 前的所有完整日志。故障恢復(fù)時(shí),需要銷毀所有 LSN 大于 VCL 的日志。
Consistency Point LSNs(CPLs),MySQL(InnoDB) 的事務(wù)由內(nèi)部多個(gè) Mini-Transaction 組成,每個(gè) Mini-Transaction 是原子操作的最小單位。例如 B+-Tree 分裂需要修改多個(gè) page,它們必須是原子的,必須由一組原子的 redo 日志來表示。CPL 是 Mini-Transaction 的最后一個(gè)日志的 LSN,日志回放時(shí),需要以 CPL 為單位。一個(gè)事務(wù)通常由多個(gè) CPL 組成。
Volume Durable LSN(VDL),所有 CPLs 中已經(jīng)持久化的最大 LSN,它代表了數(shù)據(jù)庫處于一致狀態(tài)的最新位點(diǎn),VDL 一定小于等于 VCL。為了保證 Mini-Transaction 的原子性,所有大于 VDL 的日志也需要被銷毀。在故障恢復(fù)階段,數(shù)據(jù)庫實(shí)例通過 Quorum 讀可以計(jì)算得到最新的 VDL。
舉個(gè)例子,VCL=1007,CPLs={900,1000,1100},那么 1000 以后的日志都應(yīng)該被截?cái)唷?/p>
3.PolarDB
PolarDB 是阿里云推出的云原生數(shù)據(jù)庫,雖然都是計(jì)算存儲分離架構(gòu),但是與 Aurora 在設(shè)計(jì)理念上有非常大的區(qū)別。
3.1 關(guān)鍵問題
PolarDB 在許多公開的分享和文章中,強(qiáng)調(diào)了傳統(tǒng)數(shù)據(jù)庫架構(gòu)上云,存在很多問題,這里列舉一些典型的:
擴(kuò)展性相關(guān):
- 由于物理機(jī)磁盤限制以及備份等策略,數(shù)據(jù)庫的數(shù)據(jù)大小不能太大,太大的實(shí)例是運(yùn)維的災(zāi)難。
- 活動上線時(shí)造成壓力突增,而數(shù)據(jù)庫卻來不及升級。
- 業(yè)務(wù)發(fā)展很快,來不及進(jìn)行拆庫,也來不及分庫分表。
性能相關(guān):
- 傳統(tǒng)備份技術(shù),由于涉及到拷貝數(shù)據(jù),并上傳廉價(jià)存儲,速度因此也受網(wǎng)絡(luò)影響。一次全量數(shù)據(jù)備份需要大量時(shí)間,并且必須鎖表等。
- 讀寫實(shí)例和只讀實(shí)例各自擁有一份獨(dú)立的數(shù)據(jù),新建一個(gè)只讀實(shí)例需要重新拷貝數(shù)據(jù),考慮到網(wǎng)絡(luò)限流,速度不會很快。
- MySQL數(shù)據(jù)庫早期的版本,對早期的系統(tǒng)/硬件做了很多優(yōu)化,但是并沒有考慮到現(xiàn)代主流的系統(tǒng)/硬件的優(yōu)秀特性,在高并發(fā)環(huán)境下,性能還有很大的提升空間。
- MySQL為了兼容性,需要寫兩份日志(事務(wù)日志和復(fù)制日志),與其他商業(yè)數(shù)據(jù)庫相比,性能相對較差。
- 讀寫實(shí)例和只讀實(shí)例通過增量邏輯數(shù)據(jù)同步,讀寫實(shí)例上所有的SQL需要在只讀實(shí)例上重新執(zhí)行一遍(包括SQL解析,SQL優(yōu)化等無效步驟),同時(shí),復(fù)制并發(fā)讀最高是基于表維度,導(dǎo)致主備延遲非常普遍,進(jìn)而影響各種切換任務(wù)。
- 應(yīng)用擴(kuò)容之后,上百臺ECS連接一臺數(shù)據(jù)庫,因此在高并發(fā)下性能很差。
成本相關(guān):
- 讀寫實(shí)例和只讀實(shí)例各自擁有一份獨(dú)立的數(shù)據(jù),用戶購買只讀實(shí)例,不僅需要付出計(jì)算的成本,也需要付出存儲資源的成本。
....
這些問題本質(zhì)上都是單機(jī)數(shù)據(jù)庫所面臨的的問題,數(shù)據(jù)庫上云,當(dāng)然就是要解決這些問題。但是僅僅簡單的將單機(jī)數(shù)據(jù)庫部署在云上,這些問題還是存在的。PolarDB 的研發(fā)就是為了解決這一系列問題。
當(dāng)然,我認(rèn)為 Aurora 也是能夠解決上述大部分問題的,只是 PolarDB 選擇的技術(shù)路線與 Aurora 截然不同。
3.2 核心技術(shù)
3.2.1 存儲計(jì)算分離架構(gòu)
PolarDB 同樣采用存儲計(jì)算分離架構(gòu),計(jì)算節(jié)點(diǎn)是一個(gè) MySQL 實(shí)例負(fù)責(zé)存儲 SQL 解析、事務(wù)處理等,存儲節(jié)點(diǎn)是 PolarDB 的核心組件 PolarStore,負(fù)責(zé)數(shù)據(jù)可靠存儲。同一個(gè)集群的多個(gè)計(jì)算節(jié)點(diǎn)(1個(gè)讀寫實(shí)例和多個(gè)只讀實(shí)例)共享一份數(shù)據(jù),正因?yàn)槿绱?,擴(kuò)展只讀實(shí)例的速度和成本都非常低。數(shù)據(jù)庫實(shí)例的高可用通過只讀實(shí)例在 failover 時(shí)迅速轉(zhuǎn)變?yōu)樽x寫實(shí)例保證,數(shù)據(jù)的高可用和一致性由 PolarStore 內(nèi)部實(shí)現(xiàn)的 Parallel-Raft(支持亂序提交,性能比 raft 更好) 保證。
計(jì)算節(jié)點(diǎn)與存儲節(jié)點(diǎn)采用高速網(wǎng)絡(luò)互聯(lián),并通過RDMA協(xié)議進(jìn)行數(shù)據(jù)傳輸,使網(wǎng)絡(luò)不再成為瓶頸。

PolarDB 的設(shè)計(jì)重點(diǎn)是一套高性能的分布式文件系統(tǒng) PolarFS,能夠向多個(gè)數(shù)據(jù)庫實(shí)例提供高性能、高可靠的數(shù)據(jù)存取服務(wù)(支持同時(shí)掛載)。
3.2.2 PolarFS
PolarFS 是一個(gè)極低延遲,高可靠,利用了大量新硬件的分布式文件系統(tǒng),對外的接口是 libpfs,數(shù)據(jù)庫通過這一層與 PolarFS 交互。
PolarFS 存儲層次上可以分為三級:Volume,Chunk,Block。用戶創(chuàng)建 PolarDB 數(shù)據(jù)庫實(shí)例時(shí),系統(tǒng)就會為該實(shí)例創(chuàng)建一個(gè) Volume,其大小范圍可以是 10G~100T。每個(gè) Volume 由多個(gè) Chunk 組成,Chunk 是數(shù)據(jù)移動/高可用/分布式的最小單位,其必落在某塊 SSD 盤上。每個(gè) Chunk 大小為 10GB,遠(yuǎn)大于其他分布式文件系統(tǒng)(GFS為64MB)。這樣做可以減少 Volume 到 Chunk 映射的元數(shù)據(jù)量大小,更有利于管理與緩存。不足之處在于熱點(diǎn)不易打散。每個(gè) Chunk 都有3 個(gè)副本,通過 ParallelRaft 協(xié)議保證高可用。Chunk 被進(jìn)一步劃分為 163,840 個(gè) Block,每個(gè)塊大小為 64KB。是預(yù)分配空間的最小單位。
PolarFS 的主要組件包括:
libpfs: 用戶空間文件系統(tǒng)(User Space File System)庫,是計(jì)算節(jié)點(diǎn)訪問底層存儲的API接口。
PolarSwitch :部署在計(jì)算節(jié)點(diǎn)的Daemon,負(fù)責(zé)將 IO 請求轉(zhuǎn)化為對具體 ChunkServer leader 的訪問。
ChunkServer :用于管理 Chunk,處理 Block IO 請求。一個(gè)存儲節(jié)點(diǎn)可以部署多個(gè) ChunkServer,每個(gè)ChunkServer 綁定一個(gè)CPU核,管理一塊獨(dú)立的 NVMe SSD 盤,ChunkServer 之間沒有資源競爭。Chunk 修改會先 WAL,保證原子性和持久性。ChunkServer使用了 3D XPoint SSD 和普通 NVMe SSD 混合型WAL buffer,Log 會優(yōu)先存放到更快的 3DXPoint SSD 中。
PolarCtrl :系統(tǒng)的控制平面,是PolarFS集群的控制核心,負(fù)責(zé)監(jiān)控和 Balance ChunkServer、各種元信息維護(hù)、更新 PolarSwitch 元信息緩存等。

PolarFS的寫操作流程大致如下:
- POLARDB 通過 libpfs 發(fā)送寫請求經(jīng)由 ring buffer 到 PolarSwitch。
- PolarSwitch 根據(jù)本地緩存元數(shù)據(jù),將寫請求發(fā)送至對應(yīng) Chunk 的 Chunk Server Leader 節(jié)點(diǎn)。
- 請求到達(dá) ChunkServer 后,節(jié)點(diǎn)的 RDMA NIC 將 Request 加入 request 隊(duì)列。一個(gè) IO 輪詢線程不斷輪詢該請求隊(duì)列,一旦發(fā)現(xiàn)有新請求則立即開始處理。
- IO 處理線程異步將請求寫到 Chunk 對應(yīng)的 WAL 塊上(通過 SPDK),同時(shí)將請求異步發(fā)向給 Follower 節(jié)點(diǎn)。
- 寫請求到達(dá) Follower 后,同樣通過 RDMA NIC 將其放到 request 隊(duì)列。
- Follower 節(jié)點(diǎn)上的 IO 輪詢線程被觸發(fā),同樣異步寫入。
- Follower 節(jié)點(diǎn)的寫請求成功,通過 RDMA 向 Leader 發(fā)送應(yīng)答。
- Leader節(jié)點(diǎn)收到 Followers 里任一節(jié)點(diǎn)成功應(yīng)答后,即形成 majority,同樣通過 SPDK 將寫請求寫到相應(yīng)的數(shù)據(jù)塊。
- Leader 通過 RDMA NIC 向 PolarSwitch 返回請求處理結(jié)果。
- PolarSwitch 標(biāo)記請求成功并通知 POLARDB 數(shù)據(jù)庫實(shí)例。
PolarFS 讀寫使用 SPDK 直接通過 DMA 操作 SSD,而非 OS 內(nèi)核 IO 協(xié)議棧,通過輪詢的方式監(jiān)聽硬件設(shè)備 IO完成事件。IO 線程和 CPU 核綁定,也不共享任何數(shù)據(jù)結(jié)構(gòu),相互之間沒有競爭。這種 bypass OS IO 協(xié)議棧的方式,大大提升了高速設(shè)備的 IO 處理性能。同時(shí)通過 RDMA 網(wǎng)絡(luò)大大提升網(wǎng)絡(luò) IO 性能?;窘鉀Q了 HDFS 和Ceph 等分布式文件系統(tǒng)存在的性能差、延遲大的問題。
3.2.3 物理復(fù)制
PolarDB 讀寫實(shí)例和只讀實(shí)例共享一份數(shù)據(jù),需要保證一致性,例如讀寫實(shí)例新寫的 Page,即使沒有刷臟,只讀實(shí)例也應(yīng)該可以看到。因此實(shí)例之間應(yīng)當(dāng)有一定的同步機(jī)制(為了保證主備之間的一致性,)。
MySQL 有兩種主要日志: Binlog 和 InnoDB 的 Redo log。Binlog 是 tuple 級的數(shù)據(jù)變更日志,方便 MySQL 易構(gòu)存儲引擎之間同步數(shù)據(jù),也方便下游消費(fèi)。Redo log,記錄的是文件物理頁面的修改,是 InnoDB 為了支持事務(wù)的 ACID 特性。
Binlog 是 tuple 級別的,只讀實(shí)例回放的效率太低,回放過程中只讀實(shí)例可能還得去請求一些自己根本不需要的 Page。Redo log 顯然是更合適做主備之間的數(shù)據(jù)同步。并且,使用 Binlog 復(fù)制目前只能按表級別并行復(fù)制,而物理復(fù)制可以按照數(shù)據(jù)頁級別并發(fā),粒度更細(xì),并行效率更加高,主備之間的延遲可以保持在毫秒級別。同時(shí)在 Binlog 非必須的情況下,關(guān)閉 Binlog 也會對性能帶來提升。
所有節(jié)點(diǎn)共享一份全量數(shù)據(jù)和 Redo log,增加只讀節(jié)點(diǎn)非常容易,主備之間的同步只需要元數(shù)據(jù)即可。這使得系統(tǒng)在主節(jié)點(diǎn)發(fā)生故障進(jìn)行 Failover 時(shí)候,切換到只讀節(jié)點(diǎn)的故障恢復(fù)時(shí)間能縮短到 30 秒以內(nèi)。
通過物理復(fù)制保證主備節(jié)點(diǎn)之間的一致性,其實(shí)有非常復(fù)雜的算法和策略去解決各種各樣可能出現(xiàn)不一致的 case,這里就不詳述了,可以閱讀這篇 http:// mysql.taobao.org/monthl y/2017/09/01/
4.Socrates
4.1 關(guān)鍵問題
Socrates 是 Azure 在 SQL Server 基礎(chǔ)上研發(fā)的云原生數(shù)據(jù)庫,依舊采用計(jì)算存儲分離的架構(gòu),和 Aurora 相比,分離的更加徹底,在存儲層又將 Page 和日志存儲分開,目的是將持久化和高可用分離。
Azure 認(rèn)為云上的基于 log 主備同步的 SQL DB(RDS)存在以下幾個(gè)主要問題:

- 數(shù)據(jù)容量受到單機(jī)存儲能力的限制。
- 超大事務(wù)可能打爆本地磁盤。
- Backup/Restore、新拉節(jié)點(diǎn)等操作的成本,和數(shù)據(jù)的體量成正比。
- 每個(gè)節(jié)點(diǎn)保存全量的數(shù)據(jù),導(dǎo)致彈性能力較差。
微軟為了解決這些問題,設(shè)計(jì)了一種新型的云原生數(shù)據(jù)庫,在計(jì)算存儲分離架構(gòu)的基礎(chǔ)上,進(jìn)一步分離存儲層,將 Log 從存儲層分離出來,單獨(dú)設(shè)計(jì)高性能 log 服務(wù)。從更高的層面來講就是實(shí)現(xiàn)持久性(Durability,由日志實(shí)現(xiàn))和高可用性(Availability,由存儲層實(shí)現(xiàn))分離。絕大部分?jǐn)?shù)據(jù)庫這兩個(gè)能力均是由存儲層提供的。
Socrates 分離這兩個(gè)概念的思路在于: (1)持久化不完全需要昂貴的存儲設(shè)備 (詳見5.2.2) 。(2)高可用不完全需要跟多副本 (例如常規(guī)的3副本,詳見 5.2.3)。
如果把這倆概念的需求分開,那么 Socrates 就需要(1)更少昂貴的告訴存儲資源和計(jì)算資源,(2)更少的數(shù)據(jù)拷貝。那么他具體是怎么做的呢?
4.2 核心技術(shù)
Socrates 將數(shù)據(jù)庫的各個(gè)組件拆成獨(dú)立服務(wù),提供不同的能力。并盡量使用異步的方式通信,減少響應(yīng)時(shí)間,加快處理速度。Socrates 大體由 4 個(gè)組件組成

4.2.1 計(jì)算節(jié)點(diǎn)
和上述兩種云原生數(shù)據(jù)庫一樣,Socrates 計(jì)算層包括一個(gè)讀寫實(shí)例和多個(gè)只讀實(shí)例。讀寫實(shí)例如出現(xiàn)故障,則某個(gè)只讀會被選為新的讀寫實(shí)例。
如上面架構(gòu)圖所示,數(shù)據(jù)庫實(shí)例如果需要讀取本都未緩存的 Page,需要通過 GetPage@LSN(實(shí)際上是 GetPage(PageID, LSN)) 的 RPC 從響應(yīng)的 Page Server 獲取。PageID 是 Page 的唯一標(biāo)識,LSN 則告訴 Page Server 需要返回 應(yīng)用此 LSN 或者更新的 Page。
一個(gè)簡單的例子:
- 讀寫實(shí)例在本地緩存更新 Page X 。
- 某種原因,本地 Buffer Pool 中 Page X 被淘汰(需要保證其修改已經(jīng) flush 到 XLOG)。
- 讀寫實(shí)例再次讀 Page X。
為了保證可以讀到最新版本的 Page X,需要指定的 LSN。讀寫實(shí)例向 Page Server 發(fā)送 GetPage(X, X_LSN) 請求,Page Server 會等待所有小于 X_LSN 的日志都 Apply 完成了再返回 Page X。讀寫節(jié)點(diǎn)維護(hù)了一個(gè) PageID -> Page LSN 的映射。
讀寫實(shí)例和只讀實(shí)例之間沒有直接交互,只讀節(jié)點(diǎn)收到的日志,全部是 XLOG 廣播過來的。只讀節(jié)點(diǎn)收到后需要回放,回放過程中如果遇到相應(yīng) Page 不在 Buffer Pool,會直接丟棄。但是它也提供了另一種策略:從 Page Server 獲取 Page 并回放,這就保證了主備之間緩存大體一致,當(dāng)發(fā)生 failover 時(shí),會更加穩(wěn)定。
4.2.2 XLOG

XLOG 是 Socrates 日志服務(wù)層,上圖展示了 XLOG 服務(wù)的內(nèi)部實(shí)現(xiàn)。
首先,讀寫節(jié)點(diǎn)直接將日志同步寫到 Landing Zone(LZ) ,一個(gè)快速持久化存儲服務(wù),以降低寫入延遲,它由 Azure 高級存儲服務(wù) (XIO) 實(shí)現(xiàn),為了保證可靠性,數(shù)據(jù)也是三副本存儲。
同時(shí),讀寫節(jié)點(diǎn)異步寫日志到 XLOG Process,該進(jìn)程將日志傳送到 Page Server 和只讀節(jié)點(diǎn)。LZ 存在的目標(biāo)僅是為了持久性,而非可用性,可用性由 Page Server 和計(jì)算節(jié)點(diǎn)保證。
讀寫節(jié)點(diǎn)并行寫日志到 LZ 和 XLOG Process。因此,日志可能在 LZ 持久化之前到達(dá)只讀節(jié)點(diǎn),可能會在故障發(fā)生時(shí),出現(xiàn)數(shù)據(jù)不一致的情況。為此 XLOG Process 只廣播已在 LZ 中持久化的日志。讀寫節(jié)點(diǎn)首先將日志寫入到 XLOG Process 的 Pending Blocks,一旦日志持久化成功,XLOG Process 將它從 Pending Blocks 移動到LogBroker 用于廣播分發(fā)。
后臺還有一個(gè) Destaging 線程,將已經(jīng)持久化的日志移動到本地 SSD 緩存,以實(shí)現(xiàn)快速訪問。同時(shí)移動到XStore 長期歸檔保存(Long-Term Archieve, LT)。XStore 使用廉價(jià)存儲,成本低、速度慢。LZ 和 LT 保存了所有的日志,共同實(shí)現(xiàn)持久化的目標(biāo)。Socrates 默認(rèn)將日志記錄保留30天,用于指定時(shí)間點(diǎn)恢復(fù)和災(zāi)難恢復(fù)。顯然,在 LZ 中保留 30 天的日志,成本非常高,XStore 可以完美接替這個(gè)任務(wù)。
LZ 快速但昂貴,有利于事務(wù)快速提交,XStore 廉價(jià)但慢,有利于節(jié)省成本。
4.2.3 Page Server
Page Server 主要負(fù)責(zé)三件事
- 通過回放日志,管理數(shù)據(jù)庫分區(qū)
- 響應(yīng)計(jì)算節(jié)點(diǎn)的 GetPage 請求
- 打 Checkpoint 和向 XStore 備份
每個(gè) Page Server 只需要關(guān)心和自己存儲的 Page 相關(guān)的日志,日志中會增加充分的注釋信息,標(biāo)明日志記錄需要應(yīng)用到哪些分區(qū)中。XLOG Process 利用這些信息,可以為日志找到目標(biāo) Page。
Page Serve 和計(jì)算節(jié)點(diǎn)一樣,也使用RBPEX(Resilient Buffer Pool Extention)。但計(jì)算節(jié)點(diǎn)緩存是傳統(tǒng)數(shù)據(jù)庫的緩存方式:緩存最熱 Page 達(dá)到最好的性能。而 Page Server 緩存本分區(qū)的所有 Page。這對 GetPage 請求的性能非常友好,并且可以容忍 XStore 一定時(shí)間宕機(jī)。
新拉起一個(gè) Page Server 也非常方便,RBPEX 緩存異步建立,同時(shí) Page Server 可以接受請求和應(yīng)用日志了。因?yàn)槊總€(gè) Page Server 管理的數(shù)據(jù)并不大,恢復(fù)起來還非??斓摹8呖捎猛ㄟ^數(shù)據(jù)單副本和廉價(jià)存儲一份數(shù)據(jù)來共同實(shí)現(xiàn)。
4.2.4 XStore
數(shù)據(jù)庫所有的數(shù)據(jù)保存在 XStore 中,Page Server 相當(dāng)于是其一份緩存。XStore 是一個(gè)廉價(jià)、跨 AZ 高度復(fù)制的存儲系統(tǒng),幾乎不會丟數(shù)據(jù)。XStore 相當(dāng)于傳統(tǒng)數(shù)據(jù)庫中硬盤,計(jì)算節(jié)點(diǎn)和 Page Server 的內(nèi)存和 SSD 緩存(RBPEX),相當(dāng)于傳統(tǒng)數(shù)據(jù)庫的內(nèi)存。
總體來講,XLOG + XStore 實(shí)現(xiàn)了持久性,計(jì)算節(jié)點(diǎn) + Page Server 實(shí)現(xiàn)高可用性。
5.對比
對比下來,Aurora 是云原生數(shù)據(jù)庫的先驅(qū),首次將數(shù)據(jù)庫的存儲和計(jì)算能力分割,并且將部分?jǐn)?shù)據(jù)庫的能力下沉到存儲節(jié)點(diǎn)(主要是 Redo),大大減少數(shù)據(jù)的傳輸量,以提升性能和效率。Socrates 則在其之上更近一步,將數(shù)據(jù)庫的組件拆的更加細(xì),行成多個(gè)服務(wù)層。這種架構(gòu)更加靈活,可用性和成本的控制粒度更細(xì),可以幫助系統(tǒng)在保證性能和可用性的情況下,大幅控制成本。(并不是說 Socrates 比 Aurora 先進(jìn),Aurora 近幾年發(fā)展非???,在 Multi-Master、Serveless 等領(lǐng)域都有一定的突破,在云數(shù)據(jù)庫領(lǐng)域依舊處于領(lǐng)先地位)
雖然 PolarDB 也是存儲計(jì)算分離架構(gòu),多個(gè)節(jié)點(diǎn)共享一份數(shù)據(jù),但它的切面與 Aurora/Socrates 不同,存儲層是借助各種新硬件能力,保證高可靠、高性能的分布式文件系統(tǒng),然后多個(gè)節(jié)點(diǎn)掛載在上面。PolarDB 認(rèn)為高速網(wǎng)絡(luò)和各類新硬件的發(fā)展讓網(wǎng)絡(luò)不再是大瓶頸,而瓶頸是在軟件自身,因此做了大量的工作適配新新硬件,使用 OS-bypass 和 zero-copy 的技術(shù)提高 CPU 使用效率。PolarDB 這樣的架構(gòu)其實(shí)更容易跟進(jìn)開源社區(qū)的新版本,因?yàn)橛?jì)算層的功能性改動并不是特別大,主要是適配新的存儲和物理復(fù)制等。
至于這兩種方式誰更好,那就仁者見仁了。






























