雲栖号資訊:【 點選檢視更多行業資訊】
在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!
随着人工智能, IoT 等技術的推廣普及,智能監控,智能制造等新興領域蓬勃發展,湧現出了越來越多的海量非結構化資料存儲需求。舉例來說,服務于公安機關的智能監控程式,不僅需要存儲視訊,而且還需要存儲因使用人臉識别技術而産生的人臉截圖等檔案。這一類的小圖像通常隻有幾個 KiB 到 幾十個 KiB 大小,但是數目巨大,動辄十億甚至百億規模。
這類業務,并不需要檔案系統提供的複雜語義。相反,隻需要存取(PUT / GET)的簡單操作模式,使得這類業務非常适合使用對象存儲。雖然對象存儲相比檔案存儲更擅長處理數目巨大的非結構化資料,但對于十億甚至百億的規模,依然會對現有的系統(如軟體定義存儲中最常用的 Ceph)造成嚴重的性能和可用性的沖擊。本文将介紹如何通過引入 adCache、PhxKV 等自研元件,成功支援百億級别的海量小對象存儲。
基于 Ceph 方案處理海量小對象的問題
為了說明 Ceph 處理小對象合并的問題,我們首先簡要回顧 RADOS Gateway ( RGW, Ceph 的對象存儲接口)的工作原理。 RGW 工作在 Ceph 的底層存儲 RADOS 之上。RADOS 對外提供存取 RADOS Object (為了區分對象存儲,我們把 RADOS Object 稱為 R-obj )的接口。一個 R-obj 除去資料之外,還可以維護一定數量的 KV 形式表示的中繼資料( omap )。RGW 就是利用 RADOS 的接口,建構了對象存儲。其中,一個對象,會有三類與之相關的 R-obj。

如上圖所示,對象存儲中的每個桶,都會有一個對應的 bucket index R-obj,桶中的每一個對象,都會對應該 R-obj 的一條 omap,存儲諸如建立時間,權限資訊等系統中繼資料。由于 R-obj 大小的限制,對象的資料和使用者自定義中繼資料部分,被切割之後存儲在一個 header R-obj 和若幹個 tail R-obj 中。Bucket index R-obj 肩負着索引功能,以及沖突處理的邏輯,是以,每個寫操作都需要操作 bucket index R-obj。
Ceph 底層存儲 RADOS 的一緻性協定,為其處理海量小對象帶來了很大的問題。 RADOS 在維護複制組 ( PG , Placement Group )的一緻性時,要求複制組内所有線上(狀态為 up )的 OSD 都傳回成功,一個 op 才算完成。而 OSD 的狀态,是由 monitor 監控 OSD 逾時,更新視圖并擴散至全叢集的。
在上圖的例子中,OP2 在處理過程中,從 OSD2 發生離線故障,這時,由于從 OSD2 仍然被認為線上,導緻 OP2 被挂起,直到 monitor 更新從 OSD2 的狀态,OP2 才可以被傳回。當 OSD2 從離線狀态重新上線時,會執行修複操作,同步自己和主 OSD 的狀态。
為了修複複制組中離線的 OSD ,重新上線的 OSD 會比對自身和主 OSD 中的 op log ,定位出離線期間發生修改的 R-obj ,并從主 OSD 中複制這些 R-obj 的副本。注意,RADOS 中 op log 的作用隻為定位發生改變的 R-obj ,并不能通過 replay op log 的方式進行資料恢複。是以,發生變化的 R-obj 都需要進行全量修複。即使 bucket index R-obj 隻是在從 OSD2 離線時插入了一條 omap,修複 bucket index R-obj 的過程中,依然需要複制整個 bucket index R-obj 的 omap 清單,且修複過程也會阻塞業務 I/O。
講到此處,我們已經不難看出 RGW 處理海量小對象時,中繼資料處理帶來的問題:
1.OSD 短暫離線,造成 bucket index R-obj 無法通路時,會阻塞業務 I/O。
2.如果離線時間超過了心跳逾時,觸發視圖變更,業務恢複,但是會導緻 OSD 重新上線時全量修複 bucket index R-obj。海量小對象場景下,bucket index R-obj 擁有數量巨大的 omap,修複耗時,進一步增加挂起請求的時間。當網絡不穩定,視圖變更頻繁時,不僅請求挂起,還會産生巨大的不必要修複流量,消耗系統資源。
使用 PhxKV 承載海量小對象
我們已經說明了 RADOS 的一緻性協定和修複機制在海量小對象場景中帶來的問題,接下來,我們将說明深信服企業級分布式存儲 EDS 是如何通過自研的分布式 KV —— PhxKV 來支撐海量小對象的中繼資料存儲。我們首先介紹區分于 RADOS 一緻性協定的 RAFT 協定,之後介紹我們如何以 RAFT 協定為基礎,建構了 PhxKV 分布式 KV 系統。
RAFT 一緻性協定
RAFT 協定有三個重要組成部分:Log,狀态機,和一緻性子產品。一個 RAFT 組中有多個 peer,其中一個為 leader,其他為 followers。
如上圖所示,用戶端将 op 發送至 leader 的一緻性子產品,之後 leader 請求所有的 peer 将該 op append 至 Log 中,當大部分(例如,三個 peer 中的兩個)append 成功時,就可以認為 op 已經 commit,這時,leader 更新狀态機,并傳回請求。至于 followers , leader 會在後續的 op 中訓示其将已經 commit 的 op log 執行,更新狀态機。
當 leader 發生故障,或者 leader 所在分片中的 peer 數少于大部分 peer 數導緻 leader
下台時,RAFT 協定通過各 peer 間心跳的逾時來觸發選主流程,進而進行視圖的變更。
RAFT 協定的具體細節和故障處理方面比較複雜,我們就不在此贅述,有興趣的讀者可以移步去閱讀論文 In Search of an Understandable Consensus Algorithm 。我們在此歸納 RAFT 協定的以下特點:
1.RAFT 協定中,一個 op 成功的條件更容易被滿足:當複制組中大部分節點傳回成功時,一個 op 即被認為處理成功,這一特征,使得某些節點網絡不穩定或者主機重新開機時,I/O 能夠不被阻塞;
2.RAFT 協定中的視圖變更,不需要額外的 monitor 程序來觸發,而是通過複制組内各 peer 心跳逾時觸發的選主操作來進行,視圖變更更加快速。觸發視圖變更的條件更苛刻(單個 follower 離線不會導緻視圖變更),即使極端情況下,也能在較短時間内選出新主,快速恢複業務;
3.RAFT 能夠通過複制和 replay log 來實作不同 peer 間 Log 和狀态機的同步。例如,一個重新上線的 peer 執行的最新 op 為 op10000,此時,leader 執行到 op10003,為了同步狀态,該 peer 隻需從 leader 處複制 op10001 ~ op10003 的 log,并在本地執行,即可和 leader 同步狀态機。增量修複和全量修複相比,可以大幅減少修複時間,并節省大量的網絡和 I/O 資源。
4.承接第一點和第三點,peer 修複的過程對上層透明,并不會阻塞業務 I/O。
PhxKV 架構
接下來,我們介紹如何基于 RAFT 協定建構 PhxKV 系統。
PhxKV 的架構如上圖所示,PhxKV 提供和本地 KV 引擎類似的增删改查和批量操作的同步異步接口。PhxKV 的 key 空間被一緻性哈希映射到若幹個 region 中,各 region 管理的 key 沒有重合,每個 region 對應一個 RAFT 複制組,通過 RAFT 協定維護一緻性。 PhxKV 采用 RocksDB 作為底層引擎,提供本地的 KV 接口。
PhxKV 的主要元件和角色如下所述:
1.KV agent 是 PhxKV 的服務端程序,管理存儲在相同實體媒體上的 regions 。出于對故障域的考慮,不同 KV agent 管理的實體空間處在不同的 SSD 上。相同 region 的不同副本存儲在不同 KV agent 上,并通過 RAFT 協定進行同步。
2.Metadata server 管理中繼資料。通過 KV agent 定時的心跳上報收集 KV agent 和 region 的健康狀況,及時通過心跳回複下達負載均衡和修複操作。出于容錯考慮,metadata server 也有多個程序運作在不同主機上,并通過 RAFT 協定進行同步。
3.用戶端執行 I/O 操作。用戶端通過被動定時心跳和主動心跳從 metadata server 拉取各 Region 的路由資訊,之後,根據路由從對應的 KV Agent 中執行對應的增删改查操作。
PhxKV 針對業務特征,還進行了一系列深度優化:
1.RocksDB 為了提供高性能和高可靠性的寫入操作,在寫操作時,op log 先順序寫入 WAL( write-ahead log )進行持久化,資料部分則隻是在記憶體中更新 memtable,後續才以大塊 I/O 的形式刷入底層 SST 檔案。
事實上,RAFT 協定中的 Log 可以作為狀态機的 WAL 使用。我們通過 RocksDB 的 disable WAL 配置項關閉了狀态機的 WAL,這樣,狀态機的更新隻需操作記憶體,減少了一半的落盤操作,大大降低了時延。不過作為代價,在掉電的情況下重新開機,狀态機會發生資料丢失,我們首先需要利用 RAFT Log 來扮演 WAL,将狀态機恢複至重新開機前的狀态。
2.PhxKV 在進行修複和擴容時,需要将 Region 内的全部資料進行複制和遷移。逐條複制性能較差,我們使用 RockDB 提供的 sst_file_writer 和 IngestExternalFiles 功能,進行整 Region 的批量插入,降低了逐條插入的鎖操作對性能的影響,并消除了一緻性隐患。
3.RocksDB 的删除操作是異步操作,并不能快速回收空間。當某個 KV agent 容量告急,遷移出 region 時,可能很久之後才會見效,影響負載均衡效果。我們深度訂制了 RocksDB,通過改變資料組織形式,使得 Region 的删除操作可以同步快速回收空間。
PhxKV 在 EDS 海量小對象中的角色
除去 RADOS 一緻性和修複帶來的問題,中繼資料規模過大,底層資料的碎片化也是 RGW 面對小對象合并時的棘手問題。 EDS 使用另一元件,分布式緩存(adCache)和 PhxKV 雙劍合璧,一起打造了海量小對象的解決方案。 AdCache 将資料暫時緩存在 SSD 盤上,後續再批量回刷至 RADOS,大幅降低了寫請求的通路時延。
上圖表示了小對象合并架構中的各元件關系。在 Ceph 的原始實作中,RGW 将對象的資料和中繼資料都直接存儲在 RADOS 中,其中中繼資料以鍵值的形式存儲在 R-obj 的 omap 中。RGW 使用 librados 和 RADOS 進行互動。
為了實作上述改造邏輯,我們劫持了 RGW 對資料部分和中繼資料部分的操作。其中,中繼資料部分被重定向至 PhxKV 中。此外,資料操作被重定向至 adCache,adCache 用異步合并回刷的原則,後續對存儲的小對象被進一步聚合後,回刷至底層引擎 RADOS 中。同時,adCache 會生成二級索引,使得可以定位到小對象在合并後存儲的位置和偏移,在資料回刷至 RADOS 時,二級索引也會作為中繼資料被批量寫入到 PhxKV 中。
在采用了上述針對性的優化後,EDS 的小對象寫入性能,相比原生系統有了數量級的飛躍。小對象合并引入的二級索引雖然增加了讀取時的通路路徑,但是由于減少了中繼資料的規模,也變相提升了 RADOS 的通路性能,是以,整體的讀取性能并沒有下降。由于 RAFT 協定更健壯的修複機制,修複時間相比原生系統有成數量級的下降,而且能夠保持業務不中斷,修複過程中幾乎不出現業務性能的下降。
結論
海量小對象場景,是對象存儲的新機會,也為現有架構提出了新的挑戰。深信服 EDS 基于現有的架構,取長補短,合理引用新元件解決關鍵核心問題,并能夠讓老元件 RADOS 繼續發揮特長,為海量小對象的存儲打開了新思路。
作者介紹:
Eddison,從事分布式 KV 資料庫 PhxKV 的研發工作。 香港中文大學博士,研究大資料存儲系統的性能和可靠性,在 USENIX FAST, USENIX ATC 等頂級會議發表多篇學術論文。 從業以來,專注分布式一緻性, KV 引擎,糾删碼等領域,2018 年加入深信服科技。
【雲栖号線上課堂】每天都有産品技術專家分享!
課程位址:
https://yqh.aliyun.com/zhibo立即加入社群,與專家面對面,及時了解課程最新動态!
【雲栖号線上課堂 社群】
https://c.tb.cn/F3.Z8gvnK
原文釋出時間:2020-04-29
本文作者:Eddison
本文來自:“
InfoQ”,了解相關資訊可以關注“
”