從單機到分散式資料庫儲存系統的演進

語言: CN / TW / HK

日前,位元組跳動技術社群 ByteTech 舉辦的第四期位元組跳動技術沙龍圓滿落幕,本期沙龍以《位元組雲資料庫架構設計與實戰》為主題。在沙龍中,位元組跳動基礎架構資料庫開發工程師馬浩翔,跟大家探討了 《從單機到分散式資料庫儲存系統的演進》,本文根據分享整理而成。

儲存系統概覽

儲存系統是指能高效儲存,持久化使用者資料的一系列系統軟體。在眾多的儲存系統中,以下是三類比較主流的儲存產品及其特點分析:

塊儲存

  • 底層語義,基於 block 程式設計;
  • 介面樸素:在 Linux 的 IO 軟體棧中,要直接使用塊儲存的話就要基於 LBA 程式設計,因此介面較為簡單樸素,再加上塊儲存本身處於整個儲存軟體棧的底層,這導致塊儲存使用起來並不十分友好;
  • 追求低時延、高吞吐:研發一個塊儲存系統,在設計目標上我們往往會追求超高的效能,體現在超低的時延和超高的吞吐。但考慮到塊儲存的介面確實過於樸素,往往只有一些追求超高效能的系統才會直接基於塊儲存構建,然後自建應用層 cache。

物件儲存

  • 公有云上的王牌儲存產品:在 IT 時代,“Everything is data”的趨勢迅速催化了物件儲存系統。尤其對於位元組跳動的業務而言,使用物件儲存系統來處理影片、圖片、音訊等非結構化的資料,語義最為自然;
  • 非結構化資料,提供 immutable 語義:一旦圖片或影片等非結構化資料上傳至物件儲存系統成功,則無法對其進行原地修改,只能通過刪除舊資料,重新上傳新資料的方式完成“修改”邏輯;
  • 成本優先:一般不苛求單次操作的時延,但是非常注重系統吞吐 & 儲存成本。

檔案系統

  • 介面語義豐富,普適性強:遵循 POSIX/弱 POSIX 語義,諸如 Open、Write、Read 等許多操作資料的介面都能在檔案系統中被找到。
  • 擁有較多開源的分散式實現,生態良好。
  • 一般也不苛求時延,注重系統吞吐 & 儲存成本。

單機資料庫儲存解析

單機資料庫儲存,要從記憶體層和持久化層兩個方面來解析。在記憶體層,僅說關係型資料庫,其記憶體資料結構特點可以總結為:一切都是“樹”。我們以最常見的 B+ 樹為例,B+ 樹具有以下突出的特點:

  • In memory 操作效率非常高:  B+ 樹搜尋時間複雜度是 log 級別;並且 B+ 樹的葉子節點構成連結串列,非常有利於在記憶體中對資料進行 scan 操作。
  • 磁碟操作效率高: B+ 樹的 Fanout 足夠大,樹的層級較少,呈矮胖狀,可以減少磁碟 IO 數;同時 B+ 樹的非葉子節點只存索引資料,葉子節點存實際資料,能大大壓縮樹高,進一步減少磁碟 IO 數。
  • 資料結構高度統一: 資料 & 索引都可以直接組織成 B+ 樹,因此程式碼的可維護性、可讀性和開發效率都比較好。

僅有記憶體資料結構當然是不夠的,我們還需要設計高效的磁碟資料結構,下圖展示了從記憶體資料結構到磁碟資料結構的資料持久化過程:

圖片

左邊虛線框描繪的是 In Memory 結構示意圖。舉個例子,如果我們要修改 Page A 的某一行資料,對其中的一個欄位進行自增,自增值是 2。自然而然會產生一個數據庫的物理操作日誌,即 Redo Log,用來描述我們對 Page A 的修改。同時,在資料庫的事務執行過程中,可能還會產生大量臨時資料(圖裡的 Temp data),當記憶體不夠用的時候也需要將其持久化。

右邊虛線框描繪的是 In Persistent Layer 的示意圖。假設我們使用比較友好的檔案系統來將記憶體資料持久化,我們需要設定不同的檔案,讓它們各司其職。例如圖裡的藍色檔案儲存 Page,綠色檔案儲存 Redo Log,粉色檔案儲存臨時資料。如果資料庫發生 crash ,在恢復階段我們就在各類檔案中進行資料定位,結合 Redo Log File 和 Page File ,進行資料庫的資料恢復。除此之外,如果直接基於塊儲存進行持久化,就需要資料庫本身的儲存引擎管理好 LBA,需要在使用者態裡面實現 buffer cache 等邏輯,這也是可行的。

那麼基於單機的 FS / 塊儲存去做持久化,我們會遇到哪些問題呢? 通過下面的單機資料庫系統的典型架構圖,我們可以發現三個問題:

圖片

  • 單機容量瓶頸: 在 Database 層的單機伺服器上執行著 database 程序,伺服器上掛載了大量本地磁碟用作資料持久化。但一臺物理伺服器能掛載的磁碟容量總是有限的,這就導致了單機的容量瓶頸問題。
  • 擴縮容困難: 當容量、CPU 或者記憶體等資源不夠時,我們需要進行擴容。在單機時代,擴容意味著將資料從這個磁碟搬遷到另一個磁碟。但不管我們是通過網路還是有線連線手段,都需要花費一定的時間,這可能導致業務較長時間的停寫(不可用),因此擴縮容是非常困難的。
  • 多份獨立資料,成本高: 如果我們要在“複製集”之間或者主備機之間去做資料冗餘或資料同步,那麼每新增一分計算能力(新增一個計算節點),就要新增一分儲存冗餘,就會導致儲存成本提高。

分散式資料庫儲存解析

為了解決單機資料庫儲存系統面臨的問題和挑戰,位元組跳動的資料庫團隊調研了一些業界主流的分散式資料庫方案。

MyRocks: MySQL + RocksDB

需要說明的是,MyRocks 不是分散式資料庫或者分散式解決方案,它是單機 SQL over kv 的典型代表。

  • 核心理念: 用 RocksDB 替換 InnoDB 。使用 RocksDB 能夠有效緩解單機容量瓶頸的問題;
  • 特點: 一是:資料可壓縮比例較高。RocksDB 實現了一種比較優秀的壓縮演算法,根據實際調研結果顯示,在關係型資料庫場景,基本上它能實現 2-4 倍的壓縮比,能有效緩解單機的容量瓶頸問題。例如,單機原本掛載了 10 塊磁碟,只能承載 10 TB 資料,使用 RocksDB 就能在不改變硬體條件下幫助單機承載 20 TB 或 30 TB 等更多的資料;二是,順序寫效能較好,這也是 LSM-Tree 這種資料結構在 HDD 年代出現的核心原因。
  • 難點: Compaction 會導致效能抖動,且相容性一般。眾所周知,RocksDB 基於 LSM-Tree 構建,必然會遇到一些典型的 LSM-Tree-based 系統的問題。雖然 RocksDB 對順序寫特別友好,但它一定程度上犧牲了讀效能—— RocksDB 在讀的過程中會觸發 Compaction,可能引發效能抖動,導致前臺的寫出現卡頓現象;同時,這一類 SQL over kv 解決方案的相容效能表現較為一般。

Amazon Aurora: 計算儲存分離

  • 核心理念: 計算儲存分離,Log is Database。
  • 特點: 架構靈活,儲存層帶有特定的資料庫計算邏輯,除了具備儲存能力之外,還具備 Redo Log 解析、回放生成資料庫 Page、維護多版本資料的能力。
  • 優勢: 相容性強、讀擴充套件能力較優。基於共享儲存系統的特點,Amazon Aurora 的讀計算節點具備較好的擴充套件性,能夠實現一主 15 備的部署形態,其相容性、讀擴充套件性表現較好。

Spanner 系: Shared-Nothing

  • 核心理念: 計算儲存分離,且 Share-Nothing。
  • 特點: Spanner 系的資料庫系統一般基於分散式 k-v 儲存構建,由儲存層保證事務特性,計算層做成純計算的無狀態節點。
  • 難點: 要解決當前資料庫生態相容性問題 & 分散式 k-v 系統的 hotspot 問題比較麻煩。

最佳實踐:veDB 分散式儲存系統

基於上述位元組資料庫團隊的調研結果,我們設計了 veDB 分散式儲存系統以解決單機資料庫儲存系統面臨的問題與挑戰,本節將主要介紹 veDB 分散式儲存系統的系統目標與核心技術特點。

系統目標

在設計理念上,我們期望儲存系統能夠實現以下四個主要目標:

  • 極致彈性: 儲存節點與計算節點解耦,隨時彈性擴縮容;
  • 極致易用性: 構建 one-size-fits-all 的儲存系統,而非專用儲存,要能相容多個主流資料庫(MySQL & PostgreSQL & Mongo……);
  • 極致價效比: 低時延、低成本;
  • 極致可靠性: 具備高效能、高可靠的備份恢復能力。

基於以上系統目標,資料庫團隊設計並開發了 veDB 分散式儲存系統,如下圖所示:

圖片

從圖中可以看出,分散式儲存層基於 LogStore 和 PageStore 這兩個子系統構建,其系統特點與我們的設計目標相互呼應。

  • 高彈性:儲存層可獨立擴縮容,計算層完全不感知;
  • 高性價比:在 LogStore 實現了高效能 Log 儲存 & 在 PageStore 實現了低成本 Page 儲存;
  • 相容性好:LogStore 和 PageStore 都支援多 DB Engine 外掛化;
  • 高可靠:PageStore 側支援 Segment 級別的 PITR 功能。

核心技術

以下主要從研發背景(Problem)、解決思路(Solution)、解決成效(Outcome)三個方面來分別介紹 veDB 分散式儲存系統的五個核心技術。

Distributed Data Model

圖片

Problem:從單機 FS 到分散式儲存,需要有高效的資料佈局模型。 基於單機的檔案系統或塊儲存系統去實現資料持久化是比較簡單的,我們可以直接通過申請一批 LBA 或者一批檔案來儲存資料,然後控制併發即可,但是這對於分散式儲存並不容易。從上面的示意圖可以看到,最上層的 Tablespace 代表一張資料庫表,裡面可能包含上百萬甚至上千萬的 Page data(資料庫的基礎管理單元)。然而儲存系統的管理單元,卻不可能是 Page —— Page 的粒度過小,往往只有 KB 級別,如果儲存層以過小的粒度去管理資料,可能會造成元資料膨脹,增加管理成本。

Solution:Tablespace -> Segement 分散式對映。 基於上述問題,我們可以在儲存層利用相對大的管理單元 Segment 去進行資料管理。此時,資料庫的管理單元是 Page,儲存系統的管理單元是 Segement。Tablespace 和 Segement 之間必然要存在一層對映關係,該對映關係可以根據不同資料庫引擎的資料管理空間大小要求進行設定,可能 MySQL 和 PostgreSQL 的對映規則就大不相同。上述示意圖展示了最簡單的模 2 規則,我們也可以發展出其他更加複雜的打散規則,此處不進行贅述。當我們將 Page 打散到對應的 Segement 之後,資料庫就不需要管資料 Replication 的邏輯,不管底層儲存是多副本還是 EC 策略,可以完全由儲存系統來做透明的 Replication ,資料庫就像在使用單機檔案系統一樣簡單。

Outcome

  • 天然負載均衡;
  • 分散式打散,可最大程度實現平行計算;
  • Scale in/out 簡單,僅需部分 Segement 資料 rebalance,摒棄了將整個資料庫表的 TB 級資料在硬碟間搬遷的繁雜流程。

Log-Only Segement

圖片

Problem:資料冗餘成本高,需要降低儲存成本。

Solution:開發 Log-Only Segement,節省非必要的 Page 副本空間。 什麼是 Log-only segement? 關係型資料庫中往往都包含 Log 資料和 Page 資料。在儲存層中,存了多副本的 Log 資料後,我們可以選擇性地只回放一部分 Log 資料來生成 Page,讓另一部分 Log 資料保持不動,不要生成任何 Page 資料。以上面的示意圖為例,Rep_0 和 Rep_1 都是 Log 資料生成的各種版本 Page 資料,然而 Rep_2 是一個空的 Page 資料副本,它裡面只有 Redo log。我們都知道 Redo log 和 Page data 的資料大小比例是比較誇張的,Page data 的大小可能是 Redo log 的幾倍甚至十幾倍,因此通過以上方法能夠較大的節省單機的 Page 儲存空間。

Outcome: 結合單機引擎的壓縮演算法,能將儲存空間放大倍數從 3.x -> 1.x,較好緩解成本問題。

高效能 IO 引擎

圖片

Problem: 儲存層寫效能容易成為系統性能瓶頸,如何解決?

Solution:全非同步 IO + 無鎖結構 +併發打散。 當資料庫提交了一個 Redo log 到 Log Storage 之後,Log Storage 中會有一個無鎖的 Ring buffer 去對 Redo log 進行有序組織,然後我們將 Redo Log 的 Ring buffer 進行線性的定長切割,併發打散到底層儲存的 Blob 單元。

Outcome:4KB + depth 8,write latency ~100+us, 較好支撐了資料庫下發日誌的效能剛需。

PITR

PITR(Point-in-time Recovery)是指我們都可以迅速地恢復在過去一段時間內某個時間點的資料庫快照。

圖片

Problem: 如何快速備份恢復,且降低對前臺業務影響?

Solution:基於 Segement 的高併發 PITR 機制,Segement 間互不影響。 之前提到儲存層的管理單元是 Segement ,我們也可以基於 Segement 做備份恢復。這樣做有兩個好處:首先計算層是完全透明的,計算層完全不會感知,並且計算層的效能不會抖動。其次基於 Segment 可以做到天然的併發打散,因此備份恢復也可以做到併發恢復。

Outcome

  • 效能優秀,恢復 1TB 資料 ~15min;
  • 擴充套件性強,不受資料大小影響,效能與資料大小呈近常數關係:因為基於 Segment 單元去做併發備份恢復,每個 Segment 都是獨立的,其效能能夠與資料大小解耦開來。因此不管資料大小是多少,只要備份恢復資源足夠,都能做到常數級的備份恢復效能。

多計算引擎外掛化

Problem: 資料庫團隊希望統一的儲存層能夠支援不同的資料庫引擎,做到 100% 相容和快速接入。

Solution:Write Ahead Log + Log Replay = 任意 Page Data。 基於本地儲存引擎的 k-v 結構,或者基於裸的塊裝置抽象出一種相對通用的資料結構,從而高效地儲存 Page data。同時,我們在 SDK 側和 Server 側都做了 Log parse 的外掛化,要接入新的資料庫引擎只需要其提供適配儲存介面的日誌外掛,從而可以快速接入各式各樣的資料庫計算引擎。

Outcome:

  • 基於統一介面,計算引擎僅需提供 Log parse + Replay lib 即可接入 veDB 儲存層。
  • 統一儲存層已支援 MySQL、PostgreSQL、MongoDB 計算引擎,目前仍在持續拓展。

資料庫儲存系統:What's Next

在談及資料庫儲存的未來演進時,首先我們可以思考一下哪些因素會觸發資料庫儲存架構的變革和演進?答案可能包含:儲存架構自身的革命、資料庫理論的突破、或者新硬體衝擊引發儲存系統架構迭代。基於這三個方向的思考,我們總結了以下幾個資料庫儲存系統的演進趨勢:

HTAP/HSAP

我們總結的第一個趨勢即 HTAP/HSAP 系統將會逐漸爆發。在 HTAP/HSAP 系統中,“實時”是第一關鍵詞。為了支援實時,儲存系統可能會發生架構演進和變革,因此我們需要探索:

  • 行列存 All-in-one:既要儲存行式的資料,又要儲存列式的資料。
  • 近實時,寫時計算:我們需要在儲存層實現寫時計算的邏輯來支援實時性。

AI Enhancement

AI 技術運用領域廣泛,具體在資料庫儲存領域,我們可以利用 AI 技術進行以下工作:

  • 儲存引數調優;
  • 智慧儲存格式:利用 AI 技術進行智慧的行存和列存格式轉換,AI 可以提醒我們什麼時間進行轉換,什麼時候絕對不能轉換,從而避免格式轉換為前臺業務帶來的效能 overhead。

Hardware Revolution

在硬體變革趨勢上,我們總結了三個變革方向:

  • 儲存介質變革:前幾年,我們可能更多關注 SSD、HDD。目前我們處於 SSD 往 persistent memory 轉變的風口,那麼如何利用 persistent memory 去定製軟體架構?目前看在檔案系統側已經有一些研究,但是在資料庫側並沒有太多公開實踐。
  • 計算單元變革:CPU 產品已經從 multi-core 變成了 many-core (從 96c 變成了 192c、384c)。要怎麼利用多核的能力?對於非計算密集型的儲存系統而言,多餘的算力能否用來加速資料庫運算元?一些無鎖的資料結構是不是需要要重新設計?以上都需要我們認真考慮。
  • 網路設施變革:例如 RDMA ,以及可程式設計的 P4 交換機這類全新的一些網路設施,可能會對我們的軟體架構特別是分散式儲存架構造成較大的衝擊。相應地,我們需要在儲存側做出調整。

位元組跳動基礎架構資料庫&雲端儲存團隊

Database & Cloud Storage Team,服務於位元組跳動全系產品。在這裡,我們有豐富的雲端儲存產品,負責治理數十 EB 級別的海量資料;有多種資料庫產品,提供極致時延、超大吞吐的雲原生資料庫服務;有前沿的技術研究,探索新硬體與新軟體架構的融合,打造下一代革命性的雲端儲存與資料庫產品。

圖片

以上內容整理自第四期位元組跳動技術沙龍《位元組雲資料庫架構設計與實戰》,獲取講師 PPT 和回放影片,請在公眾號“位元組跳動技術團隊”後臺回覆關鍵詞“0416”。