十年網(wǎng)站開發(fā)經(jīng)驗 + 多家企業(yè)客戶 + 靠譜的建站團隊
量身定制 + 運營維護+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
譯者:Aceking,極數(shù)云舟技術(shù)合伙人,數(shù)據(jù)庫內(nèi)核研發(fā)專家,負責(zé)企業(yè)級云原生數(shù)據(jù)庫ArkDB等核心數(shù)據(jù)庫產(chǎn)品,華中科技大學(xué)計算機系數(shù)據(jù)庫方向研究生畢業(yè),原達夢數(shù)據(jù)庫產(chǎn)品研發(fā)工程師,負責(zé)達夢數(shù)據(jù)庫內(nèi)核開發(fā),長期致力于數(shù)據(jù)庫原理和源碼開發(fā),精通C/C++,公司內(nèi)部流傳有他的名言:以后再也不敢說精通C++了。

摘要
Amazon Aurora是亞馬遜網(wǎng)絡(luò)服務(wù)(AWS)的一個組成部分,對外提供一個OLTP負載類型的關(guān)系數(shù)據(jù)庫服務(wù)。本文描述這種架構(gòu)設(shè)計考量。我們確信,高吞吐量的數(shù)據(jù)處理的主要約束已經(jīng)從計算、存儲轉(zhuǎn)移到網(wǎng)絡(luò)中來了。Aurora給出一個新穎的架構(gòu)來解決這個約束,其最顯著的特點是把重做日志(redo)處理下放到專門為Aurora設(shè)計的存儲服務(wù)中。文章會介紹如何既能減少網(wǎng)絡(luò)流量,又能快速崩潰恢復(fù)(crash recovery), 還能實現(xiàn)復(fù)制失效不損失數(shù)據(jù),以及存儲上的容錯,自愈。然后介紹Aurora在多存儲節(jié)點中的保持持久狀態(tài)的一致性的高效異步方案,避免了代價高昂繁復(fù)的恢復(fù)協(xié)議。最后,我們分享了18個多月運營Aurora產(chǎn)品的經(jīng)驗,這些經(jīng)驗是從現(xiàn)代云應(yīng)用的客戶們對數(shù)據(jù)庫層的期望中總結(jié)的。
關(guān)鍵字
數(shù)據(jù)庫; 分布式系統(tǒng); 日志處理; 仲裁模型; 復(fù)制; 恢復(fù); 性能; OLTP
01 導(dǎo)論
越來越多的IT負載轉(zhuǎn)移到公有云中,這種全行業(yè)的轉(zhuǎn)變的重大原因包括:公有云服務(wù)商能夠彈性按需提供容量,以及只需支付運營費用而不用支付資產(chǎn)費用的模式。許多IT負載需要一個OLTP的關(guān)系型數(shù)據(jù)庫。因此,提供一種等效甚至超越的預(yù)置數(shù)據(jù)庫用以支持這種轉(zhuǎn)變顯得至關(guān)重要。
越來越多現(xiàn)代分布式云服務(wù),通過解耦計算與存儲,以及跨越多節(jié)點復(fù)制的方式實現(xiàn)了彈性與可伸縮性。這樣做,可以讓我們進行某些操作,例如替換失誤或者不可達的主機,添加復(fù)制節(jié)點,寫入節(jié)點與復(fù)制節(jié)點的故障轉(zhuǎn)移,拓展或者收縮數(shù)據(jù)庫實例的大小等等。在這種環(huán)境下,傳統(tǒng)的數(shù)據(jù)庫系統(tǒng)所面臨的IO瓶頸發(fā)生改變。因為IO可以分散到多租戶集群中的多個節(jié)點和多個磁盤中,導(dǎo)致單個磁盤和節(jié)點不在是熱點。相反,瓶頸轉(zhuǎn)移到發(fā)起這些IO請求的數(shù)據(jù)庫層與執(zhí)行這些IO請求的存儲層之間的網(wǎng)絡(luò)中。除了每秒包數(shù)(packets per second , PPS)以及帶寬這種基本瓶頸外,一個高性能的數(shù)據(jù)庫會向發(fā)起存儲機群并行的寫入,從而放大了網(wǎng)絡(luò)流量。某個異常存儲節(jié)點的性能,磁盤或者網(wǎng)絡(luò)通路,都會嚴重影響響應(yīng)時間。
雖然數(shù)據(jù)庫大部分的操作可以互相重疊執(zhí)行,但是有一些情況需要同步操作,這會導(dǎo)致停頓和上下文切換(可能是線程上下文切換,也可以是內(nèi)核態(tài)與用戶態(tài)切換--譯者注)。情況之一,數(shù)據(jù)庫由于緩沖緩存(buffer cache)未命中,導(dǎo)致進行磁盤讀,讀線程無法繼續(xù)只能等待磁盤讀取完成。將臟頁強制趕出并且刷盤,用以給新頁騰出空間。也會導(dǎo)致緩存(cache)不命中。雖然后臺處理,例如檢查點,臟頁寫入線程能減少這種強制行為,但是仍然會導(dǎo)致停頓,上下文切換以及資源爭用。
事務(wù)提交是另一個干擾源。一個正在提交的事務(wù)的停頓,會阻止另一個事務(wù)的進行。處理多節(jié)點同步協(xié)議的提交,如兩階段提交(2PC)對云級規(guī)模(cloud-scale)的分布式系統(tǒng)來說更是一個挑戰(zhàn)。這些協(xié)議不能容忍失敗。但是高規(guī)模(high-scale)總是充斥著軟硬件失效的“背景噪聲”。同時還有高延遲,因為高規(guī)模系統(tǒng)總是跨多個數(shù)據(jù)中心分布的。
本文中,我們介紹Amazon Aurora,一種新型的數(shù)據(jù)庫服務(wù),通過大膽激進的使用高分布性的云環(huán)境中的日志來解決上述問題。我們給出了一種使用多租戶可伸縮的存儲面向服務(wù)的架構(gòu)(圖1),這種存儲服務(wù)與數(shù)據(jù)庫實例集群松耦合,并且從中提取虛擬的分段的重做日志(redo log).雖然每個數(shù)據(jù)庫實例仍然有大部分的傳統(tǒng)的數(shù)據(jù)庫內(nèi)核(查詢處理,事務(wù),鎖,緩沖緩存,訪問方法,回滾管理), 但是一些功能(重做日志,持久存儲,崩潰恢復(fù),備份恢復(fù))已經(jīng)剝離,放到存儲服務(wù)中。
我們的架構(gòu)與傳統(tǒng)方法相比,有三大明顯優(yōu)勢:首先,把存儲建為一個容錯、自愈、跨多個數(shù)據(jù)中心的獨立服務(wù),我們可以保護數(shù)據(jù)庫,使得網(wǎng)絡(luò)層或存儲層的性能差異,暫時或永久的失效,對數(shù)據(jù)庫不再有影響。我們觀察到持久存儲的失效可以作為一個長時-可用性事件(longlasting availability event)來建模, 而可用性事件可以作為一個長時性能變動來建模,長時性能變動已經(jīng)有一個設(shè)計良好的系統(tǒng)統(tǒng)一處理。 其次,通過存儲寫日志的方式,可以把網(wǎng)絡(luò)的IOPS減少一個量級。一旦我們移除了這個瓶頸,就可以大膽的在大量的其他爭用點上做優(yōu)化,就能獲取比我們基于的MySQL基線源碼更明顯的吞吐量的提升。第三,我們把一些最重要最復(fù)雜的功能(如:備份、日志恢復(fù))從數(shù)據(jù)庫引擎中一次性代價高昂的操作轉(zhuǎn)變?yōu)榉謹偟酱笮头植际綑C群的連續(xù)異步的操作。從而獲得近乎瞬時的崩潰恢復(fù),無需檢查點,同時,備份的代價也不高昂,不會影響前臺處理。
本文介紹了我們的三點貢獻
如何分析云級規(guī)模下的持久性,如何設(shè)計具有對相關(guān)失效具有彈性的仲裁系統(tǒng)。(段2)
如何使用靈巧的分離存儲,使得到存儲的負載低于傳統(tǒng)的1/4。(段3)
如何在存儲分布式系統(tǒng)中消除多階段同步,崩潰恢復(fù),檢查點。(段4)
我們把三個創(chuàng)意組合在一起,設(shè)計出Auraro總體架構(gòu)(段5),隨后(段 6)檢視我們的性能結(jié)果,(段7)展示我們的運營經(jīng)驗,(段8)概述相關(guān)的工作,(段9)給出結(jié)束語。
02 可伸縮的持久性
如果數(shù)據(jù)庫不做其他的事情,必須滿足如下要求:一旦寫入,必須可讀。不是所有系統(tǒng)可以做到這一點。在本段中我們討論了aurora仲裁模型背后的原理,為什么我們要將存儲分段,如何將持久性與可獲得性結(jié)合在一起,減少抖動,并且?guī)椭鉀Q大規(guī)模存儲機群的運營問題。
2.1 復(fù)制與相關(guān)失效
實例的生命周期與存儲的生命周期并沒有太大的相關(guān)。實例失效,用戶會將其關(guān)閉,他們會根據(jù)負載情況調(diào)整其大小。這些特點有助于我們將計算層與存儲層解耦。
一旦你這樣做了,這些存儲節(jié)點和磁盤仍然會失效,因此需要某種形式的復(fù)制實現(xiàn)對失效的彈性。在規(guī)模大的云環(huán)境下,存在持續(xù)的下層背景噪聲,如節(jié)點失效,磁盤、網(wǎng)絡(luò)路徑失效。每一種失效有不同的持續(xù)時間和破壞半徑。比如,某個節(jié)點暫時無法網(wǎng)絡(luò)到達,重啟導(dǎo)致的臨時停機時間,磁盤,節(jié)點,機架,網(wǎng)關(guān)的葉節(jié)點或者主干節(jié)點, 甚至整個數(shù)據(jù)中心的永久失效。
在存在復(fù)制的系統(tǒng)中,實現(xiàn)容錯使用一種[]基于仲裁的投票協(xié)議。如果一個復(fù)制數(shù)據(jù)項有V個副本,每個副本可以賦予一個投票,一次讀操作或者寫操作分別需要Vr個投票的讀仲裁,或者Vw個投票的寫仲裁。那么要達到一致性,仲裁必須兩個規(guī)則,首先,每次讀操作必須能獲得最新的修改。公式為:
Vr+Vw > V,
這個規(guī)則,保證讀取的節(jié)點集與寫節(jié)點集有交集,讀仲裁至少能讀到一個最新版本的數(shù)據(jù)副本。其次,每次寫必須占副本多數(shù),用以避免寫沖突。即公式:
Vw > V/2,
一個能容忍復(fù)制數(shù)據(jù)項(V=3)一個節(jié)點損失的通用的方法是,寫需要2/3的投票仲裁(Vw=2),讀也需要2/3投票仲裁(Vr=2)。
我們確信2/3的投票仲裁數(shù)是不充分的,為了解釋為什么,先了解AWS的一個概念,可用區(qū)(AZ,availability zone)是區(qū)域(region)的子集。一個可用區(qū)與其他可用區(qū)可以低延遲的連接,但是可以隔離其他可用區(qū)的故障,例如,電源,網(wǎng)絡(luò),軟件部署,洪水等等??鏏Z的分布復(fù)制數(shù)據(jù)可以保證典型的大規(guī)模故障模式只影響一個復(fù)制副本,這意味著,可以簡單地放三個副本到在不同的可用區(qū),就可以支持大范圍的事件容錯,但是少量的個別失效事件除外。
然而,在一個大的存儲機群,故障的背景噪聲意味著,任意給一個時刻,都會有一個節(jié)點或者磁盤的子集可能壞掉并修復(fù)。這些故障可能在可用區(qū)A、B、C中獨立分布。然而,可用區(qū)C由于火災(zāi)、洪水、屋頂?shù)顾鹊龋c此同時,可用區(qū)A或者B也有故障(故障背景噪聲),就打破了任意一個副本的仲裁過程。在這一點上,2/3票數(shù)的讀取仲裁模型,已經(jīng)失去了兩個副本,因此無法確定第三個副本是否是最新的。換一句話說,雖然,每個可用區(qū)的個體復(fù)制節(jié)點故障互不相關(guān),但是一個可用區(qū)的失效,則與可用區(qū)所有的節(jié)點和磁盤都相關(guān)。仲裁系統(tǒng)要能容忍故障背景噪聲類型的故障與整個可用區(qū)的故障同時發(fā)生的情況。
在Aurora中,我們選用一種可以容兩種錯誤的設(shè)計:(a)在損失整個可用區(qū)外加一個節(jié)點(AZ+1)情況下不損失數(shù)據(jù)。
(b)損失整個可用區(qū)不影響寫數(shù)據(jù)的能力。
在三個可用區(qū)的情況下,我們把一個數(shù)據(jù)項6路寫入到3個可用區(qū),每個可用區(qū)有2個副本。使用6個投票模型(V=6),一個寫仲裁要4/6票數(shù)(Vw=4),一個讀仲裁需要3/6票數(shù)(Vr=3),在這種模型下,像(a)那樣丟失整個可用區(qū)外加一個節(jié)點(總共3個節(jié)點失效),不會損失讀取可用性,像(b)那樣,損失2個節(jié)點,乃至整個可用區(qū)(2個節(jié)點),仍然保持寫的可用性。保證讀仲裁,使得我們可以通過添加額外的復(fù)制副本來重建寫仲裁。
2.2 分段的存儲
現(xiàn)在我們考慮AZ+1情況是否能提供足夠的持久性問題。在這個模型中要提供足夠的持久性,就必須保證兩次獨立故障發(fā)生的概率(平均故障時間,Mean Time to Failure, MTTF)足夠低于修復(fù)這些故障的時間(Mean Time to Repair, MTTR)。如果兩次故障的概率夠高,我們可能看到可用區(qū)失效,打破了沖裁。減少獨立失效的MTTF,哪怕是一點點,都是很困難的。相反,我們更關(guān)注減少MTTR,縮短兩次故障的脆弱的時間窗口。我們是這樣做的:將數(shù)據(jù)庫卷切分成小的固定大小的段,目前是10G大小。每份數(shù)據(jù)項以6路復(fù)制到保護組(protection Group,PG)中,每個保護組由6個10GB的段組成,分散在3個可用區(qū)中。每個可用區(qū)持有2個段。一個存儲卷是一組相連的PG集合,在物理上的實現(xiàn)是,采用亞馬遜彈性計算云(ES2)提供的虛擬機附加SSD,組成的大型存儲節(jié)點機群。組成存儲卷PG隨著卷的增長而分配。目前我們支持的卷沒有開啟復(fù)制的情況下可增長達64TB。
段是我們進行背景噪聲失效和修復(fù)的獨立單元。我們把監(jiān)視和自動修復(fù)故障作為我們服務(wù)的一部分。在10Gbps的網(wǎng)絡(luò)連接情況下,一個10GB的段可以在10秒鐘修復(fù)。只有在同一個10秒窗口中,出現(xiàn)兩個段的失效,外加一個可用區(qū)的失效,并且該可用區(qū)不包含這兩個段,我們才可以看到仲裁失效。以我們觀察的失效率來看,幾乎不可能,即使在為我們客戶管理的數(shù)據(jù)庫數(shù)量上,也是如此。
2.3 彈性的運營優(yōu)勢
一旦有人設(shè)計出一種能對長時失效自然可復(fù)原(彈性)的系統(tǒng),自然也對短時失效也能做到可復(fù)原。一個能處理可用區(qū)長時失效的系統(tǒng),當(dāng)然也能處理像電源事故這樣的短時停頓,或者是因糟糕的軟件部署導(dǎo)致的回滾。能處理多秒的仲裁成員的可用性的損失的系統(tǒng),也能處理短時的網(wǎng)絡(luò)的阻塞,單個存儲節(jié)點的過載。
由于我們系統(tǒng)有高容錯性,可以利用這點,通過引起段不可用,做一些維護工作。比如 熱管理就直截了當(dāng),我們直接把把熱點磁盤或節(jié)點的一個段標(biāo)記為壞段,然后仲裁系統(tǒng)通過在機群中遷移到較冷的節(jié)點來修復(fù)。操作系統(tǒng)或者安全打補丁在打補丁時,也是一個短時不可用事件。甚至,軟件升級也可以通過這個方式做。在某個時刻,我們升級一個AZ,但是確保PG中不超過一個成員(段或者節(jié)點)也在進行補丁。因此,我們的系統(tǒng)可以用敏捷的方法論在我們的存儲服務(wù)中進行快速部署。
03 日志即數(shù)據(jù)庫
本段解釋為什么傳統(tǒng)的數(shù)據(jù)庫系統(tǒng)在采用段2所述的分段的復(fù)制存儲系統(tǒng),仍難以承受網(wǎng)絡(luò)IO和同步停頓導(dǎo)致的性能負擔(dān)。我們解釋了將日志處理剝離到存儲服務(wù)的方法,以及實驗證實這種方法如何顯著的降低網(wǎng)絡(luò)IO,最后講述了最小化同步停頓和不必要寫操作的各種技術(shù)。
3.1 寫放大的負擔(dān)
我們的存儲卷的分段存儲,以及6路寫入4/6仲裁票數(shù)的模型,具有高彈性。但是不幸的是,這種模型會讓傳統(tǒng)的數(shù)據(jù)庫如MYSQL無法承受這種性能負擔(dān),因為應(yīng)用往存儲做一次寫入,會產(chǎn)生存儲中許多不同的真實IO。高量的IO又被復(fù)制操作放大,產(chǎn)生沉重的PPS(每秒包數(shù),packets per second)負擔(dān)。同時,IO也會導(dǎo)致同步點流水線停滯,以及延遲放大。雖然鏈復(fù)制[8]以及其他替代方案能減少網(wǎng)絡(luò)代價,但是仍然有同步停頓以及額外的延遲。讓我們看看傳統(tǒng)的數(shù)據(jù)庫寫操作是怎么進行的把。像Mysql這樣的系統(tǒng),一邊要向已知對象(例如,heap文件,B-數(shù)等等)寫入頁,一邊要像先寫日志系統(tǒng)(write-ahead log, WAL)寫入日志。日志要記錄頁的前像到后像的修改的差異。應(yīng)用日志,可以讓頁由前像變?yōu)楹笙瘛?br/>而實際上,還有其他的數(shù)據(jù)也要寫入。舉個例子,考慮一下如圖2所示以主從方式實現(xiàn)的同步鏡像mysql配置, 用以實現(xiàn)跨數(shù)據(jù)中心的高可用性。AZ1是主mysql實例,使用亞馬遜彈性塊存儲(EBS),AZ2是從Mysql實例,也用EBS存儲,往主EBS的寫入,均要通過軟件鏡像的方法同步到從EBS卷中。

圖2所示,引擎需要寫入的數(shù)據(jù)類型:重做日志,binlog日志,修改過數(shù)據(jù)頁,double-write寫,還有元數(shù)據(jù)FRM文件。圖中給了如下的實際IO順序:
步驟1步驟2,像EBS發(fā)起寫操作,并且會像本地可用區(qū)的EBS鏡像寫入,以及操作完成的響應(yīng)消息。
步驟3,使用塊級鏡像軟件將寫入轉(zhuǎn)入到從節(jié)點。
步驟4和5從節(jié)點將傳入的寫入操作執(zhí)行到從節(jié)點的EBS和它的EBS鏡像中。
上述MySQL的鏡像模型不可取,不僅僅因為它如何寫入的,也因為它寫進了什么數(shù)據(jù)。首先,1,3,5步驟是順序同步的過程,由于有許多順序?qū)懭?,產(chǎn)生了額外的延遲。抖動也被放大,哪怕是在異步寫的時候,因為它必須等待最慢的操作,讓系統(tǒng)任憑異常節(jié)點的擺布。按照分布式視角來看,這個模型可以視作4/4的寫仲裁,在失效或者異常節(jié)點的異常性能面前,顯得十分脆弱。其次,OLTP應(yīng)用產(chǎn)生的用戶操作,可以產(chǎn)生許多不同類型的寫入,而這些數(shù)據(jù)寫入方式雖然都不同,但是卻表示同樣的信息。例如,往雙寫緩沖的寫入,主要是為了防止存儲層頁的損壞,(但是內(nèi)容與頁的普通寫入是一樣的)
3.2 日志處理分離到存儲
傳統(tǒng)數(shù)據(jù)庫修改一個頁,就會產(chǎn)生日志記錄。調(diào)用日志應(yīng)用器,應(yīng)用日志到內(nèi)存中該頁數(shù)據(jù)的前像,得到該頁的后像。事務(wù)提交前日志必須已經(jīng)寫入,而數(shù)據(jù)頁則可推后寫入。在Aurora中,通過網(wǎng)絡(luò)的寫入只有重做日志。數(shù)據(jù)庫層不寫入數(shù)據(jù)頁,也沒有后臺寫入,沒有檢查點,沒有Cache替換。相反,日志應(yīng)用器已經(jīng)放置到存儲層,存儲層在后臺生成,或者按需生成數(shù)據(jù)庫的數(shù)據(jù)頁。當(dāng)然,從頭開始應(yīng)用所有修改的,代價讓人難以承受,因此,我們在后臺持續(xù)生成數(shù)據(jù)頁以避免每次抓取時在重新生成這些頁。從正確性的角度來看,后臺生成是完全可選的:就引擎而言,日志即數(shù)據(jù)庫 ,存儲所有具體化出來的頁,都可以視作日志應(yīng)用的緩存。與檢查點不同,只有那些很長修改鏈的頁才會要重新具化 ,檢查點由整個日志鏈長度控制,而Aurora由給定頁的日志鏈長度來控制。
盡管復(fù)制會導(dǎo)致的寫放大,我們的方法明顯能減少網(wǎng)絡(luò)負載,而且保證性能與持久性。在令人尷尬的并行寫入,存儲能超載IO而不影響數(shù)據(jù)庫引擎的寫吞吐能力。舉個例子,圖3所示,是一個一主多從Aurora集群,部署在多個可用區(qū)中。在這個模型中,主庫往存儲寫入日志,并且把這些日志與元數(shù)據(jù)更新發(fā)送給從節(jié)點?;诠泊鎯δ繕?biāo)(一個邏輯段,比如,PG)的一批批日志都是完全有序的。把每個批次以6路傳輸給復(fù)制節(jié)點,存儲引擎等待4個或4個多的回應(yīng),用以滿足寫仲裁條件,來判斷日志在持久上或硬化上是否有問題。復(fù)制節(jié)點應(yīng)用這些日志記錄來更新自身的緩存緩沖。

為了測量網(wǎng)絡(luò)IO的狀況,我們在以上所述的兩種mysql配置下,進行了100GB的數(shù)據(jù)集只寫負載的sysbench[9]測試:一種是跨多個可用區(qū)的mysql鏡像配置,另一個是Aurora的RDS,在r3.8large EC2的實例中運行了30分鐘。
實驗結(jié)果如表1所示。在30分鐘的時間里,在事務(wù)上,Aurora比mysql鏡像多35倍,雖然Aurora的寫放大了6倍,但是每個事務(wù)寫IO仍然比mysql鏡像小7.7倍。這里并沒有記錄EBS的鏈?zhǔn)綇?fù)制與mysql跨可用區(qū)的寫。每個存儲節(jié)點,只是6個復(fù)制節(jié)點中的一個,因此看不到寫放大。這里的IO數(shù)量減少了46倍。寫入了更少的數(shù)據(jù),省下的網(wǎng)絡(luò)能力,可以激進地通過復(fù)制實現(xiàn)持久性與可用性,并在發(fā)起并行請求時,最小化抖動的影響。
把日志處理放到存儲服務(wù)中,也可以最小化崩潰恢復(fù)的時間,來提高可用性,并且消除檢查點、后臺寫、備份等后臺處理所帶來的抖動。我們再看崩潰恢復(fù)。傳統(tǒng)數(shù)據(jù)庫在崩潰之后,必須從最近的檢查點開始,將日志重演并要報保證所有的保存的日志都被應(yīng)用。在Aurora中,持續(xù)的日志應(yīng)用發(fā)生在存儲中,并且它是持續(xù)的,異步的,分散在機群中的。任何一次讀頁請求,如果該頁不是當(dāng)前版本,則要應(yīng)用一些重做日志。因此,崩潰恢復(fù)過程已經(jīng)分散到所有的正常的前臺處理中,不需要在數(shù)據(jù)庫啟動的時候執(zhí)行。
3.3 存儲服務(wù)的設(shè)計點
我們的存儲服務(wù)設(shè)計的核心原則是最小化前臺寫請求的延遲。我們把大部分的存儲處理移到了后臺。鑒于存儲層前臺的請求峰值與平均值之間的自然變動,我們有足夠的時間在前臺請求之外進行這些處理。而且我們也有機會用cpu換磁盤。比如說,當(dāng)存儲節(jié)點忙于前端請求處理,就沒有必要進行舊頁的垃圾回收(GC),除非該磁盤快滿了。Aurora中,后臺處理與前臺處理負相關(guān)。與傳統(tǒng)的數(shù)據(jù)庫不一樣,傳統(tǒng)數(shù)據(jù)庫的后臺寫頁、檢查點處理與系統(tǒng)的的前臺的請求量正相關(guān)。如果系統(tǒng)中有積壓請求,我們會抑制前端請求,避免形成長隊列。因為系統(tǒng)中的段以高熵的形式(高混亂程度)分布在不同的存儲節(jié)點中,限制一個存儲節(jié)點在4/6仲裁系統(tǒng)中自然的作為慢節(jié)點處理。
現(xiàn)在以更多的細節(jié)來觀察存儲節(jié)點各種活動。如圖4所示,包含了如下若干步驟:
1、接收日志記錄并且放入內(nèi)存隊列中。
2、保存到磁盤并且返回應(yīng)答。
3、組織日志記錄,并且識別日志中的空白,因為有些批次的日志會丟失。
4、與對等節(jié)點交流(gossip)填住空白。
5、合入日志到新的數(shù)據(jù)頁。
6、周期地將新數(shù)據(jù)頁和日志備份到S3。
7、周期的回收舊版本。
8、校驗頁中的CRC編碼。
注意,以上各個步驟不全是異步的,1 和 2 在前臺路徑中有潛在的延遲影響。

04 日志前行
本段中,我們介紹數(shù)據(jù)庫引擎生成的日志如何在持久狀態(tài),運行狀態(tài),復(fù)制狀態(tài)始終保持一致。特別是,如何不需要2PC協(xié)議高效地實現(xiàn)一致性。首先,我們展示了如何在崩潰恢復(fù)中避免使用高昂的重做處理。我們解釋正常操作中如何維護運行狀態(tài)與復(fù)制狀態(tài)。最后,我們披露崩潰恢復(fù)的細節(jié)。
4.1 解決方案草圖:異步處理
因為我們把數(shù)據(jù)庫建模為一個日志流,事實上日志作為有序的修改序列,這點我們也要利用。實際上,每個日志都有與之關(guān)聯(lián)的日志序列號(Log Sequence Number, LSN), LSN是由數(shù)據(jù)庫產(chǎn)生的單調(diào)遞增的值。
這就可以使我們簡化共識協(xié)議,我們采用異步方式協(xié)議而不是2PC協(xié)議。2PC協(xié)議交互繁復(fù)并且不容錯。在高層次上,我們維護一致性與持久化的點,在接收還未完成的存儲請求的響應(yīng)時推進這些點,并且是持續(xù)的推進。由于單獨的存儲節(jié)點可能會丟失一個或者多個日志記錄,它們可以與同PG的其他成員進行交流,尋找空白并填補空洞。運行狀態(tài)是由數(shù)據(jù)庫維護的,我們可以直接從存儲段讀取而不需要讀仲裁。但在崩潰恢復(fù)時候,運行狀態(tài)已丟失需要重建的時候除外,這時候仍需要讀仲裁。
數(shù)據(jù)庫可能有很多獨立的未完成的事務(wù),這些事務(wù)可以與發(fā)起時順序完全不同的順序完成(達到持久化的結(jié)束狀態(tài))。假設(shè)數(shù)據(jù)庫奔潰或重啟,這些獨立事務(wù)是否回滾的決策是分開的。則跟蹤并撤銷部分完成事務(wù)的邏輯保留在數(shù)據(jù)庫引擎中,就像寫入簡單磁盤一樣。在重啟過程中,在允許數(shù)據(jù)庫訪問存儲卷之前,存儲進行自身的崩潰恢復(fù),而不關(guān)心用戶層的事務(wù)。當(dāng)然,要保證數(shù)據(jù)庫看到的存儲系統(tǒng)是單一的視圖,盡管實際上存儲是分布式的。
存儲服務(wù)決定一個高的LSN,保證在在這個LSN之前的日志記錄都是可以讀取的。(VCL,volume Complete LSN)。在恢復(fù)過程中,對應(yīng)LSN任何高于VCL的重做日志都要丟棄。但是,數(shù)據(jù)庫還可以進一步約束條件,取其子集,用以標(biāo)記哪些日志可以丟棄。這個子集就是CPL集(Consistency Point LSNs)。因此,我們還可以定義一個VDL(volume durable LSN)為小與VCL的大CPL。舉個例子,雖然數(shù)據(jù)完成到LSN為1007,但是數(shù)據(jù)庫標(biāo)記的CPL集為900,1000,1100。在這種情況下,我們丟棄的位置為1000(1000以后的都要丟棄),也就是我們完成到1007(VCL),但是持久到1000(VDL)。
因此,完成性與持久性是不同的。CPL可以看作存儲事務(wù)必須有序接受的某種限制形式。如果客戶端沒有對此加以區(qū)別,我們把每個日志記錄標(biāo)記為一個CPL。實際上,數(shù)據(jù)庫與存儲以如下方式交互:
每個數(shù)據(jù)庫層面的事務(wù),都被打斷為多個迷你事務(wù)(mini-transactions,MTRs),迷你事務(wù)是有序的,而且是原子的方式執(zhí)行(就是不可分割地執(zhí)行)。
一個迷你事務(wù)由多條連續(xù)的日志記錄組成(要多少有多少)。
迷你事務(wù)的最后一條日志記錄就是一個CPL
在崩潰恢復(fù)過程中,數(shù)據(jù)庫告訴存儲層為每個PG建立一個持久點,用來建立一個VDL,然后發(fā)起丟棄高于VDL日志的命令。
4.2 正常操作
我們現(xiàn)在描述數(shù)據(jù)庫的“正常操作”, 依次關(guān)注寫、讀、提交與復(fù)制。
4.2.1 寫
在Aurora中,數(shù)據(jù)庫不停地與存儲服務(wù)交互,并且維護狀態(tài)以建立仲裁,推進卷持久性(volume durablity),并且在提交時注冊事務(wù)。舉個例子,在正常/前轉(zhuǎn)路徑中,當(dāng)數(shù)據(jù)庫接收到響應(yīng)消息為每批日志建立寫仲裁,則推進當(dāng)前VDL。在任意給定時刻,數(shù)據(jù)庫可能有大量的并發(fā)事務(wù)活動。每個事務(wù)均產(chǎn)生日志,數(shù)據(jù)庫又為每條日志分配唯一的有序的LSN,但是分配受限于一條原則,LSN不能大于當(dāng)前VDL與一個常量值的和。這個常量稱為LSN分配限值(LAL, LSN Allocation limit)(目前設(shè)置為一千萬)。這個限值保證數(shù)據(jù)庫的LSN不超出存儲系統(tǒng)太遠,在存儲或者網(wǎng)絡(luò)跟不上時,提供一個反壓,用以調(diào)節(jié)來入的寫請求。
注意,每個PG的每個段僅僅能看到存儲卷日志記錄的子集,該子集的日志僅僅影響駐留于該段的頁。每個日志記錄包含一個指向PG中前一個日志記錄的反向鏈接。這些反向鏈接可以跟蹤達到每個段的日志記錄的完成點,用以建立段完成LSN(Segment Complete LSN, SCL), SCL是表示一個大LSN,在該LSN之下所有的日志記錄均已經(jīng)被該PG接收到。存儲節(jié)點使用SCL相互交流,用來查找并交換獲得自己確實的那部分日志。
4.2.2 提交
在Aurora中,事務(wù)提交是異步完成的。當(dāng)客戶端提交一個事務(wù),處理事務(wù)提交的線程記錄一個“提交LSN”(commit lsn)到一個單獨的等待提交的事務(wù)列表中,然后將事務(wù)擱置去處理其他任務(wù)。這相當(dāng)于WAL協(xié)議是基于事務(wù)提交的完成,也即當(dāng)且僅當(dāng)最新的VDL大于或等于事務(wù)的提交LSN的時候。隨著VDL的推進,數(shù)據(jù)庫在正在等待提交事務(wù)識別符合條件的,由專有線程向正在等待的客戶端發(fā)送提交響應(yīng)消息。工作線程不會因為提交事務(wù)而暫停,它們僅是把其他的掛起的請求拉起繼續(xù)處理。
4.2.3 讀
在Aurora中,和大部分的數(shù)據(jù)庫一樣,頁由buf和cache提供。只有頁是否在Cache中還存疑的時候,才會導(dǎo)致存儲IO請求。
如果buf cache已滿,系統(tǒng)找出受害頁,將其趕出cache。在傳統(tǒng)數(shù)據(jù)庫中,如果受害頁是“臟頁“,替換前要刷盤。這就保證后續(xù)的頁的提取,總是最新數(shù)據(jù)。然而Aurora在驅(qū)逐頁的時候并沒有將頁寫出,但它強制執(zhí)行一個保證:在buff或cache中的頁始終是最新的版本。實現(xiàn)這一保證的措施是,僅僅將“頁LSN(Page LSN)“(與頁相關(guān)最新的日志LSN)大于或者等于VDL。這個協(xié)議保證了:(a)頁的所有修改在日志中都已硬化, (b)在cache沒命中的時候,獲得最近的持久化版本的頁,請求當(dāng)前VDL版本頁就足夠了。
數(shù)據(jù)庫在正常情況下不需要使用讀仲裁來建立一致性。當(dāng)從磁盤讀取一個頁,數(shù)據(jù)庫建立一個讀取點(read-point)
表示讀取發(fā)起時候的VDL。數(shù)據(jù)庫可以選擇一個相對于讀取點數(shù)據(jù)完整存儲節(jié)點,從而取得到最新版本的數(shù)據(jù)。存儲節(jié)點返回的數(shù)據(jù)頁一定與數(shù)據(jù)庫的迷你事務(wù)(mtr)的預(yù)期語義一致。因為,數(shù)據(jù)庫管理著往存儲節(jié)點日志的饋送,跟蹤這個過程(比如,每個端的SCL),因此它知道那些段滿足讀取要求(那些SCL大與讀取點的段)。然后直接向有足額數(shù)據(jù)的段發(fā)起讀請求。
考慮到數(shù)據(jù)庫知道那些讀操作沒有完成,可以在任何時間在每個PG的基礎(chǔ)上計算出最小的讀取點LSN(Minimum Read Point LSN). 如果有存儲節(jié)點交流寫的可讀副本,用來建立所有節(jié)點每個PG的最小讀取點,那么這個值就叫做保護組最小讀取點LSN(Protection Group Min Read Point LSN, PGMRPL). PGMRPL用來標(biāo)識“低位水線”,低于這個“低位水線”的PG日志記錄都是不必要的。換句話說,每個存儲段必須保證沒有低于PGMRPL的讀取點的頁讀請求。每個存儲節(jié)點可以從數(shù)據(jù)庫了解到PGMRPL,因此,可以收集舊日志,物化這些頁到磁盤中,再安全回收日志垃圾。
實際上,在數(shù)據(jù)庫執(zhí)行的并發(fā)控制協(xié)議,其數(shù)據(jù)庫頁和回滾段組織方式,與使用本地存儲的傳統(tǒng)數(shù)據(jù)庫的組織方式并無二致。
4.2.4 復(fù)制節(jié)點(replicas)
在Aurora中,在同一個存儲卷中,一次單獨的寫最多有15個讀復(fù)制。因此,讀復(fù)制在消耗存儲方面,并沒有增加額外的代價,也沒有增加額外的磁盤寫。為了最小化延遲,寫生成的日志流除了發(fā)送給存儲節(jié)點,也發(fā)送所有的讀復(fù)制節(jié)點。在讀節(jié)點中,數(shù)據(jù)庫依次檢查日志流的每一天日志記錄。如果日志引用的頁在讀節(jié)點的緩沖緩存中,則日志應(yīng)用器應(yīng)用指定的日志記錄到緩存中的頁。否則,簡單丟棄日志記錄。注意從寫節(jié)點的視角來看,復(fù)制節(jié)點是異步消費日志流的,而寫節(jié)點獨立于復(fù)制節(jié)點響應(yīng)用戶的提交。復(fù)制節(jié)點在應(yīng)用日志的時候必須遵從如下兩個重要規(guī)則:(a)只有LSN小于或等于VDL的日志記錄才能應(yīng)用。(b)只有屬于單個迷你事務(wù)(MTR)的日志記錄才能被應(yīng)用(mtr不完整的日志記錄不能應(yīng)用),用以保證復(fù)制節(jié)點看到的是所有數(shù)據(jù)庫對象的一致視圖。實際上,每個復(fù)制節(jié)點僅僅在寫節(jié)點后面延遲一個很短的時間(20ms甚至更少)。
4.3 恢復(fù)
大部分數(shù)據(jù)庫(如ARIES)采用的恢復(fù)協(xié)議,取決于是否采用了能表示提交事務(wù)所有的精確的內(nèi)容的先寫日志(write ahead log,WAL)。這些系統(tǒng)周期的做數(shù)據(jù)庫檢查點,通過刷臟頁到磁盤,并在日志中寫入檢查點記錄,粗粒度地建立持久點。在重啟的時候,任何指定的頁都可能丟失數(shù)據(jù),丟失的數(shù)據(jù)可能是已經(jīng)提交了的,也可能包含未提交的。因此,在崩潰恢復(fù)的過程中,系統(tǒng)從最近的檢查點開始處理日志,使用日志應(yīng)用器將這些日志應(yīng)用日志到相關(guān)數(shù)據(jù)庫頁中。通過執(zhí)行相應(yīng)的回滾日志記錄,可以回滾崩潰時正在運行的事務(wù),這個過程給數(shù)據(jù)庫帶來了故障點時候的一致性狀態(tài)。崩潰恢復(fù)是一個代價高昂的操作,減少檢查點間隔可以減少代價,但是這會帶來影響前端事務(wù)的代價。傳統(tǒng)數(shù)據(jù)庫需要在兩者做權(quán)衡,而Aurora則不需要這樣的權(quán)衡。
傳統(tǒng)數(shù)據(jù)庫有一個重要的簡化原則是,使用同一個日志應(yīng)用器,推進數(shù)據(jù)庫狀態(tài)與進行同步的日志恢復(fù)。并且此時從前端看來,數(shù)據(jù)庫庫是脫機的。Aurora設(shè)計也遵循同樣的原則。但是日志應(yīng)用器已經(jīng)從數(shù)據(jù)庫中解耦出來,直接在存儲服務(wù)中在總是在后臺并行運行。一旦數(shù)據(jù)庫開始執(zhí)行卷恢復(fù),它會與存儲服務(wù)合作來進行。結(jié)果是,Aurora數(shù)據(jù)庫恢復(fù)速度非??欤ㄍǔT?0秒以下),哪怕是每秒運行100,000條語句時候崩潰后情況下做恢復(fù)。
Aurora數(shù)據(jù)庫不需要在崩潰之后重建運行狀態(tài)。在崩潰的情況下,它會聯(lián)系每個PG,只要數(shù)據(jù)滿足了寫仲裁的條件,段的讀仲裁完全可以保證能發(fā)現(xiàn)這些數(shù)據(jù)。一旦數(shù)據(jù)庫為每個PG建立了讀仲裁,則可以重新計算VDL,生成高于該VDL的丟棄范圍(truncation range),保證這個范圍后的日志都要丟棄,丟棄范圍是這是數(shù)據(jù)庫能可以證明的大可能保證未完成的日志(不是完整MTR的日志)可見的結(jié)束LSN。然后,數(shù)據(jù)庫可以推斷出LSN分配的上限,即LSN的上限能超過VDL多少(前面描述的一千萬)。丟棄范圍使用時間數(shù)字(epoch number)作為版本,寫在存儲服務(wù)中,無論崩潰恢復(fù)還是重啟,都不會弄混丟棄日志的持久化。
數(shù)據(jù)庫仍然需要做undo恢復(fù),用來回滾崩潰時正在進行的事務(wù)。但是,回滾事務(wù)可以在數(shù)據(jù)庫聯(lián)機的時候進行,在此之前,數(shù)據(jù)庫已經(jīng)通過回滾段的信息,建立了未完成事務(wù)列表。
05 放在一起
本段中,我們描述如圖5所示Aurora的各個模塊。
Aurora庫是從社區(qū)版的mysql/innodb數(shù)據(jù)庫fork出來的分支,與之不同的主要在讀寫磁盤數(shù)據(jù)這塊。在社區(qū)版的Innodb中,數(shù)據(jù)的寫操作修改buffer中的頁,相應(yīng)的日志也按LSN順序?qū)懭隬AL的緩存中。在事務(wù)提交的時候,WAL協(xié)議只要求事務(wù)的日志記錄寫入到磁盤。最終,這個被修改的緩存頁使用雙寫技術(shù)寫入到磁盤中,使用雙寫的目的是為了避免不完整的頁寫(partial page writes)。這些寫操作在后臺執(zhí)行,或者從cache驅(qū)逐頁出去時候執(zhí)行,或者在檢查點時候執(zhí)行。除IO子系統(tǒng)外,innodb還包含事務(wù)子系統(tǒng),鎖管理器,B+樹的實現(xiàn),以及“迷你事務(wù)”(MTR)。innodb的將一組不可分割執(zhí)行(executed atomically)的操作稱為一個迷你事務(wù)(例如,分裂/合并B+樹頁)。

在Aurora的Innodb變種中,mtr中原先那些不可分割執(zhí)行日志,按照批次組織,分片并寫入到其所屬的PG中,將mtr最后一個日志記錄標(biāo)記為一致點(consistency point), Aurora在寫方面支持與社區(qū)mysql一樣的隔離等級(ANSI標(biāo)準(zhǔn)級別,Snapshot 隔離級,一致性讀)。Aurora讀復(fù)制節(jié)點可以從寫節(jié)點獲取事務(wù)開始和提交的持續(xù)信息,利用這些信息支持本地只讀事務(wù)的snapshot隔離級。注意,并發(fā)控制是完全有數(shù)據(jù)庫實現(xiàn)的,對存儲服務(wù)沒有任何影響。存儲服務(wù)為下層數(shù)據(jù)提供了展現(xiàn)了一個統(tǒng)一視圖,與社區(qū)版本innodb往本地存儲寫數(shù)據(jù)的方式,在邏輯上完全等效。
Aurora使用亞馬遜關(guān)系數(shù)據(jù)庫服務(wù)(RDS)作為控制平臺。RDS包含一個數(shù)據(jù)庫實例上的代理,監(jiān)控集群的健康情況,用以決定是否進行故障切換,或者是否進行實例替換。而實例可以是集群中的一個,集群則由一個寫節(jié)點,0個或者多個讀復(fù)制節(jié)點組成。集群中所有的示例,均在同一個地理區(qū)域內(nèi)(例如,us-east-1,us-west-2等)。但是放置在不同的可用區(qū)內(nèi)。連接同一個區(qū)域的存儲機群。為了安全起見,數(shù)據(jù)庫、應(yīng)用、存儲之間的連接都被隔離。實際上,每個數(shù)據(jù)庫實例都可以通過三個亞馬遜虛擬私有云(VPC)進行通信:用戶VPC,用戶應(yīng)用程序可以通過它與數(shù)據(jù)庫實例進行交互。RDS VPC,通過它數(shù)據(jù)庫節(jié)點之間,數(shù)據(jù)庫與控制平臺之間進行交互。存儲VPC,用以進行數(shù)據(jù)庫與存儲之間的交互。
存儲服務(wù)部署在EC2虛擬機集群中。這些虛擬機在一個區(qū)域內(nèi)至少跨三個可用區(qū),共同負責(zé)多用戶提供存儲卷、以及這些卷的讀寫、備份、恢復(fù)。存儲節(jié)點負責(zé)操作本地SSD,與對等節(jié)點或數(shù)據(jù)庫實例進行交互,以及備份恢復(fù)服務(wù)。備份恢復(fù)服務(wù)持續(xù)的將數(shù)據(jù)的改變備份到S3,并且依據(jù)需求從S3恢復(fù)。存儲控制平臺使用亞馬遜DynamoDB服務(wù)存儲集群的永久數(shù)據(jù)、存儲卷配置、卷的元數(shù)據(jù)、備份到S3的數(shù)據(jù)的描述信息。為了協(xié)調(diào)長時間的操作,如數(shù)據(jù)庫卷恢復(fù)操作,修復(fù)(重新復(fù)制)存儲節(jié)點失效等,存儲控制平臺使用亞馬遜簡單工作流服務(wù)(Amazon Simple Workflow Service)。維持高可用性,需要在影響用戶之前,主動地、自動地、盡早地檢測出真正的或潛在的問題。存儲操作各個關(guān)鍵的方面都要通過metric收集服務(wù)進行持續(xù)監(jiān)控,metric會在關(guān)鍵性能、可用性參數(shù)值指示出現(xiàn)異常進行報警。
06 性能結(jié)果
在本段中,我們分享了自2015五月Aurora達到“GA”(基本可用)之后的運營經(jīng)驗。我們給出工業(yè)標(biāo)準(zhǔn)的基準(zhǔn)測試結(jié)果,以及從我們客戶獲得的性能結(jié)果。
標(biāo)準(zhǔn)基準(zhǔn)測試結(jié)果
這里展示了在不同的試驗下,Aurora與mysql的性能對比結(jié)果。實驗使用的是工業(yè)標(biāo)準(zhǔn)的基準(zhǔn)測試,如sysbench和TPC-C的變種。我們運行的mysql實例,附加的存儲是有30K IPOS的EBS存儲卷,除非另有說明,這些mysql運行在有32 vCPU和244G內(nèi)存的r3.8xlarge EC2實例中,配備了Intel Xeon E5-2670 v2 (Ivy Bridge) 處理器,r3.8xlarge的緩存緩沖設(shè)置到170GB。
07 學(xué)到的經(jīng)驗
我們可以看到客戶運行的大量的各種應(yīng)用,客戶有小型的互聯(lián)網(wǎng)公司,也有運行大量Aurora集群的復(fù)雜組織。然而許多應(yīng)用的都是標(biāo)準(zhǔn)的用例,我們關(guān)注于這些云服務(wù)應(yīng)用中共同的場景和期望,以引領(lǐng)我們走向新方向。
7.1 多租戶與數(shù)據(jù)庫整合
我們許多客戶運營軟件即服務(wù)(SaaS)的業(yè)務(wù),有專有云業(yè)務(wù)客戶,也有將企業(yè)部署的遺留系統(tǒng)轉(zhuǎn)向SaaS的客戶。我們發(fā)現(xiàn)這些客戶往往依賴于一種不能輕易修改應(yīng)用。因此,他們通常把他們自己的不同用戶通過一個租戶一個庫或模式的方式整合到一個單實例中。這種風(fēng)格可以減少成本:他們避免為每個用戶購買一個專有實例,因為他們的用戶不可能同時活躍。舉個例子,有些SaaS的客戶說他們有50,000多用戶。
這種模式與我們眾所周知的多租戶應(yīng)用如Saleforce.com不同,saleFore.com采用的多租戶模式是,所有用戶都在同一模式統(tǒng)一的表中,在行級記錄中標(biāo)記租戶。結(jié)果是,我們看到進行數(shù)據(jù)庫整合的用戶擁有巨量的表。生產(chǎn)實例中擁有超過150,000個表的小數(shù)據(jù)庫非常的普遍。這就給管理如字典緩存等元數(shù)據(jù)的組件帶來了壓力。這些用戶需要(a)維持高水平的吞吐量以及并發(fā)的多用戶連接,(b)用多少數(shù)據(jù)就購買提供多少的模式,很難進一步預(yù)測使用多少存儲空間,(c)減少抖動,最小化單個用戶負載尖峰對其他租戶的影響。Aurora支持這些屬性,對這些SaaS的應(yīng)用適配得很好。
7.2 高并發(fā)自動伸縮負載
互聯(lián)網(wǎng)負載需要處理因為突發(fā)事件導(dǎo)致的尖峰流量。我們的主要客戶之一,在一次高度受歡迎的全國性電視節(jié)目中出現(xiàn)特殊狀況,經(jīng)歷了遠高于正常吞吐量峰值的尖峰,但是并沒有給數(shù)據(jù)庫帶來壓力。要支持這樣的尖峰,數(shù)據(jù)庫處理大量并發(fā)用戶連接顯得十分的重要。這在Aurora是可行的,因為下層存儲系統(tǒng)伸縮性如此之好。我們有些用戶運行時都達到了每秒8000個連接。
7.3 模式升級
現(xiàn)代web應(yīng)用框架,如Ruby on Rails,都整合了ORM(object-relational mapping, 對象-關(guān)系映射)。導(dǎo)致對于應(yīng)用開發(fā)人員來說,數(shù)據(jù)庫模式改變非常容易,但是對DBA來說,如何升級數(shù)據(jù)庫模式就成為一種挑戰(zhàn)。在Rail應(yīng)用中,我們有關(guān)于DBA的第一手資料:“數(shù)據(jù)庫遷移“,對于DBA來說是"一周做大把的遷移“,或者是采用一些回避策略,用以保證將來遷移的不那么痛苦。而Mysql提供了自由的模式升級語義,大部分的修改的實現(xiàn)方式是全表復(fù)制。頻繁的DDL操作是一個務(wù)實的現(xiàn)實,我們實現(xiàn)了一種高效的在線DDL,使用(a)基于每頁的數(shù)據(jù)庫版本化模式,使用模式歷史信息,按需對單獨頁進行解碼。(b)使用寫時修改(modify-on-write)原語,實現(xiàn)對單頁的最新模式懶更新。
7.4 可用性與軟件升級
我們的客戶對云原生數(shù)據(jù)庫如何解決運行機群和對服務(wù)器的補丁升級的矛盾,有著強烈的期望??蛻舫钟心撤N應(yīng)用,而這些應(yīng)用有主要使用Aurora作為OLTP服務(wù)支持。對這些客戶來說,任何中斷都是痛苦的。所以,我們許多客戶對數(shù)據(jù)庫軟件的更新的容忍度很低,哪怕是相當(dāng)于6周30秒的停機時間。因此,我們最近發(fā)布了零停機補?。▃ero downtime patch)特性。這個特性允許給用戶補丁但是不影響運行的數(shù)據(jù)庫連接。
如圖5所示,ZDP通過尋找沒有活動事務(wù)的瞬間,在這瞬間將應(yīng)用假脫機到本地臨時存儲,給數(shù)據(jù)庫引擎打補丁然后更新應(yīng)用狀態(tài)。在這個過程中,用戶會話仍然是活動的,并不知道數(shù)據(jù)庫引擎已經(jīng)發(fā)生了改變。
08 相關(guān)工作
在本段中,我們討論其他人的貢獻,這些貢獻與Aurora采用的方法相關(guān)。
存儲與引擎解耦雖然傳統(tǒng)的數(shù)據(jù)都被設(shè)計成單內(nèi)核的守護進程。但是仍有相關(guān)的工作,將數(shù)據(jù)庫內(nèi)核解耦成不同的組成部分,例如,Deuteronomy就是這樣的系統(tǒng),將系統(tǒng)分為事務(wù)組件(Tranascation Component, TC)和數(shù)據(jù)組件(Data Componet ,DC).事務(wù)組件提供并發(fā)控制功能已經(jīng)從DC中做崩潰恢復(fù)的功能,DC提供了一個LLAMA之上的訪問方法。LLAMA是一個無閂鎖的基于日志結(jié)構(gòu)的緩存和存儲管理系統(tǒng)。Sinfonia 和 Hyder則從可拓展的服務(wù)抽出了事務(wù)性訪問方法,實現(xiàn)了可以抽象使用這些方法的數(shù)據(jù)庫系統(tǒng)。Yesquel系統(tǒng)實現(xiàn)了一個多版本分布式平衡樹,并且把將并發(fā)控制從查詢處理中獨立出來。Aurora解耦存儲比Deuteronomy、Hyder、Sinfonia更為底層。在Aurora中,查詢處理,事務(wù),并發(fā),緩存緩沖以及訪問方法都和日志存儲解耦,并且崩潰恢復(fù)作為一個可拓展的服務(wù)實現(xiàn)的。
分布式系統(tǒng) 在分區(qū)面前,正確性與可用性的權(quán)衡早已為人所知,在網(wǎng)絡(luò)分區(qū)去情況下,一個副本的可串行化是不可能的,最近Brewer的CAP理論已經(jīng)證明了在高可用系統(tǒng)在網(wǎng)絡(luò)分區(qū)的情況下,不可能提供“強”一致性。這些結(jié)果以及我們在云級規(guī)模的經(jīng)驗,激發(fā)了我們的一致性目標(biāo),即使在AZ失效引起分區(qū)的情況下也要一致性。
Brailis等人研究了高可用事務(wù)系統(tǒng)(Highly Available Transactions, HATs)的問題。他們已經(jīng)證明分區(qū)或者高網(wǎng)絡(luò)延遲,并不會導(dǎo)致不可用性。可串行化、snapshot隔離級、可重復(fù)讀隔離級與HAT不兼容。然而其他的隔離級可以達到高可用性。Aurora提供了所有的隔離級,它給了一個簡單的假設(shè),即任何時候都只有一個寫節(jié)點產(chǎn)生日志,日志更新的LSN,是在單個有序域中分配的。
Google的Spanner提供了讀和寫的外部一致性,并且提供了跨數(shù)據(jù)在同一個時間戳上全球一致性讀。這些特性使得spanner可以一致性備份,一致性分布式查詢處理以及原子模式更新,這些特性都是全球規(guī)模的,哪怕是正在運行的事務(wù)的時候,也支持。正如Bailis解釋的一樣,Spanner是為google的重-讀(read-heavy)負載高度定制的。采用兩階段提交,讀/寫事務(wù)采用兩階段鎖。
并發(fā)控制弱一致性與弱隔離級模型在分布式數(shù)據(jù)庫中已為人所知。由此產(chǎn)生了樂觀復(fù)制技術(shù)以及最終一致性系統(tǒng)在中心化的系統(tǒng)中,采用的其他的方法,有基于鎖的悲觀模式(),采用如多版本并發(fā)控制樂觀模式,如Hekaton,如VoltDB采用分片方法,Deuteronomy和Hyper采用時間戳排序的方法。而Aurora給數(shù)據(jù)庫系統(tǒng)提供了一個持久存在的抽象的本地磁盤,允許引擎自己處理隔離級與并發(fā)控制。
日志結(jié)構(gòu)存儲1992年LFS就引入了日志結(jié)構(gòu)存儲系統(tǒng)。最近Deuteronomy和LLMA的相關(guān)工作,以及Bw-tree以多種方式在存儲引擎棧中采用日志結(jié)構(gòu)技術(shù),和Aurora一樣,通過寫入差量而不是寫整頁減少寫放大。Aurora與Deuteronomy都實現(xiàn)了純重做日志系統(tǒng),并且跟蹤高持久化的LSN,用以確認提交。
崩潰恢復(fù) 傳統(tǒng)數(shù)據(jù)庫依賴于ARIES的恢復(fù)協(xié)議,現(xiàn)在有些數(shù)據(jù)庫為了性能而采用了其他途徑。例如,Hekaton與VoltDB采用某種形式的更新日志來重建崩潰后的內(nèi)存狀態(tài)。像Sinfonia系統(tǒng)采用如進程對以及復(fù)制狀態(tài)機的技術(shù)來避免崩潰恢復(fù)。Graefe描述一種使用每頁日志記錄鏈的系統(tǒng),可以按需逐頁重做??梢蕴岣弑罎⒒謴?fù)的速度。Aurora與Deuteronomy不需要重做崩潰恢復(fù)的過程。這是因為Deuteronomy會將事務(wù)延遲,以便只有提交的事務(wù)的修改才會寫入到持久存儲中。因而,與Aurora不同,Deuteronomy能處理的事務(wù)大小必然受到限制。
09 結(jié)論
我們設(shè)計出了高吞吐量的OLTP數(shù)據(jù)庫Aurora,在云級規(guī)模環(huán)境下,并沒有損失其可用性與持久性。主要思想是,沒有采用傳統(tǒng)數(shù)據(jù)庫的單核架構(gòu),而是從計算節(jié)點中把存儲解耦出來。實際上,我們只從數(shù)據(jù)庫內(nèi)核中移走了低于1/4的部分到獨立的彈性伸縮的分布式服務(wù)中,用以管理日志與存儲。由于所有的IO寫都要通過網(wǎng)絡(luò),現(xiàn)在我們的基本的約束就在于網(wǎng)絡(luò)。所以我們關(guān)注于能緩解網(wǎng)絡(luò)壓力并能提高吞吐量的技術(shù)。我們使用仲裁模型來處理大規(guī)模的云環(huán)境下的相關(guān)的復(fù)雜失效。并且避免了特殊節(jié)點帶來的性能懲罰。使用日志處理減少總的IO負擔(dān)。采用異步共識方法消除多階段提交同步協(xié)議的繁復(fù)交互與高昂的代價,以及離線的崩潰恢復(fù),和分布式存儲的檢查點。我們的方法導(dǎo)致了簡化系統(tǒng)架構(gòu),能降低復(fù)雜性,易于伸縮能為未來的發(fā)展打下基礎(chǔ)。
文章個人解讀
圖1中所示的Data Plane中,數(shù)據(jù)庫引擎那個框包住了Caching,而存儲服務(wù)那個框,也包住了Caching。耐人尋味??梢圆聹y,Cache是共享內(nèi)存,數(shù)據(jù)庫引擎和存儲服務(wù)都可以訪問。而且Cache不光是數(shù)據(jù)庫引擎的一部分,而且也是存儲服務(wù)的一部分。全文大部分篇幅講如何優(yōu)化寫請求帶來的網(wǎng)絡(luò)負擔(dān),而只字未提讀請求是否帶來網(wǎng)絡(luò)負擔(dān)。因此,可以大膽猜測,一次寫操作,不光是把日志應(yīng)用到磁盤存儲中,同時也把已經(jīng)在cache的頁也應(yīng)用了,因此大部分的讀請求不需要真實的存儲IO,除非該頁在cache中沒有命中。從段6的Aurora測試可以看到,Aurora實例擁有的緩存相當(dāng)大,一般的應(yīng)用軟件的數(shù)據(jù)庫數(shù)據(jù)都能放在內(nèi)存中。從圖3也可以看到,AZ1主實例,也會向AZ2,AZ3數(shù)據(jù)庫從實例發(fā)送日志與元數(shù)據(jù),既然已經(jīng)將日志處理從數(shù)據(jù)庫引擎解耦出來了,發(fā)送的日志由誰應(yīng)用呢?個人認為,AZ2與AZ3接收到日志,仍然是有存儲服務(wù)應(yīng)用的,只不過Cache是存儲服務(wù)與數(shù)據(jù)庫引擎共享的。
而且有這個猜測,我們進一步解讀零停機補丁的實現(xiàn)方式。ZDP如何保持用戶連接實現(xiàn)假脫機?個人認為,應(yīng)用到數(shù)據(jù)庫之間有proxy。使用proxy來保持應(yīng)用程序的連接,并重新與更新后數(shù)據(jù)庫實例建立連接,而且要做到用戶會話無感覺,Cache是共享內(nèi)存是必要的。因為Cache保存了大部分數(shù)據(jù)庫運行狀態(tài),重啟后的數(shù)據(jù)庫實例仍然繼續(xù)進行用戶會話的查詢。這個和使用共享內(nèi)存的postgre有點像,你隨意殺死postgre某些進程,并不影響用戶會話的查詢。
此外,個人認為,Aurora的計算節(jié)點,有可能也是存儲節(jié)點。雖然邏輯上是存儲與計算分離的,但是也可以享受不分離帶來的好處,比如,存儲服務(wù)可以直接往Cache應(yīng)用日志,使得數(shù)據(jù)庫引擎在大部分讀請求不需要真實的IO。
Aurora有沒有checkpoint?
Aurora的PGMRPL可以認為是檢查點。LSN小于這個點的數(shù)據(jù)頁已經(jīng)刷盤,而大于這個點的頁可以刷盤,也可以不刷盤,LSN小于這個點的日志都可以丟棄。PGMRPL在邏輯上與存儲引擎的檢查點是等效的??梢哉J為是PG上的檢查點。
Aurora與polardb
Aurora與Polardb都是存儲與計算分離、一些多讀、共享分布式存儲的架構(gòu)。很顯然,polardb在寫放大上面沒有做優(yōu)化。從節(jié)點需要同步主庫的臟頁,共享的存儲,私有的臟頁,是個很難解決的矛盾。因此polardb的讀節(jié)點會影響寫節(jié)點。而Aurora可以認為沒有臟頁。日志即數(shù)據(jù)庫,可以把日志應(yīng)用器看作更慢的存儲,存儲服務(wù)與緩存都可以認為是日志應(yīng)用器的Cache。
從節(jié)點與主節(jié)點之間有延遲,而aurora存儲的數(shù)據(jù)頁有多版本,文中明確指出存儲有舊頁回收處理。從節(jié)點依據(jù)讀取點LSN讀取到指定版本的頁,文中段4.2.4指出,寫節(jié)點到讀復(fù)制節(jié)點之間的延遲小于20ms,因此,不可回收的舊版本頁應(yīng)該不會太多。
臨時表
每個讀節(jié)點雖然只有查詢操作,但是查詢操作會生成臨時表用以保存查詢的中間結(jié)果。生成臨時表數(shù)據(jù)是不會產(chǎn)生日志的。但是這里仍有寫IO,個人認為,Aurora有可能直接寫在本地存儲,這樣不會產(chǎn)生網(wǎng)絡(luò)上的負擔(dān)。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。