AlloyDB for PostgreSQL under the hood: Intelligent, database-aware storage

語言: CN / TW / HK

背景

存算分離成為大勢所趨。作者在博文中提到即使是存算分離也是有其發展階段,見上圖。當然,Google標榜自己是存算分離的集大成者,我感覺這是往自己臉上貼比特幣。老實說,我也沒看出來他的存算分離NB在哪裡(當然,可能下面的GFS確實厲害一點,但本質沒有不同),關於博文這段我沒有看懂。

作者也提到了傳統資料庫大一統設計的弊端,這個比較老生常談,也不需要在這裡多費筆墨。

架構

AlloyDB包含如下幾個元件(服務):

  1. 計算層:這是做過一定的改造適配的PostgreSQL,傳統PG的SQL解析、優化、儲存格式等基本不變,主要修改了WAL以及Page的讀寫,同時取消了刷髒等流程
  2. LSS:Log Storage Service:一個低延遲、高效能的WAL儲存服務,計算層將WAL儲存從本地磁碟轉為Log Storage Service,解決計算和儲存耦合問題
  3. LPS:Log processing service。根據WAL生成資料Page的後臺服務,它順序讀取LSS中的WAL,讀取Page,將WAL apply至page並寫入BSS。LPS無狀態,因而很容易實現彈效能力
  4. BSS:Block storage service。用於持久化儲存資料庫的page。BSS具備跨AZ容災能力(3AZ)。同時,為計算層提供page讀取服務(如果計算層出現page cache miss)

AlloyDB的架構具備如下優勢

  1. Full compute/storage disaggregation:完整的儲存計算分離能力。WAL以及Page都被下沉到專門的儲存服務,上層的DB不再受限於本地儲存,可以快速彈性,這也是當前的雲原生資料庫的普遍優勢。另外,在AlloyDB中,即使在儲存層,也進行了一定程度的存算分離,LPS服務無狀態,因而可以快速彈性
  2. 高效能:在AlloyDB中,計算層(DB)只需要將WAL寫入儲存(一般量比較小),避免了page刷髒邏輯,這避免了刷髒帶來的,節約網路頻寬,page的生成在儲存層內部解決(由於LPS的彈效能力),page的生成相比DB更快(將部分重資源消耗操作offload,避免對使用者請求產生影響)。這也是
  3. 彈效能力:AlloyDB可以快速增加/移除計算節點(DB)。得益於儲存和計算分離,新增DB計算節點無需傳統流程中的資料複製,因而可以做到及其快速
  4. 快速恢復能力:傳統資料庫在恢復時需要在計算層做WAL回放(讀取page、讀取WAL,apply WAL),恢復時間取決於需要回放的WAL數量,而在AlloyDB中,由於回放工作被offload至儲存層,而儲存層有可靠性保障(回放邏輯簡單,其實LPS不可用的概率相對來說就低很多),這就解放了DB層無需在重啟時進行回放,這也是採用存算分離架構的雲原生資料庫一個顯著優勢

設計細節

寫流程

引入了儲存計算分離後,AlloyDB的寫入流程如下圖描述所示:

  1. 主節點DB例項收到使用者insert請求後,進行SQL解析、規劃、建立事務等步驟(這些與傳統PG無異),一旦事務提交,DB層便將產生的WAL寫入LSS(Log Storage Service),在LSS返回寫入成功後即可返回使用者成功。這裡的LSS是一個跨AZ的日誌儲存服務。如果存在只讀DB節點的話,主節點還會非同步地將WAL複製到只讀節點,這樣只讀節點可以在本地針對熱點資料page直接進行apply以保持cache中的page狀態一致,而不需要從底層的儲存層去讀取page(提高效能)
  2. LPS服務會在後臺持續地從LSS中讀取WAL日誌流,將其apply到相關的資料page,然後將資料page寫入底層的BSS,看圖中描述,LPS還會在本地cache一份page資料。每個AZ內都可以有單獨的LPS叢集,每個LPS叢集可以動態彈性擴縮容
  3. 一旦某些WAL被LPS讀取並apply後寫入了BSS,它便可以被安全地從LLS中刪除以回收儲存空間

讀流程

  1. 主或從節點收到使用者查詢請求後,進行SQL解析、規劃、建立事務等步驟。這些與傳統PG無異)
  2. 計算層從自己本地維護的page cache中讀取page,如果沒有命中,則從儲存層去讀取page(實際上,AlloyDB在這一層還維護了一個ultra-fast cache,個人猜測是DB計算層基於本地盤構建的page cache,但沒有根據)
  3. 儲存層這邊由LPS負責服務上層的讀取page請求,LPS本身有page cache,如果cache命中則直接返回,否則,會從底層BSS讀取特定page。除此外,LPS這層還需要維護每個Page的apply狀態(也即page已經回放到哪個位點,專業點叫LSN),計算層讀取時會攜帶LSN(實現MVCC),因此,如果LPS該page的最大回放LSN低於讀取LSN,該讀取請求會被阻塞,直到回放完成。因此,LPS回放的效率是整個系統性能的另外一個關鍵因素,這也是AlloyDB做LPS彈性的根本原因

彈性

這裡討論的主要是儲存層彈性。儲存層的彈性重點是LPS。LPS一方面需要執行WAL後臺回放,另外還需要處理DB計算層的page讀取請求,因而,需要解決其效能的橫向擴充套件性問題。

在AlloyDB中,持久化狀態被劃分成一系列的group,每個group是一組page集合,在AlloyDB中被稱為shard。(劃分規則是什麼呢?Shard數量固定嗎?shard應該只在儲存層可見而對計算層透明)

每個Shard會被唯一排程到一個LPS節點,每個LPS節點上可以同時容納多個Shard。Shard和LPS之間的對映關係是動態的,也就意味著只要增加/縮減LPS節點數量,就會引發Shard的遷移,從而實現計算能力的線性擴充套件。

作者在原始博文中提到了兩種case:

  1. 假如當前所有的Shard請求數量都出現上漲,此時儲存層自動擴容一批新的LPS節點,並將部分Shard遷移至這些新節點,這樣,每個節點上負責的Shard數量少了,負載自然就下降
  2. 假如某些Shard成為熱點,此時可以將這些熱點Shard遷移至某些獨佔LPS節點,對於該Shard來說相當於升配,也可以緩解該Shard處理能力不足的問題

無論那種情況,在LPS節點變更時產生的Shard遷移對上層應用透明,應用可以做到無感知。

複製

AlloyDB實現了儲存計算分離架構,原來由計算層負責的狀態一致性現在交由儲存層負責,具體來說,儲存層將資料複製到3AZ,每個AZ內都有一份完整的資料集。

但這裡有個問題文章中沒有詳細說明:WAL資料會跨AZ複製嗎?如果是,那麼勢必會對WAL寫入效能產生影響。如果不是,那麼除主AZ外的另外兩個AZ上(如Zone A和Zone C)的LPS就需要跨AZ地從主AZ上讀取WAL,這不僅影響WAL讀取效能,也會造成架構上的混亂。

我個人猜測是WAL寫到主AZ中就返回成功(當然主AZ上有一定的容錯性保證),然後後臺非同步地同步至從AZ,從AZ上的LPS服務只讀取本AZ內的WAL即可(個人猜測,僅供參考)。

當然還有另外一種可能性:Google內部本來就有這樣成熟跨AZ的Log Service,如果這樣的話,也就不需要應用做什麼非同步複製了,上面的擔心也就不復存在。

參考