當前位置:
首頁 > 科技 > 分散式塊存儲的研發如何設計元數據服務?

分散式塊存儲的研發如何設計元數據服務?

本文是這個系列的第一篇,著重介紹相關背景和元數據服務。

一般來說,我們根據存儲的訪問介面以及應用場景,把分散式存儲分為三種類型,包括分散式塊存儲,分散式文件存儲,和分散式對象存儲。

其中,分散式塊存儲的主要應用場景包括:

1.虛擬化:比如像 KVM,VMware,XenServer等Hypervisor,以及像Openstack,AWS 等雲平台。塊存儲在其中的角色是支撐虛擬機中的虛擬盤的存儲。

2.資料庫:比如MySQL,Oracle等。很多 DBA都將資料庫的數據盤運行在一個共享的塊存儲服務上,例如分散式塊存儲。此外也有很多客戶直接把資料庫運行在虛擬機中。

3.容器:容器最近幾年在企業中使用越來越廣泛。一般來說,容器中運行的應用都是無狀態的,但在很多應用場景下,應用也會有數據持久化的需求。應用可以選擇將數據持久化到資料庫中,也可以選擇將數據持久化到一個共享虛擬磁碟上。這個需求對應到Kubernetes 中,就是 Persistent Volume 這個功能。

分散式塊存儲的研發如何設計元數據服務?

今天我將主要圍繞 SmartX 如何打造分散式塊存儲進行介紹。SmartX 從 2013 年成立開始,到目前已經積累了 5 年左右的分散式塊存儲的研發經驗,所以今天我們除了分享 SmartX 如何實現我們自己研發的分散式塊存儲 ZBS 以外,還會詳細介紹我們在分散式塊存儲的研發過程中的一些思考和選擇。此外也將介紹一下我們產品未來的規劃。

分散式塊存儲的研發如何設計元數據服務?

從廣泛意義上講,分散式存儲中通常需要解決三個問題,分別是元數據服務,數據存儲引擎,以及一致性協議。

其中,元數據服務提供的功能一般包括:集群成員管理,數據定址,副本分配,負載均衡,心跳,垃圾回收等等。數據存儲引擎負責解決數據在單機上存儲,以及本地磁碟的管理,磁碟故障處理等等。每一個數據存儲引擎之間是隔離的,在這些隔離的存儲引擎之間,需要運行一個一致性協議,來保證對於數據的訪問可以滿足我們期望的一致性狀態,例如強一致,弱一致,順序一致,線性一致等等。我們根據不同的應用場景,選擇一個適合的一致性協議,這個協議將負責數據在不同的節點之間的同步工作。

有了這三部分,我們基本上就掌握了一個分散式存儲的核心。不同的分散式存儲系統之間的區別,基本也都來自於這三個方面的選擇不同。

接下來我會分別從這三個方面介紹一下我們在做SmartX ZBS 系統設計的時候是怎樣思考的,以及最終決定採用哪種類型的技術和實現方法。

分散式塊存儲的研發如何設計元數據服務?

首先我們來介紹一下元數據服務。我們先來談談我們對元數據服務的需求。

所謂元數據就是『數據的數據』,比如說數據放在什麼位置,集群中有哪些伺服器,等等。如果元數據丟失了,或者元數據服務無法正常工作,那麼整個集群的數據都無法被訪問了。

由於元數據的重要性,所以對元數據的第一個需求就是可靠性。元數據必須是保存多份的,同時元數據服務還需要提供 Failover 的能力。

第二個需求就是高性能。儘管我們可以對 IO 路徑進行優化,使得大部分 IO 請求都不需要訪問元數據服務,但永遠都有一些 IO 請求還是需要修改元數據,比如數據分配等等。為避免元數據操作成為系統性能的瓶頸,元數據操作的響應時間必須足夠短。同時由於分散式系統的集群規模在不斷的擴大,對於元數據服務的並發能力也有一定的要求。

最後一個需求是輕量級。由於我們產品大部分使用場景是私有部署,也就是我們的產品是部署在客戶的數據中心的,且由客戶自己運維,而非我們的運維人員運維。這個場景和很多互聯網公司自己來運維自己的產品是完全不同的場景。所以對於 ZBS 來說,我們更強調整個系統,尤其是元數據服務的輕量級,以及易運維的能力。我們期望元數據服務可以輕量級到可以把元數據服務和數據服務混合部署在一起。同時我們希望大部分的運維操作都可以由程序自動完成,或用戶只需要在界面上進行簡單的操作就可以完成。如果大家了解 HDFS 的話,HDFS 中的元數據服務的模塊叫做 Namenode,這是一個非常重量級的模塊。Namenode 需要被獨立部署在一台物理伺服器上,且對硬體的要求非常高,且非常不易於運維,無論是升級還是主備切換,都是非常重的操作,非常容易因操作問題而引發故障。

以上就是我們對元數據服務的需求。接下來我們來看一下具體有哪些方法可以構造一個元數據服務。

分散式塊存儲的研發如何設計元數據服務?

談到存儲數據,尤其是存儲結構化的數據,我們第一個想到的就是關係型資料庫,例如 MySQL,以及一些成熟的 KV 存儲引擎,例如 LevelDB,RocksDB 等。但這種類型的存儲最大的問題就是無法提供可靠的數據保護和 Failover 能力。LevelDB 和 RocksDB 雖然非常輕量級,但都只能把數據保存在單機上。而儘管 MySQL 也提供一些主備方案,但我們認為 MySQL 的主備方案是一個太過笨重的方案,且缺乏簡易的自動化運維方案,所以並不是一個十分好的選擇。

其次,我們來看一下一些分散式資料庫,例如MongoDB 和 Cassandra。這兩種分散式資料庫都可以解決數據保護和提供 Failover 機制。但是他們都不提供 ACID 機制,所以在上層實現時會比較麻煩,需要額外的工作量。其次就是這些分散式資料庫在運維上也相對複雜,不是很易於自動化運維。

分散式塊存儲的研發如何設計元數據服務?

也有一種選擇是基於 Paxos 或者 Raft 協議自己實現一個框架。但這樣實現的代價非常大,對於一個創業公司不是一個很划算的選擇。並且我們創業的時間是 2013 年,當時 Raft 也只是剛剛提出。

第四種是選擇 Zookeeper。Zookeeper 基於 ZAB 協議,可以提供一個穩定可靠地分散式存儲服務。但 Zookeeper 的最大的問題是能夠存儲的數據容量非常有限。為了提高訪問速度,Zookeeper 把存儲的所有數據都緩存在內存中,所以這種方案導致元數據服務所能支撐的數據規模嚴重受限於伺服器的內存容量,使得元數據服務無法做到輕量級,也無法和數據服務混合部署在一起。

最後還有一種方式是基於Distributed Hash Table(DHT)的方法。這種方法的好處元數據中不需要保存數據副本的位置,而是根據一致性哈希的方式計算出來,這樣就極大地降低了元數據服務的存儲壓力和訪問壓力。但使用DHT 存在的問題,就喪失了對數據副本位置的控制權,在實際生產環境中,非常容易造成集群中的產生數據不均衡的現象。同時在運維過程中,如果遇到需要添加節點,移除節點,添加磁碟,移除磁碟的情況,由於哈希環會發生變化,一部分數據需要重新分布,會在集群中產生不必要的數據遷移,而且數據量往往非常大。而這種於運維操作在一個比較大規模的環境中幾乎每天都會發生。大規模的數據遷移很容易影響到線上的業務的性能,所以DHT 使得運維操作變得非常麻煩。

分散式塊存儲的研發如何設計元數據服務?

以上介紹的方法都存在各種各樣的問題,並不能直接使用。最終 ZBS 選擇了使用 LevelDB(也可以替換成 RocksDB)和 Zookeeper 結合的方式,解決元數據服務的問題。首先,這兩個服務相對來說都非常輕量級;其次 LevelDB 和 Zookeeper 使用在生產中也非常穩定。

我們採用了一種叫做 Log Replication 的機制,可以同時發揮 LevelDB 和 Zookeeper 的優點,同時避開他們自身的問題。

這裡我們簡單的介紹一下Log Replication。簡單來說,我們可以把數據或者狀態看作是一組對數據操作的歷史集合,而每一個操作都可以通過被序列化成Log 記錄下來。如果我們可以拿到所有的 Log,並按照 Log 裡面記錄的操作重複一遍,那麼我們就可以完整的恢複數據的狀態。任何一個擁有Log 的程序都可以通過重放 Log 的方式恢複數據。如果我們對Log 進行複製,實際上也就相當於對數據進行了複製。這就是 Log Replication 最基本的想法。

分散式塊存儲的研發如何設計元數據服務?

我們具體來看一下 ZBS 是如何利用 Zookeeper + LevelDB 完成 Log Replication 操作的。首先,集群中有很多個 Meta Server,每個 Server 本地運行了一個 LevelDB 資料庫。Meta Server 通過 Zookeeper 進行選主,選出一個 Leader 節點對外響應元數據請求,其他的 Meta Server 則進入Standby 狀態。

分散式塊存儲的研發如何設計元數據服務?

當 Leader 節點接收到元數據的更新操作後,會將這個操作序列化成一組操作日誌,並將這組日誌寫入Zookeeper。由於 Zookeeper 是多副本的,所以一旦 Log 數據寫入 Zookeeper,也就意味著 Log 數據是安全的了。同時這個過程也完成了對 Log 的複製。

分散式塊存儲的研發如何設計元數據服務?

當日誌提交成功後,Meta Server 就可以將對元數據的修改同時提交到本地的 LevelDB 中。這裡 LevelDB 中存儲的是一份全量的數據,而不需要以 Log 的形式存儲。

分散式塊存儲的研發如何設計元數據服務?

對於非 Leader 的 Meta Server 節點,會非同步的從 Zookeeper 中拉取 Log,並將通過反序列化,將 Log 轉換成對元數據的操作,再將這些修改操作提交到本地的LevelDB 中。這樣就能保證每一個 Meta Server 都可以保存一個完整的元數據。

分散式塊存儲的研發如何設計元數據服務?

前面提到,由於 Zookeeper 存儲數據的容量受限於內存容量。為了避免 Zookeeper 消耗過多內存,我們對 Zookeeper 中的 Log 定期執行清理。只要 Log 已經被所有的 Meta Server 同步完, Zookeeper 中保存的 Log 就可以被刪除了,以節省空間。通常我們在 Zookeeper 上只保存 1GB 的 Log,已經足夠支撐元數據服務。

分散式塊存儲的研發如何設計元數據服務?

Failover的邏輯也非常簡單。如果 Leader 節點發生故障,其他還存活的的 Meta Server 通過 Zookeeper 再重新進行一次選主,選出一個新的 Meta Leader。這個新的 Leader 將首先從 Zookeeper 上同步所有還未消耗的日誌,並在提交到本地的 LevelDB 中,然後就可以對外提供元數據服務了。

分散式塊存儲的研發如何設計元數據服務?

現在我們總結一下 ZBS 中元數據服務實現的特點。

首先,這個原理非常容易理解,而且實現起來非常簡單。由 Zookeeper 負責選主和 Log Replication,由 LevelDB 負責本地元數據的存儲。背後的邏輯就是儘可能的將邏輯進行拆分,並儘可能的復用已有項目的實現。

其次,速度足夠快。Zookeeper 和 LevelDB 本身的性能都不錯,而且在生產中,我們將 Zookeeper 和 LevelDB 運行在 SSD 上。在實際測試中,對於單次元數據的修改都是在毫秒級完成。在並發的場景下,我們可以對元數據修改的日誌做 Batch,以提高並發能力。

此外,這種方式支持 Failover,而且 Failover 的速度也非常快。Failover 的時間就是選主再加上 Log 同步的時間,可以做到秒級恢復元數據服務。

最後說一下部署。在線上部署的時候,我們通常部署 3 個或 5 個Zookeeper 服務的實例以及至少 3 個 Meta Server 服務的實例,以滿足元數據可靠性的要求。元數據服務對資源消耗都非常小,可以做到和其他服務混合部署。

分散式塊存儲的研發如何設計元數據服務?

以上是一些基本的原理,我們再來看一下 ZBS 內部的對於元數據服務的具體實現。

我們將上述的Log Replication 邏輯封裝在了一個 Log Replication Engine 中,其中包含了選主、向Zookeeper 提交 Log、向 LevelDB 同步數據等操作,進一步簡化開發複雜度。

分散式塊存儲的研發如何設計元數據服務?

在 Log Replication Engine 的基礎之上,我們實現了整個 Meta Sever 的邏輯,其中包含了 Chunk Manager,NFS Manger,iSCSI Manager,Extent Manager 等等很多管理模塊,他們都可以通過 Log Replication Engine,管理特定部分的元數據。RPC 模塊是 Meta Server 對外暴露的介面,負責接收外部的命令,並轉發給對應的 Manager。例如創建/刪除文件,創建/刪除虛擬卷等等。此外,Meta Server 中還包含了一個非常複雜的調度器模塊,裡面包含了各種複雜的分配策略,恢復策略,負載均衡策略,以及心跳,垃圾回收等功能。

以上就是關於元數據服務部分的介紹。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 中國存儲 的精彩文章:

Kafka的存儲機制以及可靠性
故障:一場由虛擬化存儲引發的分散式緩存性能悲劇

TAG:中國存儲 |