開發分布式SQL數據庫的6種技術挑戰
我們在今年2月跨越了YugaByte DB三年開發階段,到目前為止,這是一段驚心動魄的旅程,但并非沒有公平的技術挑戰。有時我們不得不回到繪圖板,甚至篩選學術研究,以找到比我們手頭的更好的解決方案,在這篇文章中,我們將概述在構建開源,云原生,高性能分布式SQL數據庫的過程中我們必須解決的一些最難的架構問題。
好的,讓我們開始探討從最簡單到***挑戰性的問題:
1.架構:亞馬遜Aurora還是谷歌Spanner?
我們早期做出的一個決定是找到一個我們可以用作YugaByte DB架構靈感的數據庫。我們密切關注兩個系統,Amazon Aurora和Google Spanner。
Amazon Aurora是一個提供高可用性的SQL數據庫。它具有與流行的RDBMS數據庫(如MySQL和PostgreSQL)的兼容性,使其易于入門并可運行各種應用程序。Amazon Aurora也是AWS歷史上發展最快的服務之一。
Amazon Aurora服務與MySQL和PostgreSQL兼容,是AWS歷史上發展最快的服務。
Amazon Aurora具有可擴展的數據存儲層,但查詢層不是這樣。以下是我們發現的Amazon Aurora的一些關鍵可擴展性限制:
- 寫入不是水平可伸縮的。擴展寫入吞吐量的唯一方法是垂直擴展處理所有寫入的節點(稱為主節點)。這種擴展方案只是到目前為止,因此數據庫能處理多少寫入IOPS存在固有的限制。
- 寫入不是全局一致的。許多現代的云原生應用程序本質上是全局性的,需要跨多個區域部署底層數據庫。但是,Aurora僅支持多主機部署,在發生沖突時***一個寫入程序(具有***時間戳)獲勝。這可能導致不一致。
- 通過使用犧牲一致性的從屬副本以獲得讀取的伸縮擴展。為了擴展讀取,應用程序需要連接到從屬節點才能實現讀取。當使用這些從屬節點實現讀取時,應用程序需要面對降級的一致性語義,以及一個單獨的連接端點。這使得應用程序架構非常復雜。
另外,Google Spanner是一個可水平擴展的SQL數據庫,專為大規模可擴展和地理分布式應用程序而構建。
Cloud Spanner是唯一為云構建的企業級、全局分布且高度一致的數據庫服務,專門用于將關系數據庫結構的優勢與非關系水平擴展相結合。
這意味著Spanner可以無縫擴展讀寫,支持需要全局一致性的地理分布式應用程序,并在不犧牲正確性的情況下從多個節點執行讀取。
但是,它放棄了RDBMS數據庫提供給開發人員期望的許多熟悉功能集。例如,Google Spanner文檔中突出顯示了不支持外鍵約束或觸發器的事實。
我們決定采用混合方法。
- YugaByte DB的核心存儲架構受到Google Spanner的啟發,該架構專為水平可擴展性和地理分布式應用程序而構建。
- YugaByte DB保留了與Amazon Aurora類似的PostgreSQL兼容查詢層,它可以支持豐富的功能集,并支持最廣泛的用例。
2. SQL協議:PostgreSQL還是MySQL?
我們想要對廣泛采用的SQL方言進行標準化。我們還希望它是開源的,并且在數據庫周圍擁有成熟的生態系統。權衡的自然選擇是PostgreSQL和MySQL?
我們之所以選擇PostgreSQL(而不是MySQL),原因如下:
- PostgreSQL有一個更寬松的許可證,更符合YugaByte DB的開源精神。
- 與任何其他SQL數據庫相比,PostgreSQL在過去幾年中的流行度一直在飆升,這絕對沒有受到影響!
在目前排在DB-Engines排名網站前10位的五個SQL數據庫中,自2014年以來,只有PostgreSQL的受歡迎程度越來越高,而其他數據庫則趨于平穩或正在失去理智。
此外,對于許多應用程序,PostgreSQL是Oracle的***替代品。組織正在被PostgreSQL所吸引,因為它是開源的,供應商中立(MySQL由Oracle擁有),擁有一個參與的開發者社區,一個繁榮的供應商生態系統,一個強大的功能集,以及一個成熟的代碼庫,一直在戰斗 - 經過20多年的嚴格使用而堅固。
3.分布式事務:Google Spanner或Percolator?
關于我們應該如何設計分布式事務,我們查看了Google Spanner和Percolator。
總而言之,Google Percolator提供高吞吐量但使用單個時間戳。這種方法本質上是不可擴展的,僅適用于單個數據中心,面向實時分析(稱為HTAP)的應用程序,而不是OLTP應用程序。另一方面,Google Spanner的分散時間跟蹤方法對于地理分布式OLTP和單數據中心HTAP應用程序來說都是一個很好的解決方案。
Google Spanner是在Google Percolator之后構建的,用于替換廣告后端中手動分片的MySQL部署,以實現水平可擴展性和地理分布式用例。但是,考慮到其真正的分布式特性以及對時鐘偏移跟蹤的需求,Google Spanner的構建難度要高一個數量級。
有關此主題的更多詳細信息,您可以詳細了解Percolator與Spanner的權衡。
我們決定采用Google Spanner方法,因為它可以支持:
- 更好的水平可擴展性
- 高度可用且性能更佳的多區域部署。
我們堅信,大多數現代云應用都需要上述兩種功能。實際上,GDPR和總共提供100個地區的公共云等合規性要求已經使這成為現實。
4. Raft是否適用于地理分布式工作負載?
Raft和Paxos是眾所周知的分布式共識算法,并且已被正式證明是安全的,Spanner使用Paxos,但是,我們選擇了Raft,因為:
- 對于開發人員和運營團隊Raft比Paxos更容易理解。
- 它提供動態更改成員資格的能力,這是至關重要的(例如:在不影響性能的情況下更改機器類型)。(banq注:Raft與Paxos主要區別在于Raft候選人可以是任何一個服務器節點,不需要專門指定候選人,否則這些候選人全部宕機怎么辦?如同一些TCC分布式事務中存在事務協調器一樣有單點風險)
然而,為了確保可線性化的讀取,Raft要求接收讀取查詢的每個***在實際提供讀取查詢之前首先將心跳消息傳播到Raft組中的大多數節點。在某些情況下,這可能會嚴重降低讀取性能。這種情況的一個示例是地理分布式部署,其中往返會顯著增加延遲,并且在諸如臨時網絡分區之類的事件的情況下增加失敗查詢的數量。
為了避免Raft高延遲,我們實施了***的租賃機制,這將允許我們無需往返實現***服務,同時保留了Raft的線性化特性。此外,我們使用單調時鐘而不是實時時鐘,以容忍時鐘偏差。
5.我們可以構建軟件定義的原子鐘嗎?
作為分布式數據庫,YugaByte DB支持跨多個節點的多鍵ACID事務(快照和可序列化隔離級別),即使存在故障也是如此。這需要一個可以跨節點同步時間的時鐘。
Google Spanner使用TrueTime,這是一個具有嚴格錯誤界限的高可用性全局同步時鐘的示例。但是,許多部署中都沒有此類時鐘。
物理時鐘(或掛鐘)不能在節點之間***同步。因此,他們無法跨節點排序事件(建立因果關系)。除非存在中央時間戳權限,否則諸如Lamport時鐘和向量時鐘之類的邏輯時鐘不會跟蹤物理時間,這成為可擴展性瓶頸。
我們的方案: 混合邏輯時鐘(HLC)通過將使用NTP粗略同步的物理時鐘與跟蹤因果關系的Lamport時鐘相結合來解決該問題。
YugaByte DB使用HLC作為高可用性群集寬時鐘,具有用戶指定的***時鐘偏差上限值。HLC值在Raft組中用作關聯更新的方式,也用作MVCC讀取點。結果是符合ACID的分布式數據庫,如Jepsen測試所示。
6.重寫或重用PostgreSQL查詢層?
***但同樣重要的是,我們需要決定是否重寫或重用PostgreSQL查詢層。
我們的初步決定
YugaByte數據庫查詢層在設計時考慮了可擴展性。通過在C ++中重寫API服務器,已經在這個查詢層框架中構建了兩個API(YCQL和YEDIS),首先重寫PostgreSQL API似乎更容易和自然。
我們的最終決定
在我們意識到這不是一條理想的道路之前,我們沿著這條路走了大約5個月。與PostgreSQL成熟,完整的數據庫相比,其他API要簡單得多。然后我們重新完成整個工作,回到繪圖板并重新開始重新使用PostgreSQL的查詢層代碼。雖然這在開始時很痛苦,但回顧起來它是一個更好的策略。
這種方法也有其自身的挑戰。我們的計劃是首先將PostgreSQL系統表移動到DocDB(YugaByte DB的存儲層),最初支持一些數據類型和一些簡單查詢,并隨著時間的推移添加更多數據類型和查詢支持。
不幸的是,這個計劃并沒有完全解決。要從psql執行看似簡單的最終用戶命令,實際上需要支持大量SQL功能。例如,\d用于列出所有表的命令在內部執行以下查詢:
- c.relname as "Name",
- CASE c.relkind
- WHEN 'r' THEN 'table'
- WHEN 'v' THEN 'view'
- WHEN 'm' THEN 'materialized view'
- WHEN 'i' THEN 'index'
- WHEN 'S' THEN 'sequence'
- WHEN 's' THEN 'special'
- WHEN 'f' THEN 'foreign table'
- END as "Type",
- pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
- FROM pg_catalog.pg_class c
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind IN ('r','')
- AND n.nspname <> 'pg_catalog'
- AND n.nspname <> 'information_schema'
- AND n.nspname !~ '^pg_toast'
- AND pg_catalog.pg_table_is_visible(c.oid)
- ORDER BY 1,2;
WHERE支持操作符,例如IN,不等于,正則表達式匹配等。滿足上述查詢需要支持以下功能:
- CASE 條款
- 加入,特別是 LEFT JOIN
- ORDER BY
- 內建等 pg_table_is_visible()
顯然,這代表了各種各樣的SQL功能,因此我們必須在創建單個用戶表之前使所有這些功能都可用!我們在Google Spanner架構上發布分布式PostgreSQL - 查詢層突出顯示了查詢層的詳細工作方式。
結論
即使對于專家用戶來說,不得不在市場上可用的許多數據庫之間進行選擇,一開始看起來似乎勢不可擋。這是因為為給定類型的應用程序選擇數據庫取決于這些數據庫在其體系結構中所做的權衡。
通過YugaByte DB,我們以一種新穎的方式組合了一組非常實用的架構決策,以創建一個獨特的開源分布式SQL數據庫。PostgreSQL強大的SQL功能現在可供您使用,零數據丟失,水平寫入可擴展性,低讀取延遲以及在公共云或Kubernetes中本機運行的能力。