真正硬核分散式資料庫:開發分散式SQL資料庫的6種技術挑戰 - YugaByte

banq發表於2019-04-27

我們在今年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中本機執行的能力。

相關文章