大規(guī)模塊存儲(chǔ) EC 系統(tǒng)構(gòu)建
本文整理自 2023 年 7 月 DataFunSummit 2023 數(shù)據(jù)基礎(chǔ)架構(gòu)峰會(huì)——大規(guī)模存儲(chǔ)架構(gòu)分論壇的同名主題分享。
非常歡迎大家的到來,今天由我來分享百度智能云塊存儲(chǔ) EC 系統(tǒng)的構(gòu)建。塊存儲(chǔ)系統(tǒng)在百度智能云的產(chǎn)品名叫 CDS,底層 EC 系統(tǒng)由 Aries 承擔(dān)。
關(guān)于 Aries 的詳細(xì)介紹,可以參考文末「傳送門」的第一篇文章。
今天主要介紹的內(nèi)容如下,首先會(huì)比較一下各種容錯(cuò)方式,介紹一下我們選擇 EC 容錯(cuò)方式的必然性;然后給大家介紹一下在塊存儲(chǔ)產(chǎn)品下構(gòu)建 EC 引擎的挑戰(zhàn),并逐步展開對(duì)這些挑戰(zhàn)進(jìn)行分析和解決的方法;最后,我們介紹一下基于這個(gè)解決方案的一些優(yōu)化。
1. 數(shù)據(jù)容錯(cuò)方式比較
首先介紹一下常見的數(shù)據(jù)容錯(cuò)方式。
數(shù)據(jù)容錯(cuò)在單機(jī)和分布式系統(tǒng)下,有著不同的選擇。
單機(jī)情況下,比較直接的方式是選用 RAID 卡。在 BIOS 中配置,一般支持 RAID5 就夠了。如果沒有 RAID 卡,也可以用軟 RAID,創(chuàng)建帶有 RAID 功能的邏輯卷。
分布式的情況,比較直接的方式是采用多副本的形式,將數(shù)據(jù)復(fù)制成多份,存在不同的機(jī)器。實(shí)際上,最好將每份數(shù)據(jù)保存到不同的交換機(jī)下。另外一種方式是采用分布式糾刪碼的方式。這種方式其實(shí)就是分布式的 RAID。只不過,單機(jī)用奇偶校驗(yàn)的 RAID5 基本可以保證數(shù)據(jù)安全,而分布式系統(tǒng)中,由于磁盤規(guī)模龐大,糾刪碼的復(fù)雜度要高一些。
圖片
這里介紹一下分布式容錯(cuò)方式的實(shí)現(xiàn)。多副本方式容錯(cuò),每個(gè)副本的數(shù)據(jù)相同,所以,一般采用分布式的一致性協(xié)議對(duì)數(shù)據(jù)進(jìn)行同步,主流的協(xié)議為 Paxos 和 Raft。有的系統(tǒng)也會(huì)自研一些分發(fā)寫的協(xié)議。最終目的是保證多份數(shù)據(jù)相同。多副本情況下,假設(shè)是 N 副本,則最多允許 N-1 份數(shù)據(jù)損壞。
糾刪碼則是將用戶的原始數(shù)據(jù)進(jìn)行切分,形成 K 個(gè)大小相等的分片,然后對(duì)這些分片進(jìn)行編碼,形成 M 個(gè)校驗(yàn)分片。校驗(yàn)分片的大小和數(shù)據(jù)分片相同。K+M 個(gè)分片會(huì)被分布在不同的機(jī)器上。一般情況下,糾刪碼允許最多 M 個(gè)分片數(shù)據(jù)損壞。最常用的糾刪碼是 Reed-Solomon 編碼(RS 碼);
圖片
從成本考慮,3 副本將數(shù)據(jù)存儲(chǔ) 3 份,因此是 3 倍的存儲(chǔ)成本。而糾刪碼,是 K+M 的形式,K 份的數(shù)據(jù),編碼形成 M 份校驗(yàn)。通常情況下,M 比 K 要小。因此存儲(chǔ)成本一般為 1.x 倍。
但是,糾刪碼也有自己缺點(diǎn)。多副本將數(shù)據(jù)無修改地復(fù)制到另外節(jié)點(diǎn),不需要計(jì)算參與,數(shù)據(jù)恢復(fù)則是將數(shù)據(jù)重新復(fù)制一遍,方法比較簡單。而糾刪碼則涉及到編碼和解碼,除了計(jì)算以外,編碼和解碼同樣會(huì)帶來額外的 I/O 開銷。
現(xiàn)代 CPU 已經(jīng)支持 RS 編碼的硬件加速,能夠提升編碼/解碼速度,極大減少計(jì)算壓力。而 I/O 放大,則是我們重點(diǎn)要解決的問題。
磁盤通常具有 1%~2% 的年化故障率。由于分布式系統(tǒng)規(guī)模都比較大,大的集群都會(huì)有千臺(tái)機(jī)器,萬塊磁盤的規(guī)模,一定會(huì)同時(shí)出現(xiàn)多塊磁盤同時(shí)故障。因此,一般分布式系統(tǒng)都采用 2 個(gè)以上的備份或者校驗(yàn)。一般采用 3 副本或者 RS 編碼才能保證數(shù)據(jù)的可靠性?;谀壳暗募阂?guī)模和成本,糾刪碼是必然的選擇。
圖片
2. 大規(guī)模塊存儲(chǔ) EC 的技術(shù)挑戰(zhàn)
既然選擇了糾刪碼,下面那必須解決 EC 系統(tǒng)中面臨的各種問題。
系統(tǒng)面臨的挑戰(zhàn)主要來自幾個(gè)方面。一個(gè)是產(chǎn)品訪問特性,塊存儲(chǔ)存儲(chǔ)的是動(dòng)態(tài)數(shù)據(jù),用戶會(huì)隨時(shí)對(duì)數(shù)據(jù)進(jìn)行修改。而 EC 修改代價(jià)高,原地修改需要引入額外計(jì)算和 I/O 放大。
另外就是用戶下發(fā)給磁盤的數(shù)據(jù)大小不一,而小寫不適合 EC。如何解決小 I/O 的 EC,是另外一個(gè)問題。
應(yīng)用場景上,用戶對(duì)大小寫的要求是不一樣的。因此,我們的設(shè)計(jì)需要一個(gè)合理的系統(tǒng)開銷,減少資源占用,使得用戶有比較好的 I/O 表現(xiàn)。
圖片
對(duì)比一下對(duì)象存儲(chǔ)。它對(duì)外的接口是 put、get、delete。寫入時(shí),將一個(gè)對(duì)象的整體數(shù)據(jù)整體寫入和整體刪除,不涉及到重新編碼的問題。因此,除了寫入時(shí)產(chǎn)生的校驗(yàn)數(shù)據(jù)需要保存,沒有其他寫放大問題。
圖片
塊存儲(chǔ),主要接口是讀寫和刪除。與對(duì)象存儲(chǔ)不同,這里的寫絕大部分是對(duì)原有數(shù)據(jù)的修改。
對(duì)于部分?jǐn)?shù)據(jù)修改,如果重新計(jì)算校驗(yàn),需要將剩余數(shù)據(jù)從其余節(jié)點(diǎn)讀到內(nèi)存中,重新計(jì)算校驗(yàn)值,然后將校驗(yàn)再次寫回。這里涉及到了「讀-修改-寫」,I/O 放大比較嚴(yán)重。
圖片
這里是線上 I/O 次數(shù)統(tǒng)計(jì),小 I/O 的次數(shù)遠(yuǎn)遠(yuǎn)多于大 I/O。塊存儲(chǔ) CDS 中,4K 大小的 I/O 占據(jù)了半數(shù)以上的寫次數(shù)。但是小 I/O 不太適合 EC。
舉個(gè)例子:我們?nèi)绻捎?K 為 4,M 為 2 的編碼,需要將分片切成 1K 大小,而一般情況下,文件系統(tǒng)對(duì)于 4K 倍數(shù)的 I/O 支持比較友好,1K 的 I/O size,相對(duì)來說,不是很合理的 I/O size。如果 K 值更大,會(huì)產(chǎn)生更細(xì)碎的分片。我們需要解決這些小 I/O 的 EC。
圖片
現(xiàn)代軟件已經(jīng)對(duì)磁盤訪問有比較多的優(yōu)化。當(dāng)程序追求吞吐時(shí)會(huì)下發(fā)大 I/O,減少磁盤尋道時(shí)間。當(dāng)程序?qū)ρ訒r(shí)有需求時(shí),通常會(huì)下發(fā)盡量小的 I/O,減少不必要的數(shù)據(jù)對(duì) I/O 帶寬的占用。
因此,總體看來,對(duì)于小 I/O用戶需要的是小的延時(shí),對(duì)于用戶大的 I/O 用戶需要的是高的吞吐。
硬件的物理帶寬是有限的,提供高性能的存儲(chǔ)引擎,系統(tǒng)本身占用的資源應(yīng)該盡量小。對(duì)于存儲(chǔ)引擎來說,主要就是寫放大問題。
圖片
3. 百度滄海的實(shí)現(xiàn)方案
針對(duì)以上問題,我們看一下百度滄海的解決方案。
這里我們重新看一下修改,I/O 放大的主要原因是需要對(duì)原有存量數(shù)據(jù)進(jìn)行修改操作,如果不做特殊優(yōu)化,這些操作需要將原有數(shù)據(jù)讀出來,用于計(jì)算新的校驗(yàn)。
CDS 的選擇是構(gòu)建一個(gè)索引層,索引指向 EC 后的數(shù)據(jù),數(shù)據(jù)的修改不在原地進(jìn)行。
這里給了個(gè)例子,用戶第一次寫的數(shù)據(jù),EC 并且存儲(chǔ)后,建立了一個(gè)索引指向這塊數(shù)據(jù)。后續(xù)在中間的修改將作為一個(gè)新數(shù)據(jù)進(jìn)行 EC 并且存儲(chǔ)。然后構(gòu)建新的索引指向新的數(shù)據(jù),之前的索引分裂成 2 部分。
圖片
這樣做后,我們實(shí)際上建立了一個(gè)基于 EC 數(shù)據(jù)的 Append 引擎。EC 后的數(shù)據(jù),對(duì)應(yīng)的就是 Append 引擎中的 segment。所有的修改采用追加寫的形式,等同于 append 引擎的單路追加寫。
實(shí)際上,CDS 并未從頭開始設(shè)計(jì)一個(gè) EC 系統(tǒng),而是采用了公司內(nèi)成熟的 EC 系統(tǒng) Aries 作為底層儲(chǔ)存介質(zhì),Aries 是百度滄海提供的特別優(yōu)秀的 EC 系統(tǒng)和數(shù)據(jù)底座。寫入 Aries 的數(shù)據(jù)將作為一個(gè) slice 存儲(chǔ),而一個(gè) slice 可以對(duì)應(yīng)到邏輯層的一個(gè) segment。
圖片
我們前面提到了需要處理用戶小寫 EC 的問題。可以采用的一個(gè)方案是建立一個(gè)三副本的存儲(chǔ)層,用來緩存用戶 I/O。當(dāng)用戶寫滿一定規(guī)模的數(shù)據(jù)時(shí)(比如:1GB),將這些數(shù)據(jù) EC 后進(jìn)行存儲(chǔ)。
這樣的好處是所有寫數(shù)據(jù)混在一起進(jìn)行存儲(chǔ),分片的切分可以根據(jù) EC 規(guī)模進(jìn)行選擇,可以做到分片對(duì) I/O 友好。其中,EC 層基本可以假設(shè)分片是固定大小的。
圖片
但是,這么做的缺點(diǎn)也很明顯。數(shù)據(jù)會(huì)被先寫到 3 副本層,再寫到 EC 層。一定會(huì)有多于 4 倍的 I/O 放大。我們也統(tǒng)計(jì)了線上數(shù)據(jù)的寫入量,數(shù)據(jù)量占據(jù)比較多的是大 I/O。這些大 I/O 對(duì) EC 相對(duì)比較友好,可以采用直接 EC 的方式進(jìn)行。
圖片
百度滄海的方案是將大寫和小寫進(jìn)行分別處理。大寫直接進(jìn)行 EC,小寫采用 3 副本形式存儲(chǔ)。
這樣做的好處是,大寫的數(shù)據(jù)不經(jīng)過 3 副本層,規(guī)避了緩存帶來的絕大多數(shù) I/O 放大。3 副本主要存儲(chǔ)小 I/O,因?yàn)檎急刃。詫?duì)成本的壓力增長不是很大,I/O 放大也不是很嚴(yán)重。
圖片
系統(tǒng)實(shí)現(xiàn)時(shí),預(yù)留了 10% 作為 3 副本存儲(chǔ)空間。3 副本也采用 append 引擎進(jìn)行存儲(chǔ)。數(shù)據(jù) compaction 時(shí),直接將數(shù)據(jù)存儲(chǔ)到 EC 層。
這種設(shè)計(jì),當(dāng) 3 副本層空間緊張時(shí),數(shù)據(jù)仍然會(huì)被進(jìn)行 EC 存儲(chǔ),能夠在用戶都是小 I/O 的極端情況下,仍然有不錯(cuò)的成本表現(xiàn)。
圖片
內(nèi)部交流時(shí),經(jīng)常會(huì)被問到數(shù)據(jù)是否會(huì)從 EC 層轉(zhuǎn)移到 3 副本層。如果是同種介質(zhì),我們假設(shè)訪問延時(shí)沒有變化。因此,不將 3 副本層作為緩存層。
圖片
大 I/O 并不是固定大小,系統(tǒng)選擇將大 I/O 直接 EC 的情況下,對(duì)于底層 EC 的存儲(chǔ)引擎有新的要求。它必須能夠處理不同大小的分片。
設(shè)計(jì)難點(diǎn)是,釋放的空間如何被回收利用。圖中給了個(gè)例子,當(dāng)數(shù)據(jù)比空洞大時(shí),無法將數(shù)據(jù)放入;當(dāng)數(shù)據(jù)比空洞小時(shí),造成空間浪費(fèi)。因此,下層存儲(chǔ)應(yīng)該采用能夠很好適應(yīng)變長分片的引擎。
圖片
EC 存儲(chǔ)引擎層仍然采用 append 寫的方式。新數(shù)據(jù) append 寫,緊密排列在存儲(chǔ)系統(tǒng)的后端。這樣,新數(shù)據(jù)的空間分配變的簡單。
相對(duì)于原地寫,append 寫無論是對(duì)于 ssd 還是 hdd,性能都更好,這也為高性能存儲(chǔ)打下了基礎(chǔ)。
圖片
因此,總體架構(gòu)是一個(gè)雙層 append 架構(gòu)。第一層有一個(gè)邏輯的 append 引擎,每個(gè) EC 數(shù)據(jù)對(duì)應(yīng)一個(gè)邏輯 segment。下層物理層存儲(chǔ) EC 的分片,也采用 append 的方式,數(shù)據(jù)只進(jìn)行追加寫。
我們通過兩層架構(gòu)解決了修改放大,大小寫如何 EC 的問題。但是,仍然需要進(jìn)一步提高系統(tǒng)性能,提升用戶體驗(yàn)。
圖片
目前采用 2 層 append 引擎的方式構(gòu)建系統(tǒng)。Append 的性能必然會(huì)對(duì)系統(tǒng)產(chǎn)生比較大的影響。存儲(chǔ)系統(tǒng),一般的瓶頸都是 I/O。Append 引擎天然存在寫放大,主要來源為 compaction。
由于系統(tǒng)總帶寬固定,如果 compaction 占用的帶寬過大,留給用戶使用的帶寬就會(huì)降低,影響用戶體驗(yàn)。
Append 引擎的一個(gè)重要評(píng)價(jià)指標(biāo),就是 I/O 放大,即系統(tǒng)總的物理 I/O 除以用戶的 I/O。
圖片
要實(shí)現(xiàn)比較低的 I/O 放大,我們需要了解用戶數(shù)據(jù)的訪問特征。根據(jù)用戶數(shù)據(jù)的訪問特征,進(jìn)行有效優(yōu)化。
一般來說,用戶的數(shù)據(jù)訪問都存在熱點(diǎn)情況。即最近寫過的數(shù)據(jù),被再次寫的概率更大,也符合齊夫分布的特征。
齊夫分布,如公式所示,r 為訪問頻率的排名。C 和 ? 為常數(shù)。即排名越往后,訪問頻率越低。對(duì)這個(gè)公式同時(shí)取對(duì)數(shù)的情況下,是一個(gè)下降的直線。我們也對(duì)線上數(shù)據(jù)的寫頻率進(jìn)行了統(tǒng)計(jì)。除了長尾外,前半部分排名的數(shù)據(jù),基本符合齊夫分布。
如果按照訪問時(shí)間進(jìn)行統(tǒng)計(jì),那么 1 天內(nèi)有寫的熱數(shù)據(jù),只占總數(shù)據(jù)的 5% 左右。
圖片
既然數(shù)據(jù)有冷熱,那么當(dāng)我們選擇 segment 進(jìn)行 compaction,就可以利用數(shù)據(jù)的這種訪問特點(diǎn)進(jìn)行。
一般的選擇方法是貪心算法,即選擇最空的 segment 進(jìn)行 compaction。如圖中的例子,在貪心算法的情況下,由于 segment B 的空洞率更高,會(huì)選擇 segment B 進(jìn)行 compaction。但是,由于 B 中的數(shù)據(jù)比較新,很有可能是熱數(shù)據(jù),則這些數(shù)據(jù)過很小的一段時(shí)間就可能被覆蓋寫。這次數(shù)據(jù)搬遷就顯得多余。
考慮到 segment A 中數(shù)據(jù)比較老。按照用戶訪問特點(diǎn),更老的數(shù)據(jù)被更新的可能性更小。Segment A 會(huì)被長期占用,空洞空間無法釋放。實(shí)際上,這些空洞更有價(jià)值,因?yàn)橐坏┽尫?,能夠被利用很長時(shí)間。所以,cost-benefit 算法兼顧了空洞率和數(shù)據(jù)年齡。它的 pick 算法如公式所示,其中 u 代表有效數(shù)據(jù)率,age 表示 segment 中最新數(shù)據(jù)的年齡。這樣,空洞率比較高的的 segment 會(huì)被選中,老的 segment 也有大概率被選中,釋放出更有價(jià)值的空洞。
圖片
另外,compaction 的數(shù)據(jù)和用戶的寫入數(shù)據(jù)同樣有不同的冷熱。通常 compaction 的數(shù)據(jù)為長時(shí)間沒有寫到的數(shù)據(jù)。將這些數(shù)據(jù)單獨(dú)分流,能夠形成較為穩(wěn)定的 segment。而用戶的寫入的數(shù)據(jù)短時(shí)間內(nèi)被寫的可能性比較大,也單獨(dú)放置。這樣進(jìn)行分類后,能夠形成一些致密的 segment,存放老數(shù)據(jù)。頻繁的寫入形成一些稀疏的 segment,這些 segment 可以被反復(fù)利用。
如果想要更好的效果,可以將數(shù)據(jù)流劃分更細(xì),更多地減少寫放大。
圖片
我們統(tǒng)計(jì)了線上統(tǒng)計(jì)訪問,進(jìn)行回放,控制不同物理空間使用情況下,驗(yàn)證了寫放大的優(yōu)化效果。
從圖中可以看出,cost-benefit 與貪心算法相比,能夠有效減少寫放大。在高空間占用率的情況下(如 95%),cost-benefit 方式,能夠達(dá)到 1.5 以下的寫放大。而貪心算法則要達(dá)到 4 倍以上。
另外,更多的分流能夠減少寫放大。在貪心算法的情況下,能夠節(jié)省較多的 I/O。cost-benefit 情況下,4 路到 6 路收益不太明顯。
另外可以看出,空間使用率對(duì)寫放大也有影響,即較低的空間使用率的情況下,寫放大更好。這也符合直覺,compaction 越晚發(fā)生,segment 形成的空洞越多。
圖片
對(duì)于多層 append 系統(tǒng),每一層都期望把本層能用的空間盡量用滿,然后再做 compaction。但是這么做會(huì)導(dǎo)致下一層的空間持續(xù)緊張,導(dǎo)致下層寫放大比較嚴(yán)重。例如,如果邏輯層寫的比較滿,遲遲不做 compaction,那么物理層則需要頻繁做 compaction 為邏輯層提供充足寫空間。
系統(tǒng)的整體寫放大,應(yīng)該是每一層的寫放大的乘積。那么,總體寫放大并不是追求單獨(dú)一層低寫放大,而是一個(gè)均衡的寫放大,使得整體寫放大較低。
圖片
因此,我們結(jié)合自己的系統(tǒng)特征,設(shè)計(jì)了一個(gè)均衡 compaction 的點(diǎn)。最上層是用戶數(shù)據(jù)空間,下層是 EC 系統(tǒng)能夠提供的物理空間,中間是寫入 EC 層的數(shù)據(jù)。
我們選擇一個(gè)中間點(diǎn)進(jìn)行 compaction 的選擇,如果這個(gè)點(diǎn)偏左,說明上層數(shù)據(jù)比較致密,空洞較少,則下層進(jìn)行 compaction。如果這個(gè)點(diǎn)偏右,則說明下層致密,上層空洞率較多,觸發(fā)上層的 compaction。
這樣,我們就形成了一個(gè)動(dòng)態(tài)可調(diào)節(jié)的 compaction 點(diǎn),使得上下層的 compaction 都不太大,動(dòng)態(tài)維護(hù)一個(gè)較低的整體 compaction。
圖片
總結(jié)下來,系統(tǒng)有低成本的需求,大規(guī)模場景下多副本由于成本問題,不能滿足需求。因此,我們必要采用糾刪碼的形式組織數(shù)據(jù)。
而糾刪碼本身修改代價(jià)比較大,系統(tǒng)設(shè)計(jì)當(dāng)中,利用追加寫的方式進(jìn)行修改。并且采用大小寫分離的方式存儲(chǔ)數(shù)據(jù)。分離后,大寫部分產(chǎn)生的 EC 數(shù)據(jù)為變長,采用 append 的引擎,為這種變長分片提供更好的空間分配機(jī)制,同時(shí)能夠充分利用硬件追加寫的性能優(yōu)勢。
綜上,百度滄海的塊存儲(chǔ)采用了 2 層 append 方案,規(guī)避了 EC 的修改代價(jià)。通過大小寫分離情況,解決了小寫不適合 EC 的情況。同時(shí)選擇了合適 pick 算法、數(shù)據(jù)分流、合適的 compaction 點(diǎn)的方式,優(yōu)化了系統(tǒng)的寫放大,能夠達(dá)到低成本下較高的系統(tǒng)性能。
圖片
以上是今天分享的全部內(nèi)容。