Aurora論文理解

聲明

本文是基于Aws Aurora論文的翻譯、理解以及思考,論文的原地址是:
https://dl.acm.org/doi/pdf/10.1145/3035918.3056101

整體理解

簡而言之Aurora實際上只是將 MySQL 的 redo log 設計成一個獨立的存儲服務, 從而提高了MySQL的吞吐,關于redo log的基礎知識請自行學習,這里只做簡答說明。

redo log 就是 MySQL 的 WAL log, 是為了確保事務的持久性的. 每次事務提交前都需要確保 redo log 被持久化. 當數據庫 crash 時, 會使用 redo log 進行 point-in-time recovery。
從數據庫架構上來說, Aurora僅僅是將下圖中的 Log Manager 中的一部分功能剝離出 MySQL, 做成一個高可用的服務, 其他部分不變.


MySQL整體

概述

Amazon Aurora是AWS提供的OLTP類型的數據庫服務。本文主要說明Aurora的整體架構以及進行此架構設計的背后原因。進行aurora設計的主導思想是:AWS認為大數據量處理的瓶頸已經由原來的計算和存儲資源轉移到了網絡資源。為了解決存在的網絡瓶頸, Aurora將redo log的處理獨立處理 ,單獨設計成一個多租戶且可彈性橫向擴展的存儲服務。這種設計不僅可以減少網絡流量,還可以實現:快速故障恢復,在不丟失數據的情況下實現master故障轉移到slave,容錯,以及具備自我修復功能的存儲服務。Aurora實現了一種高效的異步方案可以在眾多存儲節點(storage node)上實現基于持久狀態的共識,而不需要繁瑣的恢復協議。

1 引子

現在越來越多的instructure都要上云。上云的原因包括:按需靈活配置容量以及按需付費等。當然這些上云的instructure中也包括數據庫服務。 在現代分布式云服務中,已經有很多先行者實現了部分的存儲計算分離,從而可以實現彈性伸縮,進而可以更加便捷的進行數據庫從庫添加、master故障切換、在線彈性伸縮數據庫實例等。
在云環境下的存儲計算數據庫面臨的I / O瓶頸有所改變。由于I / O可以分布在多租戶機群中的許多節點和許多磁盤上,因此數據庫實例上的磁盤已經不再是瓶頸。相反,瓶頸轉移到了多租戶請求I / O的數據庫層和執行這些I / O的存儲層之間的網絡。除了每秒數據包(PPS)和帶寬的基本瓶頸之外,由于存儲計算分離數據庫將并行向存儲隊列發出寫操作,因此會進一步增加寫入流量。底層的存儲節點(storage node)的磁盤或網絡性能會影響數據庫的響應時間。
盡管數據庫中的大多數操作可以并行執行,但是在某些情況下需要同步執行。這種同步執行的操作會導致停頓和上下文切換。一種典型的場景:由于數據庫的buffer pool中數據頁命中而導致而導致磁盤讀取。讀取線程在讀取完成之前無法繼續進行后續操作。由于buffer pool未命中還可能會產生刷臟頁(將buffer pool中的臟頁刷到磁盤上)等操作(當未命中而從磁盤讀取,但是此時buffer pool滿了,新讀取到的數據頁要想存入到buffer pool中就必須將buffer pool中現有的一些臟頁刷到磁盤中,從而騰出內存空間供從磁盤中新讀取的數據頁使用) 。雖然諸如check point和臟頁寫入之類的后臺處理可以減少這種損失的發生,但也可能導致停頓,上下文切換和資源爭用的發生。
事務提交是另一個可能會產生負面影響的動作。事務提交導致的停頓可能會阻止其他事務的執行。在云環境的分布式系統中,不太適合使用多階段提交協議(例如2階段提交提交(2PC)),因為這類協議對故障的容忍度比較低,而大規模分布式系統中硬件和軟件故障確是常態化的。除此之外,由于大規模的分布式系統經??缍鄠€數據中心,因此網絡的是高延遲也是一個很嚴重的問題。
Amazon Aurora通過在高度分布式的云環境中更加積極地利用redo log來解決上述問題。Aurora基于多租戶橫向擴展的存儲服務(Storage service,)實現了一種新穎的面向服務的體系架構,如下圖所示:



該存儲服務抽象出一種虛擬化的分段redo log并且與數據庫實例完全解耦。盡管每個數據庫實例仍包含傳統數據庫內核的大部分組件和功能(query processor, transactions, locking, buffer cache, access methods and undo management),但是將某些功能(redo logging, durable storage, crash recovery, and backup/restore)從數據庫實例中剝離出來,并下推到了存儲服務(storage service)中。

與傳統的數據庫相比,Aurora體系架構具有三個重要優勢。
首先,通過將構建跨多個數據中心的、獨立的、可以容錯和自我修復的存儲服務,可以讓數據庫服務免受由于網絡或存儲層的瞬間或永久性故障而帶來的性能損耗。
其次,通過僅將redo log寫入存儲服務,aurora能夠將網絡IOPS降低一個數量級。一旦消除了網絡瓶頸,我們就可以積極的繼續優化其他的瓶頸,這種對網絡瓶頸的優化相對于對MySQL代碼的優化而言,收益顯著。
第三,aurora將一些最復雜和關鍵的功能(備份和重做恢復)從數據庫引擎剝離出來,以前在數據引擎中每做一次這種操作都會直接對數據庫的讀寫性能產生負面影響?,F在剝離出來了,轉移到大型分布式集群中,并且分攤到集群中的每個節點上執行持續的異步操作。這樣就可以在沒有check point的情況下實現近乎即時的故障恢復并且也可以實現對數據庫讀寫無感的輕量備份。

2 大規模數據的持久性

數據庫的持久化要求:數據一旦寫入,就可以被讀取。在本節中,我們將討論quorum模型的原理,為什么我們要對storage進行分段以及如何將各個段結合在一起,從而不僅可以提供持久性,可用性和減少性能抖動,還可以幫助我們解決很多大型存儲集群的運維問題。

2.1 數據復制以及故障處理

數據庫實例的存活周期與存儲的數據的存活周期無必然聯系。數據庫實例故障時,客戶可以將其關閉。并且可以根據負載上下調整實例大小。因此這種特性是有利于實現存儲計算分離。
但是一旦實現了存儲計算分離,存儲節點(storage node)和磁盤也可能發生故障。因此,必須實現數據復制并提供故障恢復能力。在大規模云環境中,節點,磁盤和網絡故障常態化。每種故障可能具有不同的持續時間和影響范圍。例如,一個節點可能會出現暫時的網絡不可達,重新啟動時可能會暫時停機,或者磁盤,節點,機架,分支或主干網絡交換機甚至數據中心永久性故障。
Aurora存儲層的復制基于Quorum協議,假設復制拓撲中有V個節點,每個節點有一個投票權,讀 或 寫 必須拿到Vr 或 Vw個投票才能返回。為了滿足一致性,需要滿足兩個條件,首先Vr + Vw > V,這個保證了每次讀都能讀到擁有最新數據的節點;第二,Vw > V/2,每次寫都要保證能獲取到上次寫的最新數據,避免寫沖突。比如V=3,那么為了滿足上述兩個條件,Vr=2,Vw=2。為了保證各種異常情況下的系統高可用,Aurora的數據庫實例部署在3個不同AZ(AvailablityZone),每個AZ包含了2個副本,總共6個副本,每個AZ相當于一個機房,是一個獨立的容錯單元,包含獨立的電源系統,網絡,軟件部署等。結合Quorum模型以及前面提到的兩條規則, V=6,Vw=4,Vr=3,Aurora可以容忍任何一個AZ出現故障,而不會影響寫服務;任何一個AZ出現故障,以及另外一個AZ中的一個節點出現故障,不會影響讀服務且不會丟失數據。

2.2 分段存儲

通過Quorum協議,Aurora可以保證只要AZ級別的故障(火災,洪水,網絡故障)和節點故障(磁盤故障,掉電,機器損壞)不同時發生,就不會破壞協議本身,數據庫可用性和正確性就能得到保證。那么,如果想要數據庫“永久可用”,問題變成如何降低兩類故障同時發生的概率。由于特定故障發生的頻率(MTTF, Mean Time to Fail)是一定的,為了減少故障同時發生的概率,可以想辦法提高故障的修復時間(MTTR,Mean Time To Repair)。Aurora將存儲進行分片(segment)管理,每個分片10G,6個10G副本構成一個PGs(Protection Groups)。Aurora存儲由若干個PGs構成,這些PGs實際上是EC2(AmazonElastic Compute Cloud)服務器+本地SSD磁盤組成的存儲節點構成,目前Aurora最多支持64T的存儲空間。分片后,每個分片作為一個故障單位,在10Gbps網絡下,一個10G的分片可以在10s內恢復,因此當前僅當10s內同時出現大于2個的分片同時故障,才會影響數據庫服務的可用性,實際上這種情況基本不會出現。 通過分片管理,巧妙提高了數據庫服務的可用性。

2.3 彈性運維

基于分片管理,系統可以靈活應對故障和運維。比如,某個存儲節點的磁盤IO壓力比較大,可以人為的將這個節點標記為bad,quorum將會通過快速將這部分數據遷移到集群中的其他冷節點上來將這部分被標記為bad的數據進行修復。另外,也可以使用相同的方式進行集群中節點的軟件升級。所有這些故障和運維管理都是分片粒度滾動進行的,對于用戶完全透明。

3. 日志即數據庫

3.1 寫放大問題

Aurora的存儲容量分片模型、6副本、多數派quorum共同保證了aurora的高度彈性。不幸的是,若只是簡單地將此模型應用在傳統數據庫(如MySQL)上,會導致數據的性能極不穩定,因為每當應用執行一次數據庫寫入,數據實際上會產生多次不同的I/O操作。而這種高I??/O操作又通過復制進行了放大。而且,很多I / O都是同步操作,從而使數據處理流程停滯并增加延遲。
讓我們研究一下傳統數據庫中的寫入操作做了哪些事情。以mysql為例:首先將數據頁寫入到堆文件或者b樹中,并將redo log寫入到預寫日志(WAL)。每個redo log記錄都包含了被修改頁的前后差異??梢詫edo log應用于舊頁從而產生新頁。
實際上,除了redo log,還必須寫入其他數據。例如,考慮一個進行同步復制的mysql集群,并且要求該集群在數據中心之間實現高可用性,并以primary-standby的模式運行,如圖所示:


AZ1中有一個primary的MySQL實例中的數據存儲在Amazon Elastic Block Store(EBS)上。 AZ2中還有一個stand by的MySQL實例,數據也存儲在EBS上。通過mysql的同步復制實現primaryEBS卷的寫入與standby的EBS卷同步。
上圖中顯示了數據庫引擎需要寫入的各種不同類型的數據: redo log和binlog(將會被歸檔到S3存儲上,以支持基于時間點的數據恢復)修改后的數據頁、double-write的數據頁和元數據文件(FRM)。
該圖還顯示了實際IO流的順序。在第1步和第2步中,將向EBS發出寫操作,然后由EBS將其發送到同AZ中的本地鏡像EBS,并在完成這兩個操作完成后接收確認。接下來,在步驟3中,將寫操作同步到standby mysql實例。最后,在步驟4和5中,將數據寫入到standy 實例使用的EBS以及相應的同AZ中的本地鏡像EBS。
上圖中展示的MySQL架構模型由于寫放大而被人詬病。首先,步驟1、3和5是順序的且同步的。
這種模型延時很高,因為要進行4次網絡IO,且其中3次是同步串行的。從存儲角度來看,數據在EBS上存了4份,需要4份都寫成功才能返回。 所以這種模型會導致數據庫的性能極差。

3.2 將redo log的處理下推到存儲層

當傳統數據庫修改數據頁時,將生成一個redo log record,并調用日志應用程序,該應用程序將redo log record應用于內存中的數據頁,從而生成最新的數據。事務提交的時候必須即時寫入日志,但是數據頁的更新可能會延后(mysql中的change buffer)。
在Aurora中的數據庫實例唯一通過網絡執行的寫入數據就是redo log record,無論從數據庫層還是從后臺都不會寫入任何數據頁,因此也不會有check point和cache的換入換出。Aurora數據庫實例中的log applicator會直接將redo log record推送給存儲層(storage service),而storage service則會在后臺或者需要的時候基于redo log生成數據庫的data page。當然,基于redo log每次都從頭去應用redo log進而生成每個data page的代價是非常大的。因此,Aurora的存儲服務會不斷在后臺去應用redo log 從而生成數據庫data page,以避免每次都從頭開始重新生成它們,這個過程稱之為后臺物化(background materialization)。從正確性來看,后臺物化完全是可行的:就引擎而言,log就是數據庫,而存儲系統生成的data page都是log的緩存。只有含有大量redo log(修改記錄)的data page是需要考慮被從后臺重新物化(生成)的,因為這種含有大量redo log的數據頁若不及時應用,則在真正請求該數據頁的時候,需要應用大量的redo log,從而導致請求性能極低。因此storage node中的check point是由整個redo log chain的長度控制的。 Aurora中data page的物化操作是由data page所對應的redo log chain的長度控制的。
盡管放大了復制的寫操作,但是這種設計仍大大降低了網絡開銷,并提供了性能和持久性。存儲服務以并行方式擴展了 I/O,而且也不會影響數據庫引擎的寫吞吐量。如圖所示:



上圖顯示了一個Aurora群集,其中有一個主實例和跨多個AZ部署的多個副本實例。在此模型中,主數據庫僅將redo log record寫入存儲服務,并將這些redo log record以及元數據更新信息流式地傳輸到副本實例。 IO流基于分片(邏輯分片 logic segment,即PG)對一批redo log record進行排序,并將每批完成排序的redo log records寫入到所有6個副本(storage node)中,并且在每個storage node上將這些redo log record保存在磁盤上,數據庫引擎等待6個中的4個進行確認答復后才會認為寫入成功從而保證redo log的持久性。每個副本都會應用redo log record從而更新各自的buffer cache。
通過SysBench 我們對Aurora進行了只寫工作負載測試,該工作負載通過分別對上述的跨多個AZ的mirrored mysql集群和跨多個AZ的 Aurora MySQL集群寫入100GB的數據進行測試,并且雙方均運行持續壓測了30分鐘。結果如下:



測試結果解讀:
相同時間內,Aurora能夠處理的事務是鏡像MySQL的35倍。
Aurora中數據庫節點上每個事務的I / O數量比mirrored MySQL少7.7倍。
由于減輕了網絡負載,因此可以積極地通過復制數據以提高持久性和可用性,并且每次發起讀寫請求都可以并行執行,以最大程度地減少性能抖動。

存儲服務來處理redo log還可以最大程度地減少崩潰恢復時間來提高可用性,并消除由后臺處理(如check point,后臺數據頁寫入和備份)引起的性能抖動。
我們研究一下崩潰恢復過程。在傳統數據庫中,崩潰后,系統必須從最新的check point開始并重新應用redo log,以確保應用所有已經持久化的redo log。在Aurora中,持久化的redo log record在存儲服務上持續不斷地異步地在整個集群上被一直被應用。如果data page不是最新的,則對data page的任何讀取請求都可能需要應用一些redo log record。因此,崩潰恢復的處理過程分散在所有常規的數據請求處理中。因此數據庫啟動或者故障恢復時幾乎不需要任何操作。

3.3 存儲服務設計要點

存儲服務(storage service)的核心設計原則是最大程度地減少寫入請求的延遲。因此aurora將大部分的存儲處理都移至后臺。例如,當存儲節點(storage node)忙于處理寫入請求時,不必立即執行舊data page的垃圾回收(GC),除非磁盤快要達到容量上限時,才必須馬上啟動舊數據的gc。在Aurora中,后臺處理程序與前臺業務處理具有負相關性。這不同于傳統的數據庫,在傳統的數據庫中,data page的后臺寫入和check point與系統上的前臺數據寫入負載具有正相關,也就是說后臺的操作會直接影響前臺的數據操作的性能 。由于在aurora中的各個分片是極度平均的分散在整個集群的各個存儲節點上的,而且在存儲服務上還存在4/6的quorum模型進行仲裁,因此后臺操作對前臺的讀寫性能的影響是可控的。



我們更詳細地研究一下存儲節點上的各種處理邏輯。如圖所示,它涉及以下步驟:
(1)接收redo log recored并將其添加到內存隊列中
(2)將redo log record 持久存儲到磁盤上并將確認消息發送給數據庫實例;
(3)將受到的redo log record進行組織并識別出由于某些batch丟失而導致的redo log record空洞。
(4)通過gossip協議與其他的storage node同步,獲得丟失的redo log record,進而填補確實的空洞。
(5)將redo log record合并到新的data page中。
(6)定期將階段性的redo log和對應的新的data page暫存到S3。
(7)定期對舊版本數據進行GC。
(8)定期驗證data page上的CRC code。
上述步驟中僅(1)和(2)是處于讀寫執行路徑上的并且,這兩部是同步執行的,可能會影響到讀寫延遲,其他步驟都是異步執行的。

4. 日志處理

本節描述如何從數據庫引擎生成log,進而始終保證持久狀態,運行時狀態和副本狀態的一致。特別是,會詳細說明如何在不使用昂貴的2PC協議的情況下有效地實現一致性。

4.1 異步處理

由于aurora的核心就是redo log stream,因此可以利用redo log的有序性、順序性,且記錄了data page的變化記錄的特性進行進一步處理。實際上,每個redo log record都有一個關聯的日志序列號(LSN),并且它是由數據庫生成的單調遞增的值。
基于上述redo log的特性,storage node通過異步方式實現了一個簡單的維護狀態的一致性協議,而沒有使用像2PC協議,因為2PC協議容易出錯且對故障的容忍度極低。從更高的層面去看,storage node會一直維護一致性和持久性的point,并在收到未完成的存儲請求的確認后不斷向前推進這些point。由于任何單個存儲節點都可能都丟失一個或多個redo log record,因此它們會與PG的其他成員通過gossip協議找到丟失的redo log record進行填補。由于數據庫本身就維護的運行時狀態,因此我們可以使用單分片讀取來取代基于quorum的讀取,除非在故障恢復的時候數據頁需要rebuild,這時丟失了運行時狀態,才需要基于quorum進行讀取。
數據庫實例中活躍著很多事務,事務的開始順序與提交順序也不盡相同。當數據庫異常宕機重啟時,數據庫實例需要確定每個事務最終要提交還是回滾。
存儲服務記錄了VCL(Volume Complete LSN),可以保證LSN小于VCL的redo log record都是是可用的。在故障恢復時,所有LSN大于VCL的日志都要被截斷。但是數據庫可以通過對redo log record 打標從而其標識為CPLs或一致性點LSNs(ConsistencyPoint LSNs)來進一步限制允許被截斷的point的子集。因此VDL/持久卷LSN( Volume Durable LSN)就是小于或等于VCL,且已經持久化了的最高CPL,因此在故障恢復時需要截斷所有大于VDL的LSN號對應的redo log record。
如下圖所示:


每個事務在物理上由多個mini-transaction組成,而每個mini-transaction是最小原子操作單位,比如B樹分裂可能涉及到多個數據頁的修改,那么這些頁修改對應的對應一組日志就是原子的,在回放redo log時,也需要以mini-transaction為單位。
CPL就是屬于一個mini-transaction的一組日志中最后(最大,因為LSN是遞增的)一條日志的LSN,一個事務由多個CPL組成,所以稱之為CPLs。為了保證不破壞mini-transaction原子性,所有大于VDL的日志,都需要被截斷。
舉個例子:
即使我們的VCL為1007,并且現在的CPLS為:900, 1000和1100,我們也必須截斷LSN>1000的日志。
因此完整性和持久性的含義是不同的,VDL表示了數據庫已經持久化了的處于一致狀態的最新位點,在故障恢復時,數據庫實例以PG為單位確認VDL,截斷所有大于VDL的日志。 數據庫和存儲交互過程如下:
1.每個數據庫級別的事務被分解為多個有序的mini-transactions(MTR),這些事務中的每一個都可以原子執行。
2.每個mini-transaction都由多個連續的log record(根據需要)組成。
3.mini-transaction中的最終log record是CPL。
恢復時,數據庫與storage service進行交互以建立每個PG的持久點(durable point),并使用該持久點來建立VDL,然后發出命令以截斷大于VDL的log record。

4.2 數據庫操作流程

現在,我們針對數據庫引擎的一些常規操作流程進行說明。
4.2.1 Writes
在Aurora中,數據庫實例向存儲節點傳遞redo日志,達成多數派后將事務標記為提交狀態,然后推進VDL,使數據庫進入一個新的一致狀態。在任意時刻,數據庫中都會有大量活躍的并發事務,每個事務都會生成自己的redo log record。數據庫會為每個log record分配一個唯一有序的LSN,這個LSN一定大于當前最新的VDL,為了避免前臺事務并發執行太快,而存儲服務的VDL推進不及時,定義了LSN Allocation Limit(LAL),目前定義的是10,000,000,這個值表示新分配LSN與VDL的差值的最大閥值,設置這個值的目的是避免存儲服務成為瓶頸,進而影響后續的寫操作。
由于底層存儲按segment分片,每個分片管理一部分數據 ,當一個事務涉及的修改跨多個分片時,事務對應的日志會被打散到多個segment上,每個分片只能看到這個事務的部分日志。 為了確保各個分片日志的完整性,每條日志中都包含一個前向鏈接,前向鏈接指向了前一條log record。通過前向鏈接就可以遍歷分布在PG中各個segment上的所有redo log,進而構建一份完成的LSN:SCL(Segment Complete LSN),SCL代表PG當前receive到的最大LSN。SCL主要用于各個storage node通過gossip協議查找和交換各自缺失的redo log record。

4.2.2 Commits
在Aurora中,事務的提交是異步的。每個事務由若干個日志組成,并包含有一個唯一的“commit LSN”。當工作線程處理事務提交請求時,處理提交請求的線程將事務的“commit LSN”提交到持久化隊列中并將事務掛起繼續處理數據庫請求,該持久化隊列中記錄了所有待提交事務的“commit LSN”。當VDL的位點大于事務的commit LSN時,表示這個事務redo日志都已經持久化,可以向客戶端回包,通知事務已經成功執行。在Aurora中,有一個獨立的線程處理事務成功執行的回包工作,因此,從整個提交流程來看,所有工作線程不會因為事務提交等待日志推進而堵塞 ,他們會繼續處理新的請求,通過這種異步提交方式,大大提高了系統的吞吐。
整個處理過程如下:

  1. StorageNode的工作線程T1接收到事務1的提交請求,并將對應的commit-LSN-1放入到持久化隊列中,然后馬上將事務1掛起,繼續處理其他數據庫請求。
  2. StorageNode中的另一個異步線程T2,不斷地將持久化隊列中的每個commit-LSN持久化到磁盤上,并記錄VDL,當VDL大于commit-LSN-1時,表示事務1的redolog全部持久化。
  3. 由單獨的線程T3不停的檢測提交成功的事務列表,并基于事務向客戶端返回包,通知事務執行成功。如下圖所示:


    image.png

4.2.3 Reads

與大多數數據庫一樣,在Aurora中,數據是從buffer cache中進行讀寫的,并且僅當buffer cache中不存在所請求的數據時,才會從磁盤中獲取。從磁盤中讀取的數據也要放入到buffer cache中,而如果buffer cache已滿,則根據特定的淘汰算法(比如LRU),系統會選擇將一個數據頁淘汰置換出去,如果需要被置換的數據頁被修改過,在傳統的數據庫系統中,首先需要將這個數據頁刷盤,確保下次訪問這個頁時,能讀到最新的數據。
但是Aurora不一樣,需要被置換出去的數據頁并不會刷寫到磁盤,而是直接丟棄。 這就要求Aurora緩沖池中的數據頁一定有最新數據,被淘汰的數據頁的LSN需要小于或等于VDL。(注意,這里論文中描述有問題,page-LSN<=VDL才能被淘汰,而不是大于等于) 這個約束保證了兩點:1.這個數據頁所有的修改都已經在日志中持久化,2.當緩存不命中時,通過持久化的數據頁和VDL總能得到最新的數據頁。

在正常情況下,進行讀操作時并不需要達成Quorum。當數據庫實例從disk去讀數據頁時,將當前請求發起時的最新VDL作為本次讀的一致性位點read-point,并選擇一個VDL大于等于read-point的storage node,這樣只需要訪問這一個storage node即可得到本次請求所需要的數據頁的最新版本 。從實現上來看,因為所有數據頁通過segment管理,數據庫實例記錄了storage node管理的分片以及SCL(Segment Complete LSN)信息,因此進行IO操作時,通過元信息可以知道具體哪個storage node有需要訪問的數據頁,并且SCL>read-point。
數據庫實例接收客戶端的請求,會計算出每個PG的Minimum Read Point LSN,在有讀副本實例的情況下,寫實例會通過gossip協議與各個讀副本實例之間建立起每個PG的Minimum Read Point LSN(基于每個storage node得出的Minimum Read Point LSN) ,稱之為PGMRPL( Protection Group Min Read Point LSN)。PGMRPL是全局read-point的低水位,PG中所有LSN低于PGMRPL的redo log record都可以刪掉。換而言之,任意一個storage node上都不會再發起read point LSN小于PGMRPL的數據頁查詢請求。
每個storage node都會記錄PGMRPL,因此可以不斷地應用redo log record生成新的將數據頁進行持久化,并回收不再使用的redo log record。

4.2.4 Replicas
在Aurora中,寫副本實例和最多15個讀副本實例共享一套分布式存儲服務,因此增加讀副本實例并不會造成寫放大,也不會增加磁盤存儲資源。這也是共享存儲的優勢,在不增加存儲成本的前提下增加新的讀副本,從而提升讀能力。
讀副本和寫副本實例間通過redo log同步。寫副本實例往storage node發送日志的同時也會向讀副本發送日志,讀副本按日志順序回放, 如果回放日志時,若需要訪問的數據頁:page1不在緩沖池中,則直接將日志丟棄??梢詠G棄的原因在于,storage node擁有所有日志,當下次需要訪問page1數據頁時,storage node根據read-point,可以構造出特定版本的數據頁。
需要說明的是,寫副本實例向讀副本發送日志是異步的,寫副本執行提交操作并不受讀副本的影響。副本回放日志時需要遵守兩個基本原則:
【1】回放日志的LSN需要小于或等于VDL
【2】回放日志時需要以MTR為單位,確保副本能看到一致性視圖。
在實際場景下,讀副本與寫副本的延時不超過20ms。

4.3 Recovery

大多數數據庫基于經典的ARIES協議處理故障恢復,通過WAL機制確保故障時已經提交的事務持久化,并回滾未提交的事務。
這類系統通常會周期性地做檢查點,并將檢查點信息計入日志。故障時,數據頁中同時可能包含了提交和未提交的數據,因此,在故障恢復時,系統首先需要從上一個檢查點開始回放日志,將數據頁恢復到故障時的狀態,然后根據undo日志回滾未交事務。
從故障恢復的過程來看, 故障恢復是一個比較耗時的操作,并且與檢查點操作頻率強相關。通過提高檢查點頻率,可以減少故障恢復時間,但是這直接會影響系統處理前臺請求吞吐,所以需要在檢查點頻率和故障恢復時間做一個權衡,而在Aurora中由于其存儲計算分離的架構設計,不需要做這種權衡。

傳統數據庫中,故障恢復過程通過回放日志推進數據庫狀態,重做日志時整個數據庫處于離線狀態。
Aurora也采用類似的方法,區別在于將回放日志邏輯下推到存儲節點,并且在數據庫在線提供服務時在后臺常態運行。 因此,當出現故障重啟時,存儲服務能快速恢復,即使在10wTPS的壓力下,也能在10s以內恢復。數據庫實例宕機重啟后,需要故障恢復來獲得運行時的一致狀態,實例與Read Quorum個存儲節點通信,這樣確保能讀到最新的數據,并重新計算新的VDL,超過VDL部分的日志都可以被截斷丟棄。在Aurora中,對于新分配的LSN范圍做了限制,LSN與VDL差值的范圍不能超過10,000,000,這個主要是為了避免數據庫實例上堆積過多的未提交事務,因為數據庫回放完redo日志后還需要做undo recovery,將未提交的事務進行回滾。在Aurora中,收集完所有活躍事務后即可提供服務,整個undo recovery過程可以在數據庫online后再進行。

5. 總結

Aurora的整體架構圖如下所示:


Aurora數據庫引擎基于社區版InnoDB引進行改造,主要對磁盤IO讀寫數據的方式進行了改造,將這些操作分離到存儲服務層。
在社區InnoDB中,一個寫操作會修改緩沖池中數據頁內容,并將對應的redo日志按順序寫入WAL。事務提交時,WAL協議約定對應事務的日志需要持久化后才能返回。實際上,為了防止頁斷裂,緩沖池中修改的數據頁也會寫入double-write區域。數據頁的寫操作在后臺進行,一般是在頁面置換或是做檢查點過程中發生。InnoDB除了IO子系統,還包括事務子系統,鎖管理系統,B+Tress實現以及MTR等。MTR約定了最小事務,MTR中的日志必需以原子的方式執行(比如B+Tree分裂或合并相關的數據頁)。
在Aurora InnoDB中,每個redo log record都代表著可以在每個MTR中原子執行的數據頁的改變。多個redo log records會被組織成batch,一個batch的redo log records會按照PGs劃分,寫入到storage service中。每個MTR的最后一條日志是一致性位點。
與社區的MySQL版本一樣,支持標準的隔離級別和快照讀。 Aurora只讀副本不斷從寫副本實例獲取事務開始和提交信息,并利用該信息提供快照讀功能。Aurora的并發控制完全在數據庫引擎中實現,不會影響存儲服務。數據庫實例與存儲服務層相互獨立,存儲服務層向上為數據庫實例提供統一的數據視圖,數據庫實例從存儲服務層獲取數據與從本地讀取數據一樣。
上圖展示了云上Aurora的部署架構,Aurora利用AmazonRelational Database Service (RDS)來進行管理。
RDS在每個實例部署一個agent,稱之為Host Manager(HM)。HM監控集群的健康狀況并確定是否需要做異常切換,或者是該實例是否需要被替換。每個集群由一個寫副本,0個或多個讀副本組成。所有實例都在一個物理Region(比如美國東部,美國西部),一般是跨AZ部署,并且每個實例都會與同一個region中的分布式存儲服務層進行交互。為了保證安全,Aurora在數據庫層,應用層和存儲層做了隔離。實際上,數據庫實例通過3類Amazon Virtual Private Cloud (VPC)網絡可以相互通信。應用程序可以通過應用層VPC訪問數據庫;數據庫可以通過RDS VPC管控節點交互;數據庫可以通過存儲層VPC和存儲服務節點交互。
存儲服務實際上是部署在一組EC2集群上的,這個集群橫跨至少3個AZ,對多個用戶提供存儲,讀寫IO,備份恢復等服務。storage node管理本地SSD并與數據庫實例和其它storage node交互,備份/還原服務不斷備份新數據到S3,在必要時從S3還原數據。存儲服務的管控系統借助Amazon DynamoDB服務作為持久化存儲,存儲內容包括配置,元數據信息,備份到S3數據的信息等。Aurora使用Amazon Simple Workflow Service來編排和管理需要長時間執行的操作,例如:數據庫存儲卷的恢復操作或者當一個storage node宕機后需要執行的修復操作。為了保證存儲服務高可用,整個系統需要在異常影響到用戶之前,主動快速發現問題。存儲系統中的所有關鍵操作都被監控,一旦關鍵性能或者可用性方面出現問題,則立即會產生報警。

6. 性能數據

性能數據我這里不一一展開了,具體數據大家可以參考原論文。

7. 實踐經驗

我們發現越來越多的應用遷移到Aurora集群,他們有一些共通點,我們希望抽象出一些典型的場景,并總結經驗。

7.1多租戶

Aurora很多用戶都在做Software-as-a-Service(SaaS)服務,這些服務底層存儲模型一般比較穩定,通過多個用戶共享一個數據庫實例來減少成本。這種模式下,數據庫實例上會有大量的表,導致元數據暴增,這加大了字典管理的負擔。這種架構下,用戶一般會面臨解決三類問題:
1).實例上持續較高的吞吐和并發,
2).能夠靈活應對磁盤空間問題,提前評估磁盤空間并具備快速的擴展能力,
3).不同租戶間的影響要控制在最小。
Aurora能完美解決這三類問題。

7.2高并發處理能力

互聯網應用通常會因為各種原因導致壓力驟增,這需要系統有良好的擴展能力和處理高并發的能力。在Aurora中,由于底層的存儲服務和上層的計算節徹底分離,因此可以方便地自動擴容,因此Aurora具備快速擴展能力,并且實際上Aurora有很多用戶連接數長期維持在8000以上。

7.3表結構變更

由于表結構變更往往伴隨著鎖表和拷表,持續時間也比較長,而DDL是一個日常操作,因此需要有一套高效的online DDL機制。主要包括2點:1).schema多版本,每個數據頁都存儲當時的schema信息,頁內數據可以通過schema信息解析,
2).對于修改的page采用modify-on-write機制來減少影響。

7.4軟件更新

由于用戶使用Aurora實例通常只有一個主實例,因此發生任何問題都特別嚴重。Aurora中,所有的持久化數據都在存儲層,數據庫實例的狀態信息可以借助存儲層和元數據獲得,因此可以很方便的構造一個新的數據庫實例,為了提高軟件升級效率,我們通過Zero-Downtime Patch (ZDP)來滾動升級

8. 總結

Aurora誕生的原因是在彈性伸縮的云環境下,傳統的高吞吐OLTP數據庫既不能保證可用性,又不能保證持久性。 Aurora的關鍵點在于將傳統數據庫中的存儲與計算分離,具體而言,將日志部分下推到一個獨立的分布式存儲服務層。由于這種分離架構下,所有IO操作都是通過網絡,網絡將成為最大的瓶頸,因此Aurora集中精力優化網絡以便提高系統吞吐能力。Aurora依靠Quorum模型,在性能影響可控的前提下,解決云環境下的各種異常錯誤。在Aurora中,日志處理技術減少了I/O寫放大,異步提交協議避免了同步等待,同時分離的存儲服務層還避免了離線故障恢復和檢查點操作。Aurora的存儲計算分離方案使得系統整體架構非常簡單,而且方便未來的演進。

Q&A

  1. 一般了解到的Quorum算法只需要滿足Vr + Vw > N即可,為啥Aurora還需要滿足第2個條件?
    從文中了解到Aurora中Quorum算法需要滿足兩個條件:
    a).Vr + Vw > N,NWR算法,確保讀和寫有交集,能讀到最新寫入的數據。
    b).Vw > N/2,避免更新沖突。
    Quorum算法的基本含義是確保讀副本數與寫副本數有交集,能讀到最新寫入的數據。Aurora加入第二條約束主要是為了保證每次寫入集合都與上次寫入的節點集合有交集,確保能讀到最近一次的更新,這樣日志能夠以自增的方式追加,確保不會丟失更新。從另外一個角度來說,寫副本數設置為Vw > N/2也間接在讀寫性能做了均衡。假設N=5,W=1,R=5,滿足第一個條件,每次寫入一個節點即可返回成功,那么讀取時則需要讀取5個副本,才能拿到最新的數據。

  2. 每個segment分片是10G,如果事務跨多個segment分片如何處理?
    每個事務實際是有若干個MTR(mini-transaction)構成,MTR是InnoDB中修改物理塊的最小原子操作單位,MTR的redo日志作為一個整體寫入到全局redo日志區。在Aurora中存儲按照segment分片管理,發送日志時,也按分片歸類后發送,那么就存在一種情況,一個MTR跨多個segement分片,MTR日志被打散的情況。本質上來說,這種情況也不存在問題,因為事務提交時,如果事務跨多個segment分片,則會需要多個PG都滿足Quorum協議才返回,進而推進VDL。所以如果VDL超過了事務的commit-LSN,則表示事務涉及到的日志已經全部持久化,不會存在部分segment日志丟失的問題,所以能夠保證事務持久性。

3.Aurora 讀副本如何實現MVCC?
在Aurora中,寫副本實例往存儲節點發送日志的同時會傳遞日志到讀副本實例,讀副本以MTR為單位回放日志;與此同時,寫副本還會將事務開始和提交的信息傳遞給讀副本,這樣在讀副本實例上也能構造出活躍的事務視圖。在讀副本上執行讀操作時,會依據活躍事務視圖作為記錄可見性判斷依據,進而能讀到合適版本的數據。當然,Aurora讀副本與寫副本之間是異步復制,至多有20ms的延遲,因此在Aurora讀副本上可能不能讀到最新的提交數據。

  1. 為什么Aurora有成本優勢?
    Aurora中,多個數據庫實例(寫副本+多個讀副本)共享一個分布式存儲層。對于數據庫引擎而言,整個存儲服務一個大的資源池,用戶根據需求申請存儲空間,最小粒度是10G,相對于單個實例的本地存儲,存儲空間利用率大大提升,而且非常方便擴展。另一方面,所有數據庫引擎公用一份存儲,零存儲成本增加數據庫引擎,能大幅降低成本。

5.Aurora 的優勢和缺陷?
優勢:
1). 存儲節點,計算節點彈性伸縮,按需配比
2). 一份存儲,對接多個計算節點,多租戶存儲服務,成本低。
3). 只傳遞日志,巧妙的解決寫放大問題。
4). 實例快速故障恢復
5). 架構簡單,通過多副本能快速擴展讀能力,單個寫副本則巧妙地避免了分布式事務等復雜實現。

缺陷:
1). 適合于讀多寫少的應用,寫水平擴展依賴于中間件方案。
2). SQL層與社區版MySQL一樣,復雜查詢能力(比如OLAP場景)較弱。
3). 單個寫副本,無分區多點寫能力(用戶維度+時間維度)
4). 總容量有上限,64TB

6.Aurora 與Spanner的異同點?

image

從對比來看,Aurora與Spanner走的兩條不同的路線,Aurora以市場用戶為導向,所以選擇全面兼容MySQL/PostgreSQL,用戶可以無縫遷移到Aurora。另外就是,Aurora基于MySQL修改,并通過共享存儲方案避免了二階段提交,分布式事務等復雜的實現,因此開發周期相對較短,也更容易出成果。反觀Spanner,是一個重新設計的數據庫,無論是基于Paxos的強同步還是分布式事務的支持,以及利用TrueTime機制實現全局一致性讀等都是比較復雜的實現,功能非常強大,但是在SQL兼容性這塊確做地不夠好,這也可以解釋為什么Aurora廣泛用于云上業務,而Spanner則更多地是在Google內部使用,相信即使Spanner現在開放了云版本,其SQL兼容性也是一個很大的挑戰。當然Aurora本身的數據庫引擎就是MySQL,其對于復雜查詢的短板還需要持續改進和優化

  1. Aurora有沒有checkpoint?
    Aurora的PGMRPL可以認為是檢查點。LSN?小于這個點的數據?頁已經刷盤,?而?大于這個點的?頁可 以刷盤,也可以不不刷盤,LSN?小于這個點的?日志都可以丟棄。PGMRPL在邏輯上與存儲引擎的檢 查點是等效的??梢哉J為是PG上的檢查點。

  2. Aurora與polardb
    Aurora與Polardb都是存儲與計算分離、單寫多讀、共享分布式存儲的架構。很顯然,polardb在寫放?上?沒有做優化。從節點需要同步主庫的臟?頁,共享的存儲,私有的臟?頁,是個很難解決的矛盾。因此polardb的讀節點會影響寫節點。?Aurora可以認為沒有臟?頁。日志即數據庫,可以把日志應?服務器看作更慢的存儲,存儲服務與緩存都可以認為是日志應用服務器的cache。
    從節點與主節點之間有延遲,?aurora存儲的數據頁有多版本,文中明確指出存儲有舊?回收處 理。從節點依據讀取點LSN讀取到指定版本的頁,?文中指出,寫節點到讀復制節點之間的 延遲?于20ms,因此,不可回收的舊版本?應該不會太多。

  3. 臨時表
    每個讀節點雖然只有查詢操作,但是查詢操作會?成臨時表用以保存查詢的中間結果。?成臨時表數據是不會產生日志的。但是這里仍有寫IO,個?人認為,Aurora有可能直接寫在本地存儲,這樣不會產生網絡上的負擔

參考與感謝

http://www.lxweimin.com/p/db00b1e4695e
https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Replication.html
https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Replication.MySQL.html
https://www.cnblogs.com/cchust/p/7476876.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374