Amazon Aurora 是如何設計原生雲關係型資料庫的?
關係型資料庫已經存在很長時間了。資料的關係模型是 E.F. Codd 在 20 世紀 70 年代提出的,而支撐當今主要關係型資料庫管理系統的核心技術是在 1980 到 1990 年代開發的。關係型資料庫的基礎,包括資料關係、ACID(原子性、一致性、隔離性、永續性)事務和 SQL 查詢語言,都經受住了時間的考驗。這些基本特性使關係型資料庫備受全世界的使用者歡迎。它們仍然是許多公司 IT 基礎設施的基石。
當然,這並不是說系統管理員一定喜歡處理關係型資料庫。幾十年來,管理關係型資料庫一直是一項高技能、勞動密集型任務,是一項需要專用系統以及資料庫管理員全神貫注的任務。在保證容錯性、效能和爆炸半徑(故障的影響)的同時,擴充套件關係型資料庫一直是管理員面臨的一項永續性挑戰。
與此同時,現代網際網路工作負載的要求越來越高,需要基礎設施具備以下幾個基本特性:
- 使用者希望最開始能夠是很小的記憶體佔用,然後在不受基礎設施限制的情況下大規模擴充套件。
- 在大型系統中,失敗是一種常態,而不是例外。客戶工作負載必須與元件故障或系統故障隔離。
- 爆炸半徑小,沒有人希望單個系統故障對他們的業務產生很大影響。
這都是些困難的問題,解決這些問題需要擺脫老式的關係型資料庫體系結構。當 Amazon 面臨 Oracle 等老式關係型資料庫的限制時,我們建立了一個現代關係型資料庫服務 Amazon Aurora。
Aurora 的設計保留了關係型資料庫的核心“事務一致性”這個優勢,在儲存層進行了創新,建立了一個為雲構建的資料庫,可以在不犧牲效能的情況下支援現代工作負載。這也是客戶喜歡的一點,因為 Aurora 以十分之一的成本提供了商用資料庫的效能和可用性。自發布以來,它就一直是 AWS 歷史上增長最快的服務。
在這篇文章中,我想簡單介紹下我們是如何構建 Aurora 的,同時還會討論為什麼客戶採用它的速度比 AWS 歷史上任何其他服務都要快。
新概念關係型資料庫
首先,我們先來看一下舊式關係型資料庫的體系結構:
在過去的 30 到 40 年裡,這種整體式關係型資料庫棧沒有發生太大的變化。雖然存在不同的用於擴充套件資料庫的傳統方法(例如分片、無共享或共享磁碟),但它們都共享相同的基本資料庫體系結構。由於緊耦合的整體式棧的基本限制仍然存在,所以在規模較大的情況下,它們沒有解決效能、彈性和爆炸半徑的問題。
為了著手解決關係型資料庫的限制,我們通過將系統分解為基本的構建塊重新定義了棧。我們認識到,快取層和日誌層的創新時機已經成熟。我們可以將這些層移動到一個專門構建的、可擴充套件的、自修復的、多租戶的、資料庫優化的儲存服務中。當我們開始構建這個分散式儲存系統時,Amazon Aurora 就誕生了。
我們挑戰了關係型資料庫中快取和日誌記錄的傳統思想,重新設計了資料庫 I/O 層,並獲得了較大的可伸縮性和彈性優勢。Amazon Aurora 具有顯著的可伸縮性和彈性,因為它支援解除安裝重做日誌、基於單元的體系結構、仲裁和快速資料庫修復。
重做日誌解除安裝:日誌即資料庫
傳統的關係型資料庫以頁的形式組織資料,當對頁進行修改時,必須定期將它們重新整理到磁碟。為了提高故障恢復能力和維持 ACID 語義,頁修改也記錄在重做日誌記錄中,這些日誌記錄會以連續流的形式寫入磁碟。雖然這種體系結構提供了支援關係型資料庫管理系統所需的基本功能,但是效率很低。例如,一個邏輯資料庫寫操作會變成多個(最多五個) 物理磁碟寫操作,從而導致效能問題。
資料庫管理員試圖通過減少頁重新整理的頻率來解決寫放大問題。這反過來又加劇了崩潰恢復時長的問題。重新整理間隔越長,意味著需要從磁碟讀取更多的重做日誌記錄,並應用重做日誌記錄來重構正確的頁映像。這會導致恢復變慢。
在 Amazon Aurora 中,日誌即資料庫。資料庫例項將重做日誌記錄寫入分散式儲存層,儲存層根據需要從日誌記錄構造頁映像。資料庫例項永遠不必重新整理髒頁,因為儲存層總是知道頁是什麼樣子。這改進了資料庫多個方面的效能和可靠性。由於消除了寫入放大並使用了可擴充套件儲存機群,寫入效能大大提高。
例如,與在類似硬體上執行的 Amazon RDS for MySQL 相比,Amazon Aurora MySQL 相容版在 SysBench 基準上展現出了 5 倍的寫 IOPS。資料庫崩潰恢復時間大大縮短,因為資料庫例項不再需要執行重做日誌流重放。儲存層負責在頁讀取時應用重做日誌,從而產生一個不受遺留資料庫體系結構限制的新儲存服務,由此,你可以進行更進一步的創新。
基於單元的體系結構
就像我之前說的那樣,任何東西都總是會出現故障。在大型系統中,元件會出現故障,而且會常常出現。整個例項出現故障。網路故障可以隔離大量的基礎設施。在少數情況下,整個資料中心會因為自然災害而變成孤島或崩潰。在 AWS,我們針對故障進行設計,並依賴基於單元的體系結構在問題發生之前進行處理。
AWS 有多個地理“Region”(20 個並且還在增加),在每個 Region,我們有幾個“可用性區域(Availability Zones)”。利用多個 Region 和 Zone,可以在不影響服務可用性的情況下,使設計良好的服務在普通元件故障和較大的災難中倖存下來。Amazon Aurora 將所有寫複製到三個 Zone,以提供更好的資料永續性和可用性。事實上,Aurora 可以在不丟失資料可用性的情況下容忍整個 Zone 的丟失,並且可以從較大的故障中快速恢復。
然而,眾所周知,複製是資源密集型的過程,那麼是什麼使 Aurora 能夠在提供高效能的同時提供健壯的資料複製呢?答案就在仲裁中。
仲裁之美
任何東西都總是會出現故障。系統越大,某個地方發生故障的可能性就越大:網路連結、SSD、整個例項或軟體元件。即使軟體元件沒有 Bug,它仍然需要定期重啟升級。
傳統的方法是阻塞 I/O 處理,直到可以執行故障轉移(並且在出現故障元件時以“降級模式”執行),這種方法在規模較大時會產生問題。應用程式通常不能很好地容忍 I/O 故障。通過中等複雜的數學計算就可以證明,在大型系統中,隨著系統規模的增長,在降級模式下執行的概率接近 1。然後,還有一個非常隱蔽的問題“灰色故障(gray failures)”。當元件沒有完全失敗,而是變得很慢時,就會發生這種情況。如果系統設計沒有預見到這種降級,那麼低速齒輪會降低整個系統的效能。
Amazon Aurora 使用仲裁來解決元件故障和效能下降的問題。寫仲裁的基本思想很簡單:寫儘可能多的副本,以確保讀仲裁讀總是可以找到最新的資料。最基本的仲裁示例是“2 / 3”:
Vw+Vr > V
Vw > V / 2
V=3
Vw=Vr=2
例如,你可能有三個物理寫操作要執行,而寫仲裁為 2。在邏輯寫操作被宣佈成功之前,你不必等待這三個操作都完成。如果一個寫操作失敗或者很慢,也沒關係,因為總體操作結果和延遲不會受到異常值的影響。這很重要:即使有的東西壞了,也可以寫得又快又成功。
簡單的 2/3 仲裁將使你可以容忍整個可用性區域的丟失。但這還不夠好。雖然區域丟失是一個罕見的事件,但它並不會降低其他區域的元件失敗的可能性。使用 Aurora,我們的目標是可用性區域 +1:我們希望能夠容忍一個區域的丟失外加一個失敗,而不會造成任何資料永續性損失,並且對資料可用性的影響最小。我們使用 4/6 仲裁來達到這個目的:
Vw+Vr > V
Vw > V / 2
V=6
Vw=4
Vr=3
對於每個邏輯日誌寫,我們發出 6 個物理副本寫,並認為當其中 4 個寫完成時,寫操作就成功了。每個區域使用兩個副本,如果整個可用性區域出現故障,仍然需要能夠完成寫操作。如果某個區域宕機併發生其他故障,你仍然可以實現讀仲裁,然後通過快速修復快速恢復寫能力。
快速恢復並加緊彌補
資料複製有不同的做法。在傳統的儲存系統中,資料映象或擦除編碼發生在整個物理儲存單元的級別,多個單元組合成一個 RAID 陣列。這種方法使修復變得緩慢。RAID 陣列重建效能受陣列中少量裝置的能力限制。隨著儲存裝置變大,重建期間需要複製的資料量也會變大。
Amazon Aurora 使用完全不同的複製方法,基於分片和可擴充套件架構。Aurora 資料庫卷邏輯上分為大小為 10-GiB 的邏輯單元(保護組),每個保護組通過六路複製到物理單元(段)中。單個儲存段分佈在一個大型分散式儲存機群中。當故障發生並取出一個段時,單個保護組的修復只需要移動大約 10 GiB 的資料,這在幾秒內就可以完成。
此外,當必須對多個保護組進行修復時,整個儲存機群將參與修復過程。這提供了巨大的頻寬,從而可以完成整個批次的快速修復。因此,如果一個區域丟失之後又出現另一個元件故障,對於給定的保護組,Aurora 可能會丟失幾秒鐘的寫仲裁。然而,一個自啟動的修復其後會以極快的速度恢復可寫性。換句話說,Aurora 儲存器能迅速自我修復。
如何能夠對資料進行六路複製並保持高效能的寫操作?這在傳統的資料庫體系結構中是不可能的,因為在傳統的資料庫體系結構中,全部頁或磁碟扇區都會被寫到儲存中,網路將被淹沒。相比之下,在 Aurora 中,例項只將重做日誌記錄寫入儲存器。這些記錄要小得多(通常是幾十到幾百個位元組),這使 4/6 寫仲裁成為可能,而且不會使網路過載。
寫仲裁的基本思想意味著某些段可能不會總是在一開始就接收所有的寫。這些段如何處理重做日誌流中的缺口?Aurora 儲存節點之間會不斷地“傳播”以填補漏洞(並執行修復)。日誌流的處理過程是通過日誌序列號(LSN)管理緊密協調的。我們使用一組 LSN 標記來維護每個單獨段的狀態。
讀取呢?讀仲裁的代價很高,最好避免。客戶端 Aurora 儲存驅動程式會跟蹤哪些寫入成功了。它不需要在常規頁讀取上執行仲裁讀,因為它總是知道從哪裡獲取頁的最新副本。此外,驅動程式跟蹤讀取延遲,並始終嘗試從過去已經證明的延遲最低的儲存節點讀取。惟一需要讀仲裁的場景是在資料庫例項重新啟動期間的恢復過程。LSN 標記的初始集必須通過詢問儲存節點來重構。
創新基礎
分散式、自修復的儲存體系結構為許多引人注目的 Aurora 新特性提供了直接支援。舉幾個例子:
- 讀可擴充套件性:除了主資料庫例項之外,還可以跨 Aurora 中的多個區域提供多達 15 個讀副本,以實現讀可伸縮性和更高的可用性。讀副本使用與主伺服器相同的共享儲存卷。
- 連續備份和時間點還原:Aurora 儲存層連續透明地將重做日誌流備份到 Amazon S3。你可以在配置好的備份視窗中對任何時間戳執行時間點還原。不需要對快照建立進行排程,當快照距離我們感興趣的時間點較遠時,不會丟失任何事務。
- 快速克隆:Aurora 儲存層可以快速建立卷的物理副本,而不需要複製所有頁。頁最初是在父卷和子卷之間共享的,當頁被修改時,就完成了寫時複製。在克隆卷時沒有重複開銷。
- 回溯:一種使資料庫及時回到特定位置的快速方法,不需要從備份中進行完全恢復。不小心丟了一張表?你可以用 Aurora 回溯讓時間倒流。
在 Aurora 儲存引擎的基礎上,還有很多關係型資料庫的創新。我們都進入了關係型資料庫的新時代,Aurora 只是一個開始。另外,也得到了客戶的認可,Capital One、道瓊斯、Netflix 和 verizon 等所有行業的領導者都將關係型資料庫的工作負載遷移到 Aurora。
檢視英文原文:Amazon Aurora ascendant: How we designed a cloud-native relational database