一般來(lái)說(shuō),我們根據(jù)存儲(chǔ)的訪(fǎng)問(wèn)接口以及應(yīng)用場(chǎng)景,把分布式存儲(chǔ)分為三種類(lèi)型,包括分布式塊存儲(chǔ),分布式文件存儲(chǔ),和分布式對(duì)象存儲(chǔ)。
其中,分布式塊存儲(chǔ)的主要應(yīng)用場(chǎng)景包括:
1.虛擬化:比如像 KVM,VMware,XenServer等Hypervisor,以及像Openstack,AWS 等云平臺(tái)。塊存儲(chǔ)在其中的角色是支撐虛擬機(jī)中的虛擬盤(pán)的存儲(chǔ)。
2.數(shù)據(jù)庫(kù):比如MySQL,Oracle等。很多 DBA都將數(shù)據(jù)庫(kù)的數(shù)據(jù)盤(pán)運(yùn)行在一個(gè)共享的塊存儲(chǔ)服務(wù)上,例如分布式塊存儲(chǔ)。此外也有很多客戶(hù)直接把數(shù)據(jù)庫(kù)運(yùn)行在虛擬機(jī)中。
3.容器:容器最近幾年在企業(yè)中使用越來(lái)越廣泛。一般來(lái)說(shuō),容器中運(yùn)行的應(yīng)用都是無(wú)狀態(tài)的,但在很多應(yīng)用場(chǎng)景下,應(yīng)用也會(huì)有數(shù)據(jù)持久化的需求。應(yīng)用可以選擇將數(shù)據(jù)持久化到數(shù)據(jù)庫(kù)中,也可以選擇將數(shù)據(jù)持久化到一個(gè)共享虛擬磁盤(pán)上。這個(gè)需求對(duì)應(yīng)到Kubernetes 中,就是 Persistent Volume 這個(gè)功能。
今天我將主要圍繞 SmartX 如何打造分布式塊存儲(chǔ)進(jìn)行介紹。SmartX 從 2013 年成立開(kāi)始,到目前已經(jīng)積累了 5 年左右的分布式塊存儲(chǔ)的研發(fā)經(jīng)驗(yàn),所以今天我們除了分享 SmartX 如何實(shí)現(xiàn)我們自己研發(fā)的分布式塊存儲(chǔ) ZBS 以外,還會(huì)詳細(xì)介紹我們?cè)诜植际綁K存儲(chǔ)的研發(fā)過(guò)程中的一些思考和選擇。此外也將介紹一下我們產(chǎn)品未來(lái)的規(guī)劃。
從廣泛意義上講,分布式存儲(chǔ)中通常需要解決三個(gè)問(wèn)題,分別是元數(shù)據(jù)服務(wù),數(shù)據(jù)存儲(chǔ)引擎,以及一致性協(xié)議。
其中,元數(shù)據(jù)服務(wù)提供的功能一般包括:集群成員管理,數(shù)據(jù)尋址,副本分配,負(fù)載均衡,心跳,垃圾回收等等。數(shù)據(jù)存儲(chǔ)引擎負(fù)責(zé)解決數(shù)據(jù)在單機(jī)上存儲(chǔ),以及本地磁盤(pán)的管理,磁盤(pán)故障處理等等。每一個(gè)數(shù)據(jù)存儲(chǔ)引擎之間是隔離的,在這些隔離的存儲(chǔ)引擎之間,需要運(yùn)行一個(gè)一致性協(xié)議,來(lái)保證對(duì)于數(shù)據(jù)的訪(fǎng)問(wèn)可以滿(mǎn)足我們期望的一致性狀態(tài),例如強(qiáng)一致,弱一致,順序一致,線(xiàn)性一致等等。我們根據(jù)不同的應(yīng)用場(chǎng)景,選擇一個(gè)適合的一致性協(xié)議,這個(gè)協(xié)議將負(fù)責(zé)數(shù)據(jù)在不同的節(jié)點(diǎn)之間的同步工作。
有了這三部分,我們基本上就掌握了一個(gè)分布式存儲(chǔ)的核心。不同的分布式存儲(chǔ)系統(tǒng)之間的區(qū)別,基本也都來(lái)自于這三個(gè)方面的選擇不同。
接下來(lái)我會(huì)分別從這三個(gè)方面介紹一下我們?cè)谧鯯martX ZBS 系統(tǒng)設(shè)計(jì)的時(shí)候是怎樣思考的,以及最終決定采用哪種類(lèi)型的技術(shù)和實(shí)現(xiàn)方法。
首先我們來(lái)介紹一下元數(shù)據(jù)服務(wù)。我們先來(lái)談?wù)勎覀儗?duì)元數(shù)據(jù)服務(wù)的需求。
所謂元數(shù)據(jù)就是『數(shù)據(jù)的數(shù)據(jù)』,比如說(shuō)數(shù)據(jù)放在什么位置,集群中有哪些服務(wù)器,等等。如果元數(shù)據(jù)丟失了,或者元數(shù)據(jù)服務(wù)無(wú)法正常工作,那么整個(gè)集群的數(shù)據(jù)都無(wú)法被訪(fǎng)問(wèn)了。
由于元數(shù)據(jù)的重要性,所以對(duì)元數(shù)據(jù)的第一個(gè)需求就是可靠性。元數(shù)據(jù)必須是保存多份的,同時(shí)元數(shù)據(jù)服務(wù)還需要提供 Failover 的能力。
第二個(gè)需求就是高性能。盡管我們可以對(duì) IO 路徑進(jìn)行優(yōu)化,使得大部分 IO 請(qǐng)求都不需要訪(fǎng)問(wèn)元數(shù)據(jù)服務(wù),但永遠(yuǎn)都有一些 IO 請(qǐng)求還是需要修改元數(shù)據(jù),比如數(shù)據(jù)分配等等。為避免元數(shù)據(jù)操作成為系統(tǒng)性能的瓶頸,元數(shù)據(jù)操作的響應(yīng)時(shí)間必須足夠短。同時(shí)由于分布式系統(tǒng)的集群規(guī)模在不斷的擴(kuò)大,對(duì)于元數(shù)據(jù)服務(wù)的并發(fā)能力也有一定的要求。
最后一個(gè)需求是輕量級(jí)。由于我們產(chǎn)品大部分使用場(chǎng)景是私有部署,也就是我們的產(chǎn)品是部署在客戶(hù)的數(shù)據(jù)中心的,且由客戶(hù)自己運(yùn)維,而非我們的運(yùn)維人員運(yùn)維。這個(gè)場(chǎng)景和很多互聯(lián)網(wǎng)公司自己來(lái)運(yùn)維自己的產(chǎn)品是完全不同的場(chǎng)景。所以對(duì)于 ZBS 來(lái)說(shuō),我們更強(qiáng)調(diào)整個(gè)系統(tǒng),尤其是元數(shù)據(jù)服務(wù)的輕量級(jí),以及易運(yùn)維的能力。我們期望元數(shù)據(jù)服務(wù)可以輕量級(jí)到可以把元數(shù)據(jù)服務(wù)和數(shù)據(jù)服務(wù)混合部署在一起。同時(shí)我們希望大部分的運(yùn)維操作都可以由程序自動(dòng)完成,或用戶(hù)只需要在界面上進(jìn)行簡(jiǎn)單的操作就可以完成。如果大家了解 HDFS 的話(huà),HDFS 中的元數(shù)據(jù)服務(wù)的模塊叫做 Namenode,這是一個(gè)非常重量級(jí)的模塊。Namenode 需要被獨(dú)立部署在一臺(tái)物理服務(wù)器上,且對(duì)硬件的要求非常高,且非常不易于運(yùn)維,無(wú)論是升級(jí)還是主備切換,都是非常重的操作,非常容易因操作問(wèn)題而引發(fā)故障。
以上就是我們對(duì)元數(shù)據(jù)服務(wù)的需求。接下來(lái)我們來(lái)看一下具體有哪些方法可以構(gòu)造一個(gè)元數(shù)據(jù)服務(wù)。
談到存儲(chǔ)數(shù)據(jù),尤其是存儲(chǔ)結(jié)構(gòu)化的數(shù)據(jù),我們第一個(gè)想到的就是關(guān)系型數(shù)據(jù)庫(kù),例如 MySQL,以及一些成熟的 KV 存儲(chǔ)引擎,例如 LevelDB,RocksDB 等。但這種類(lèi)型的存儲(chǔ)最大的問(wèn)題就是無(wú)法提供可靠的數(shù)據(jù)保護(hù)和 Failover 能力。LevelDB 和 RocksDB 雖然非常輕量級(jí),但都只能把數(shù)據(jù)保存在單機(jī)上。而盡管 MySQL 也提供一些主備方案,但我們認(rèn)為 MySQL 的主備方案是一個(gè)太過(guò)笨重的方案,且缺乏簡(jiǎn)易的自動(dòng)化運(yùn)維方案,所以并不是一個(gè)十分好的選擇。
其次,我們來(lái)看一下一些分布式數(shù)據(jù)庫(kù),例如MongoDB 和 Cassandra。這兩種分布式數(shù)據(jù)庫(kù)都可以解決數(shù)據(jù)保護(hù)和提供 Failover 機(jī)制。但是他們都不提供 ACID 機(jī)制,所以在上層實(shí)現(xiàn)時(shí)會(huì)比較麻煩,需要額外的工作量。其次就是這些分布式數(shù)據(jù)庫(kù)在運(yùn)維上也相對(duì)復(fù)雜,不是很易于自動(dòng)化運(yùn)維。
也有一種選擇是基于 Paxos 或者 Raft 協(xié)議自己實(shí)現(xiàn)一個(gè)框架。但這樣實(shí)現(xiàn)的代價(jià)非常大,對(duì)于一個(gè)創(chuàng)業(yè)公司不是一個(gè)很劃算的選擇。并且我們創(chuàng)業(yè)的時(shí)間是 2013 年,當(dāng)時(shí) Raft 也只是剛剛提出。
第四種是選擇 Zookeeper。Zookeeper 基于 ZAB 協(xié)議,可以提供一個(gè)穩(wěn)定可靠地分布式存儲(chǔ)服務(wù)。但 Zookeeper 的最大的問(wèn)題是能夠存儲(chǔ)的數(shù)據(jù)容量非常有限。為了提高訪(fǎng)問(wèn)速度,Zookeeper 把存儲(chǔ)的所有數(shù)據(jù)都緩存在內(nèi)存中,所以這種方案導(dǎo)致元數(shù)據(jù)服務(wù)所能支撐的數(shù)據(jù)規(guī)模嚴(yán)重受限于服務(wù)器的內(nèi)存容量,使得元數(shù)據(jù)服務(wù)無(wú)法做到輕量級(jí),也無(wú)法和數(shù)據(jù)服務(wù)混合部署在一起。
最后還有一種方式是基于Distributed Hash Table(DHT)的方法。這種方法的好處元數(shù)據(jù)中不需要保存數(shù)據(jù)副本的位置,而是根據(jù)一致性哈希的方式計(jì)算出來(lái),這樣就極大地降低了元數(shù)據(jù)服務(wù)的存儲(chǔ)壓力和訪(fǎng)問(wèn)壓力。但使用DHT 存在的問(wèn)題,就喪失了對(duì)數(shù)據(jù)副本位置的控制權(quán),在實(shí)際生產(chǎn)環(huán)境中,非常容易造成集群中的產(chǎn)生數(shù)據(jù)不均衡的現(xiàn)象。同時(shí)在運(yùn)維過(guò)程中,如果遇到需要添加節(jié)點(diǎn),移除節(jié)點(diǎn),添加磁盤(pán),移除磁盤(pán)的情況,由于哈希環(huán)會(huì)發(fā)生變化,一部分?jǐn)?shù)據(jù)需要重新分布,會(huì)在集群中產(chǎn)生不必要的數(shù)據(jù)遷移,而且數(shù)據(jù)量往往非常大。而這種于運(yùn)維操作在一個(gè)比較大規(guī)模的環(huán)境中幾乎每天都會(huì)發(fā)生。大規(guī)模的數(shù)據(jù)遷移很容易影響到線(xiàn)上的業(yè)務(wù)的性能,所以DHT 使得運(yùn)維操作變得非常麻煩。
以上介紹的方法都存在各種各樣的問(wèn)題,并不能直接使用。最終 ZBS 選擇了使用 LevelDB(也可以替換成 RocksDB)和 Zookeeper 結(jié)合的方式,解決元數(shù)據(jù)服務(wù)的問(wèn)題。首先,這兩個(gè)服務(wù)相對(duì)來(lái)說(shuō)都非常輕量級(jí);其次 LevelDB 和 Zookeeper 使用在生產(chǎn)中也非常穩(wěn)定。
我們采用了一種叫做 Log Replication 的機(jī)制,可以同時(shí)發(fā)揮 LevelDB 和 Zookeeper 的優(yōu)點(diǎn),同時(shí)避開(kāi)他們自身的問(wèn)題。
這里我們簡(jiǎn)單的介紹一下Log Replication。簡(jiǎn)單來(lái)說(shuō),我們可以把數(shù)據(jù)或者狀態(tài)看作是一組對(duì)數(shù)據(jù)操作的歷史集合,而每一個(gè)操作都可以通過(guò)被序列化成Log 記錄下來(lái)。如果我們可以拿到所有的 Log,并按照 Log 里面記錄的操作重復(fù)一遍,那么我們就可以完整的恢復(fù)數(shù)據(jù)的狀態(tài)。任何一個(gè)擁有Log 的程序都可以通過(guò)重放 Log 的方式恢復(fù)數(shù)據(jù)。如果我們對(duì)Log 進(jìn)行復(fù)制,實(shí)際上也就相當(dāng)于對(duì)數(shù)據(jù)進(jìn)行了復(fù)制。這就是 Log Replication 最基本的想法。
我們具體來(lái)看一下 ZBS 是如何利用 Zookeeper + LevelDB 完成 Log Replication 操作的。首先,集群中有很多個(gè) Meta Server,每個(gè) Server 本地運(yùn)行了一個(gè) LevelDB 數(shù)據(jù)庫(kù)。Meta Server 通過(guò) Zookeeper 進(jìn)行選主,選出一個(gè) Leader 節(jié)點(diǎn)對(duì)外響應(yīng)元數(shù)據(jù)請(qǐng)求,其他的 Meta Server 則進(jìn)入Standby 狀態(tài)。
當(dāng) Leader 節(jié)點(diǎn)接收到元數(shù)據(jù)的更新操作后,會(huì)將這個(gè)操作序列化成一組操作日志,并將這組日志寫(xiě)入Zookeeper。由于 Zookeeper 是多副本的,所以一旦 Log 數(shù)據(jù)寫(xiě)入 Zookeeper,也就意味著 Log 數(shù)據(jù)是安全的了。同時(shí)這個(gè)過(guò)程也完成了對(duì) Log 的復(fù)制。
當(dāng)日志提交成功后,Meta Server 就可以將對(duì)元數(shù)據(jù)的修改同時(shí)提交到本地的 LevelDB 中。這里 LevelDB 中存儲(chǔ)的是一份全量的數(shù)據(jù),而不需要以 Log 的形式存儲(chǔ)。
對(duì)于非 Leader 的 Meta Server 節(jié)點(diǎn),會(huì)異步的從 Zookeeper 中拉取 Log,并將通過(guò)反序列化,將 Log 轉(zhuǎn)換成對(duì)元數(shù)據(jù)的操作,再將這些修改操作提交到本地的LevelDB 中。這樣就能保證每一個(gè) Meta Server 都可以保存一個(gè)完整的元數(shù)據(jù)。
前面提到,由于 Zookeeper 存儲(chǔ)數(shù)據(jù)的容量受限于內(nèi)存容量。為了避免 Zookeeper 消耗過(guò)多內(nèi)存,我們對(duì) Zookeeper 中的 Log 定期執(zhí)行清理。只要 Log 已經(jīng)被所有的 Meta Server 同步完, Zookeeper 中保存的 Log 就可以被刪除了,以節(jié)省空間。通常我們?cè)?Zookeeper 上只保存 1GB 的 Log,已經(jīng)足夠支撐元數(shù)據(jù)服務(wù)。
Failover的邏輯也非常簡(jiǎn)單。如果 Leader 節(jié)點(diǎn)發(fā)生故障,其他還存活的的 Meta Server 通過(guò) Zookeeper 再重新進(jìn)行一次選主,選出一個(gè)新的 Meta Leader。這個(gè)新的 Leader 將首先從 Zookeeper 上同步所有還未消耗的日志,并在提交到本地的 LevelDB 中,然后就可以對(duì)外提供元數(shù)據(jù)服務(wù)了。
現(xiàn)在我們總結(jié)一下 ZBS 中元數(shù)據(jù)服務(wù)實(shí)現(xiàn)的特點(diǎn)。
首先,這個(gè)原理非常容易理解,而且實(shí)現(xiàn)起來(lái)非常簡(jiǎn)單。由 Zookeeper 負(fù)責(zé)選主和 Log Replication,由 LevelDB 負(fù)責(zé)本地元數(shù)據(jù)的存儲(chǔ)。背后的邏輯就是盡可能的將邏輯進(jìn)行拆分,并盡可能的復(fù)用已有項(xiàng)目的實(shí)現(xiàn)。
其次,速度足夠快。Zookeeper 和 LevelDB 本身的性能都不錯(cuò),而且在生產(chǎn)中,我們將 Zookeeper 和 LevelDB 運(yùn)行在 SSD 上。在實(shí)際測(cè)試中,對(duì)于單次元數(shù)據(jù)的修改都是在毫秒級(jí)完成。在并發(fā)的場(chǎng)景下,我們可以對(duì)元數(shù)據(jù)修改的日志做 Batch,以提高并發(fā)能力。
此外,這種方式支持 Failover,而且 Failover 的速度也非??臁ailover 的時(shí)間就是選主再加上 Log 同步的時(shí)間,可以做到秒級(jí)恢復(fù)元數(shù)據(jù)服務(wù)。
最后說(shuō)一下部署。在線(xiàn)上部署的時(shí)候,我們通常部署 3 個(gè)或 5 個(gè)Zookeeper 服務(wù)的實(shí)例以及至少 3 個(gè) Meta Server 服務(wù)的實(shí)例,以滿(mǎn)足元數(shù)據(jù)可靠性的要求。元數(shù)據(jù)服務(wù)對(duì)資源消耗都非常小,可以做到和其他服務(wù)混合部署。
以上是一些基本的原理,我們?cè)賮?lái)看一下 ZBS 內(nèi)部的對(duì)于元數(shù)據(jù)服務(wù)的具體實(shí)現(xiàn)。
我們將上述的Log Replication 邏輯封裝在了一個(gè) Log Replication Engine 中,其中包含了選主、向Zookeeper 提交 Log、向 LevelDB 同步數(shù)據(jù)等操作,進(jìn)一步簡(jiǎn)化開(kāi)發(fā)復(fù)雜度。
在 Log Replication Engine 的基礎(chǔ)之上,我們實(shí)現(xiàn)了整個(gè) Meta Sever 的邏輯,其中包含了 Chunk Manager,NFS Manger,iSCSI Manager,Extent Manager 等等很多管理模塊,他們都可以通過(guò) Log Replication Engine,管理特定部分的元數(shù)據(jù)。RPC 模塊是 Meta Server 對(duì)外暴露的接口,負(fù)責(zé)接收外部的命令,并轉(zhuǎn)發(fā)給對(duì)應(yīng)的 Manager。例如創(chuàng)建/刪除文件,創(chuàng)建/刪除虛擬卷等等。此外,Meta Server 中還包含了一個(gè)非常復(fù)雜的調(diào)度器模塊,里面包含了各種復(fù)雜的分配策略,恢復(fù)策略,負(fù)載均衡策略,以及心跳,垃圾回收等功能。
以上就是關(guān)于元數(shù)據(jù)服務(wù)部分的介紹。
評(píng)論
查看更多