新聞中心
作者 | 馬浩翔

日前,字節(jié)跳動技術(shù)社區(qū) ByteTech 舉辦的第四期字節(jié)跳動技術(shù)沙龍圓滿落幕,本期沙龍以《字節(jié)云數(shù)據(jù)庫架構(gòu)設計與實戰(zhàn)》為主題。在沙龍中,字節(jié)跳動基礎(chǔ)架構(gòu)數(shù)據(jù)庫開發(fā)工程師馬浩翔,跟大家探討了 《從單機到分布式數(shù)據(jù)庫存儲系統(tǒng)的演進》,本文根據(jù)分享整理而成。
存儲系統(tǒng)概覽
存儲系統(tǒng)是指能高效存儲,持久化用戶數(shù)據(jù)的一系列系統(tǒng)軟件。在眾多的存儲系統(tǒng)中,以下是三類比較主流的存儲產(chǎn)品及其特點分析:
塊存儲
- 底層語義,基于 block 編程;
- 接口樸素:在 Linux 的 IO 軟件棧中,要直接使用塊存儲的話就要基于 LBA 編程,因此接口較為簡單樸素,再加上塊存儲本身處于整個存儲軟件棧的底層,這導致塊存儲使用起來并不十分友好;
- 追求低時延、高吞吐:研發(fā)一個塊存儲系統(tǒng),在設計目標上我們往往會追求超高的性能,體現(xiàn)在超低的時延和超高的吞吐。但考慮到塊存儲的接口確實過于樸素,往往只有一些追求超高性能的系統(tǒng)才會直接基于塊存儲構(gòu)建,然后自建應用層 cache。
對象存儲
- 公有云上的王牌存儲產(chǎn)品:在 IT 時代,“Everything is data”的趨勢迅速催化了對象存儲系統(tǒng)。尤其對于字節(jié)跳動的業(yè)務而言,使用對象存儲系統(tǒng)來處理視頻、圖片、音頻等非結(jié)構(gòu)化的數(shù)據(jù),語義最為自然;
- 非結(jié)構(gòu)化數(shù)據(jù),提供 immutable 語義:一旦圖片或視頻等非結(jié)構(gòu)化數(shù)據(jù)上傳至對象存儲系統(tǒng)成功,則無法對其進行原地修改,只能通過刪除舊數(shù)據(jù),重新上傳新數(shù)據(jù)的方式完成“修改”邏輯;
- 成本優(yōu)先:一般不苛求單次操作的時延,但是非常注重系統(tǒng)吞吐 & 存儲成本。
文件系統(tǒng)
- 接口語義豐富,普適性強:遵循 POSIX/弱 POSIX 語義,諸如 Open、Write、Read 等許多操作數(shù)據(jù)的接口都能在文件系統(tǒng)中被找到。
- 擁有較多開源的分布式實現(xiàn),生態(tài)良好。
- 一般也不苛求時延,注重系統(tǒng)吞吐 & 存儲成本。
單機數(shù)據(jù)庫存儲解析
單機數(shù)據(jù)庫存儲,要從內(nèi)存層和持久化層兩個方面來解析。在內(nèi)存層,僅說關(guān)系型數(shù)據(jù)庫,其內(nèi)存數(shù)據(jù)結(jié)構(gòu)特點可以總結(jié)為:一切都是“樹”。我們以最常見的 B+ 樹為例,B+ 樹具有以下突出的特點:
- In memory 操作效率非常高:B+ 樹搜索時間復雜度是 log 級別;并且 B+ 樹的葉子節(jié)點構(gòu)成鏈表,非常有利于在內(nèi)存中對數(shù)據(jù)進行 scan 操作。
- 磁盤操作效率高:B+ 樹的 Fanout 足夠大,樹的層級較少,呈矮胖狀,可以減少磁盤 IO 數(shù);同時 B+ 樹的非葉子節(jié)點只存索引數(shù)據(jù),葉子節(jié)點存實際數(shù)據(jù),能大大壓縮樹高,進一步減少磁盤 IO 數(shù)。
- 數(shù)據(jù)結(jié)構(gòu)高度統(tǒng)一:數(shù)據(jù) & 索引都可以直接組織成 B+ 樹,因此代碼的可維護性、可讀性和開發(fā)效率都比較好。
僅有內(nèi)存數(shù)據(jù)結(jié)構(gòu)當然是不夠的,我們還需要設計高效的磁盤數(shù)據(jù)結(jié)構(gòu),下圖展示了從內(nèi)存數(shù)據(jù)結(jié)構(gòu)到磁盤數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)持久化過程:
左邊虛線框描繪的是 In Memory 結(jié)構(gòu)示意圖。舉個例子,如果我們要修改 Page A 的某一行數(shù)據(jù),對其中的一個字段進行自增,自增值是 2。自然而然會產(chǎn)生一個數(shù)據(jù)庫的物理操作日志,即 Redo Log,用來描述我們對 Page A 的修改。同時,在數(shù)據(jù)庫的事務執(zhí)行過程中,可能還會產(chǎn)生大量臨時數(shù)據(jù)(圖里的 Temp data),當內(nèi)存不夠用的時候也需要將其持久化。
右邊虛線框描繪的是 In Persistent Layer 的示意圖。假設我們使用比較友好的文件系統(tǒng)來將內(nèi)存數(shù)據(jù)持久化,我們需要設置不同的文件,讓它們各司其職。例如圖里的藍色文件存儲 Page,綠色文件存儲 Redo Log,粉色文件存儲臨時數(shù)據(jù)。如果數(shù)據(jù)庫發(fā)生 crash ,在恢復階段我們就在各類文件中進行數(shù)據(jù)定位,結(jié)合 Redo Log File 和 Page File ,進行數(shù)據(jù)庫的數(shù)據(jù)恢復。除此之外,如果直接基于塊存儲進行持久化,就需要數(shù)據(jù)庫本身的存儲引擎管理好 LBA,需要在用戶態(tài)里面實現(xiàn) buffer cache 等邏輯,這也是可行的。
那么基于單機的 FS / 塊存儲去做持久化,我們會遇到哪些問題呢?通過下面的單機數(shù)據(jù)庫系統(tǒng)的典型架構(gòu)圖,我們可以發(fā)現(xiàn)三個問題:
- 單機容量瓶頸:在 Database 層的單機服務器上運行著 database 進程,服務器上掛載了大量本地磁盤用作數(shù)據(jù)持久化。但一臺物理服務器能掛載的磁盤容量總是有限的,這就導致了單機的容量瓶頸問題。
- 擴縮容困難:當容量、CPU 或者內(nèi)存等資源不夠時,我們需要進行擴容。在單機時代,擴容意味著將數(shù)據(jù)從這個磁盤搬遷到另一個磁盤。但不管我們是通過網(wǎng)絡還是有線連接手段,都需要花費一定的時間,這可能導致業(yè)務較長時間的停寫(不可用),因此擴縮容是非常困難的。
- 多份獨立數(shù)據(jù),成本高:如果我們要在“復制集”之間或者主備機之間去做數(shù)據(jù)冗余或數(shù)據(jù)同步,那么每新增一分計算能力(新增一個計算節(jié)點),就要新增一分存儲冗余,就會導致存儲成本提高。
分布式數(shù)據(jù)庫存儲解析
為了解決單機數(shù)據(jù)庫存儲系統(tǒng)面臨的問題和挑戰(zhàn),字節(jié)跳動的數(shù)據(jù)庫團隊調(diào)研了一些業(yè)界主流的分布式數(shù)據(jù)庫方案。
MyRocks: MySQL + RocksDB
需要說明的是,MyRocks 不是分布式數(shù)據(jù)庫或者分布式解決方案,它是單機 SQL over kv 的典型代表。
- 核心理念:用 RocksDB 替換 InnoDB 。使用 RocksDB 能夠有效緩解單機容量瓶頸的問題;
- 特點:一是:數(shù)據(jù)可壓縮比例較高。RocksDB 實現(xiàn)了一種比較優(yōu)秀的壓縮算法,根據(jù)實際調(diào)研結(jié)果顯示,在關(guān)系型數(shù)據(jù)庫場景,基本上它能實現(xiàn) 2-4 倍的壓縮比,能有效緩解單機的容量瓶頸問題。例如,單機原本掛載了 10 塊磁盤,只能承載 10 TB 數(shù)據(jù),使用 RocksDB 就能在不改變硬件條件下幫助單機承載 20 TB 或 30 TB 等更多的數(shù)據(jù);二是,順序?qū)懶阅茌^好,這也是 LSM-Tree 這種數(shù)據(jù)結(jié)構(gòu)在 HDD 年代出現(xiàn)的核心原因。
- 難點:Compaction 會導致性能抖動,且兼容性一般。眾所周知,RocksDB 基于 LSM-Tree 構(gòu)建,必然會遇到一些典型的 LSM-Tree-based 系統(tǒng)的問題。雖然 RocksDB 對順序?qū)懱貏e友好,但它一定程度上犧牲了讀性能—— RocksDB 在讀的過程中會觸發(fā) Compaction,可能引發(fā)性能抖動,導致前臺的寫出現(xiàn)卡頓現(xiàn)象;同時,這一類 SQL over kv 解決方案的兼容性能表現(xiàn)較為一般。
Amazon Aurora: 計算存儲分離
- 核心理念:計算存儲分離,Log is Database。
- 特點:架構(gòu)靈活,存儲層帶有特定的數(shù)據(jù)庫計算邏輯,除了具備存儲能力之外,還具備 Redo Log 解析、回放生成數(shù)據(jù)庫 Page、維護多版本數(shù)據(jù)的能力。
- 優(yōu)勢:兼容性強、讀擴展能力較優(yōu)。基于共享存儲系統(tǒng)的特點,Amazon Aurora 的讀計算節(jié)點具備較好的擴展性,能夠?qū)崿F(xiàn)一主 15 備的部署形態(tài),其兼容性、讀擴展性表現(xiàn)較好。
Spanner 系: Shared-Nothing
- 核心理念:計算存儲分離,且 Share-Nothing。
- 特點:Spanner 系的數(shù)據(jù)庫系統(tǒng)一般基于分布式 k-v 存儲構(gòu)建,由存儲層保證事務特性,計算層做成純計算的無狀態(tài)節(jié)點。
- 難點:要解決當前數(shù)據(jù)庫生態(tài)兼容性問題 & 分布式 k-v 系統(tǒng)的 hotspot 問題比較麻煩。
最佳實踐:veDB 分布式存儲系統(tǒng)
基于上述字節(jié)數(shù)據(jù)庫團隊的調(diào)研結(jié)果,我們設計了 veDB 分布式存儲系統(tǒng)以解決單機數(shù)據(jù)庫存儲系統(tǒng)面臨的問題與挑戰(zhàn),本節(jié)將主要介紹 veDB 分布式存儲系統(tǒng)的系統(tǒng)目標與核心技術(shù)特點。
系統(tǒng)目標
在設計理念上,我們期望存儲系統(tǒng)能夠?qū)崿F(xiàn)以下四個主要目標:
- 極致彈性:存儲節(jié)點與計算節(jié)點解耦,隨時彈性擴縮容;
- 極致易用性:構(gòu)建 one-size-fits-all 的存儲系統(tǒng),而非專用存儲,要能兼容多個主流數(shù)據(jù)庫(MySQL & PostgreSQL & Mongo……);
- 極致性價比:低時延、低成本;
- 極致可靠性:具備高性能、高可靠的備份恢復能力。
基于以上系統(tǒng)目標,數(shù)據(jù)庫團隊設計并開發(fā)了 veDB 分布式存儲系統(tǒng),如下圖所示:
從圖中可以看出,分布式存儲層基于 LogStore 和 PageStore 這兩個子系統(tǒng)構(gòu)建,其系統(tǒng)特點與我們的設計目標相互呼應。
- 高彈性:存儲層可獨立擴縮容,計算層完全不感知;
- 高性價比:在 LogStore 實現(xiàn)了高性能 Log 存儲 & 在 PageStore 實現(xiàn)了低成本 Page 存儲;
- 兼容性好:LogStore 和 PageStore 都支持多 DB Engine 插件化;
- 高可靠:PageStore 側(cè)支持 Segment 級別的 PITR 功能。
核心技術(shù)
以下主要從研發(fā)背景(Problem)、解決思路(Solution)、解決成效(Outcome)三個方面來分別介紹 veDB 分布式存儲系統(tǒng)的五個核心技術(shù)。
Distributed Data Model
Problem:從單機 FS 到分布式存儲,需要有高效的數(shù)據(jù)布局模型?;趩螜C的文件系統(tǒng)或塊存儲系統(tǒng)去實現(xiàn)數(shù)據(jù)持久化是比較簡單的,我們可以直接通過申請一批 LBA 或者一批文件來存儲數(shù)據(jù),然后控制并發(fā)即可,但是這對于分布式存儲并不容易。從上面的示意圖可以看到,最上層的 Tablespace 代表一張數(shù)據(jù)庫表,里面可能包含上百萬甚至上千萬的 Page data(數(shù)據(jù)庫的基礎(chǔ)管理單元)。然而存儲系統(tǒng)的管理單元,卻不可能是 Page —— Page 的粒度過小,往往只有 KB 級別,如果存儲層以過小的粒度去管理數(shù)據(jù),可能會造成元數(shù)據(jù)膨脹,增加管理成本。
Solution:Tablespace -> Segement 分布式映射?;谏鲜鰡栴},我們可以在存儲層利用相對大的管理單元 Segment 去進行數(shù)據(jù)管理。此時,數(shù)據(jù)庫的管理單元是 Page,存儲系統(tǒng)的管理單元是 Segement。Tablespace 和 Segement 之間必然要存在一層映射關(guān)系,該映射關(guān)系可以根據(jù)不同數(shù)據(jù)庫引擎的數(shù)據(jù)管理空間大小要求進行設置,可能 MySQL 和 PostgreSQL 的映射規(guī)則就大不相同。上述示意圖展示了最簡單的模 2 規(guī)則,我們也可以發(fā)展出其他更加復雜的打散規(guī)則,此處不進行贅述。當我們將 Page 打散到對應的 Segement 之后,數(shù)據(jù)庫就不需要管數(shù)據(jù) Replication 的邏輯,不管底層存儲是多副本還是 EC 策略,可以完全由存儲系統(tǒng)來做透明的 Replication ,數(shù)據(jù)庫就像在使用單機文件系統(tǒng)一樣簡單。
Outcome
- 天然負載均衡;
- 分布式打散,可最大程度實現(xiàn)并行計算;
- Scale in/out 簡單,僅需部分 Segement 數(shù)據(jù) rebalance,摒棄了將整個數(shù)據(jù)庫表的 TB 級數(shù)據(jù)在硬盤間搬遷的繁雜流程。
Log-Only Segement
Problem:數(shù)據(jù)冗余成本高,需要降低存儲成本。
Solution:開發(fā) Log-Only Segement,節(jié)省非必要的 Page 副本空間。什么是 Log-only segement? 關(guān)系型數(shù)據(jù)庫中往往都包含 Log 數(shù)據(jù)和 Page 數(shù)據(jù)。在存儲層中,存了多副本的 Log 數(shù)據(jù)后,我們可以選擇性地只回放一部分 Log 數(shù)據(jù)來生成 Page,讓另一部分 Log 數(shù)據(jù)保持不動,不要生成任何 Page 數(shù)據(jù)。以上面的示意圖為例,Rep_0 和 Rep_1 都是 Log 數(shù)據(jù)生成的各種版本 Page 數(shù)據(jù),然而 Rep_2 是一個空的 Page 數(shù)據(jù)副本,它里面只有 Redo log。我們都知道 Redo log 和 Page data 的數(shù)據(jù)大小比例是比較夸張的,Page data 的大小可能是 Redo log 的幾倍甚至十幾倍,因此通過以上方法能夠較大的節(jié)省單機的 Page 存儲空間。
Outcome:結(jié)合單機引擎的壓縮算法,能將存儲空間放大倍數(shù)從 3.x -> 1.x,較好緩解成本問題。
高性能 IO 引擎
Problem:存儲層寫性能容易成為系統(tǒng)性能瓶頸,如何解決?
Solution:全異步 IO + 無鎖結(jié)構(gòu) +并發(fā)打散。當數(shù)據(jù)庫提交了一個 Redo log 到 Log Storage 之后,Log Storage 中會有一個無鎖的 Ring buffer 去對 Redo log 進行有序組織,然后我們將 Redo Log 的 Ring buffer 進行線性的定長切割,并發(fā)打散到底層存儲的 Blob 單元。
Outcome:4KB + depth 8,write latency ~100+us,較好支撐了數(shù)據(jù)庫下發(fā)日志的性能剛需。
PITR
PITR(Point-in-time Recovery)是指我們都可以迅速地恢復在過去一段時間內(nèi)某個時間點的數(shù)據(jù)庫快照。
Problem:如何快速備份恢復,且降低對前臺業(yè)務影響?
Solution:基于 Segement 的高并發(fā) PITR 機制,Segement 間互不影響。之前提到存儲層的管理單元是 Segement ,我們也可以基于 Segement 做備份恢復。這樣做有兩個好處:首先計算層是完全透明的,計算層完全不會感知,并且計算層的性能不會抖動。其次基于 Segment 可以做到天然的并發(fā)打散,因此備份恢復也可以做到并發(fā)恢復。
Outcome
- 性能優(yōu)秀,恢復 1TB 數(shù)據(jù) ~15min;
- 擴展性強,不受數(shù)據(jù)大小影響,性能與數(shù)據(jù)大小呈近常數(shù)關(guān)系:因為基于 Segment 單元去做并發(fā)備份恢復,每個 Segment 都是獨立的,其性能能夠與數(shù)據(jù)大小解耦開來。因此不管數(shù)據(jù)大小是多少,只要備份恢復資源足夠,都能做到常數(shù)級的備份恢復性能。
多計算引擎插件化
Problem:數(shù)據(jù)庫團隊希望統(tǒng)一的存儲層能夠支持不同的數(shù)據(jù)庫引擎,做到 100% 兼容和快速接入。
Solution:Write Ahead Log + Log Replay = 任意 Page Data?;诒镜卮鎯σ娴?k-v 結(jié)構(gòu),或者基于裸的塊設備抽象出一種相對通用的數(shù)據(jù)結(jié)構(gòu),從而高效地存儲 Page data。同時,我們在 SDK 側(cè)和 Server 側(cè)都做了 Log parse 的插件化,要接入新的數(shù)據(jù)庫引擎只需要其提供適配存儲接口的日志插件,從而可以快速接入各式各樣的數(shù)據(jù)庫計算引擎。
Outcome:
- 基于統(tǒng)一接口,計算引擎僅需提供 Log parse + Replay lib 即可接入 veDB 存儲層。
- 統(tǒng)一存儲層已支持 MySQL、PostgreSQL、MongoDB 計算引擎,目前仍在持續(xù)拓展。
數(shù)據(jù)庫存儲系統(tǒng):What's Next
在談及數(shù)據(jù)庫存儲的未來演進時,首先我們可以思考一下哪些因素會觸發(fā)數(shù)據(jù)庫存儲架構(gòu)的變革和演進?答案可能包含:存儲架構(gòu)自身的革命、數(shù)據(jù)庫理論的突破、或者新硬件沖擊引發(fā)存儲系統(tǒng)架構(gòu)迭代。基于這三個方向的思考,我們總結(jié)了以下幾個數(shù)據(jù)庫存儲系統(tǒng)的演進趨勢:
HTAP/HSAP
我們總結(jié)的第一個趨勢即 HTAP/HSAP 系統(tǒng)將會逐漸爆發(fā)。在 HTAP/HSAP 系統(tǒng)中,“實時”是第一關(guān)鍵詞。為了支持實時,存儲系統(tǒng)可能會發(fā)生架構(gòu)演進和變革,因此我們需要探索:
- 行列存 All-in-one:既要存儲行式的數(shù)據(jù),又要存儲列式的數(shù)據(jù)。
- 近實時,寫時計算:我們需要在存儲層實現(xiàn)寫時計算的邏輯來支持實時性。
AI Enhancement
AI 技術(shù)運用領(lǐng)域廣泛,具體在數(shù)據(jù)庫存儲領(lǐng)域,我們可以利用 AI 技術(shù)進行以下工作:
- 存儲參數(shù)調(diào)優(yōu);
- 智能存儲格式:利用 AI 技術(shù)進行智能的行存和列存格式轉(zhuǎn)換,AI 可以提醒我們什么時間進行轉(zhuǎn)換,什么時候絕對不能轉(zhuǎn)換,從而避免格式轉(zhuǎn)換為前臺業(yè)務帶來的性能 overhead。
Hardware Revolution
在硬件變革趨勢上,我們總結(jié)了三個變革方向:
- 存儲介質(zhì)變革:前幾年,我們可能更多關(guān)注 SSD、HDD。目前我們處于 SSD 往 persistent memory 轉(zhuǎn)變的風口,那么如何利用 persistent memory 去定制軟件架構(gòu)?目前看在文件系統(tǒng)側(cè)已經(jīng)有一些研究,但是在數(shù)據(jù)庫側(cè)并沒有太多公開實踐。
- 計算單元變革:CPU 產(chǎn)品已經(jīng)從 multi-core 變成了 many-core (從 96c 變成了 192c、384c)。要怎么利用多核的能力?對于非計算密集型的存儲系統(tǒng)而言,多余的算力能否用來加速數(shù)據(jù)庫算子?一些無鎖的數(shù)據(jù)結(jié)構(gòu)是不是需要要重新設計?以上都需要我們認真考慮。
- 網(wǎng)絡設施變革:例如 RDMA ,以及可編程的 P4 交換機這類全新的一些網(wǎng)絡設施,可能會對我們的軟件架構(gòu)特別是分布式存儲架構(gòu)造成較大的沖擊。相應地,我們需要在存儲側(cè)做出調(diào)整。
當前文章:從單機到分布式數(shù)據(jù)庫存儲系統(tǒng)的演進
分享路徑:http://fisionsoft.com.cn/article/coioech.html


咨詢
建站咨詢
