本博客在http://doc001.com/同步更新。
本文主要內(nèi)容翻譯自MySQL開(kāi)發(fā)者Ulf Wendel在PHP Submmit 2013上所做的報(bào)告「Scaling database to million of nodes」。翻譯過(guò)程中沒(méi)有全盤(pán)照搬原PPT,按照自己的理解進(jìn)行了部分改寫(xiě)。水平有限,如有錯(cuò)誤和疏漏,歡迎指正。
本文是系列的第三篇,本系列所有文章如下:
- 百萬(wàn)節(jié)點(diǎn)數(shù)據(jù)庫(kù)擴(kuò)展之道(3): Google Bigtable
Google Bigtable
GFS:概述
GFS(Google file sysem)是構(gòu)建Bigtable的基石,運(yùn)行在通用硬件和Linux之上。對(duì)比于關(guān)系型數(shù)據(jù)庫(kù),通用硬件和Linux等同于磁盤(pán),提供原始存儲(chǔ)資源;GFS等同于文件系統(tǒng),提供文件存取功能;Bigtable當(dāng)然就等同于傳統(tǒng)數(shù)據(jù)庫(kù)本身。
GFS被設(shè)計(jì)用于存儲(chǔ)大文件。每一個(gè)大文件被切割成很多的塊(chunk),存儲(chǔ)在被稱(chēng)之為chunkserver的服務(wù)器上。默認(rèn)情況下,每個(gè)chunk有3個(gè)副本。3副本是很多可靠存儲(chǔ)系統(tǒng)的標(biāo)配,一般都是2個(gè)副本位于同一機(jī)架,1個(gè)副本位于其它機(jī)架。
GFS有一個(gè)master節(jié)點(diǎn),管理chunk的元數(shù)據(jù)信息,供用戶(hù)查詢(xún)文件的位置。因?yàn)樵獢?shù)據(jù)信息尺寸相當(dāng)?shù)男。琺aster將它們都存儲(chǔ)在內(nèi)存中。master通過(guò)心跳包監(jiān)測(cè)chunkserver的活躍狀態(tài),以及了解chunk分布情況。
GFS:讀操作
一個(gè)GFS讀操作分為幾個(gè)步驟:
- 客戶(hù)端向master詢(xún)問(wèn)欲讀取的數(shù)據(jù)的位置信息(在哪個(gè)chunkserver)。
- master返回一個(gè)租約(lease),和數(shù)據(jù)的位置信息。客戶(hù)端緩存該信息,緩存信息可減少master的查詢(xún)壓力。
- 客戶(hù)端聯(lián)系相應(yīng)的chunkserver,提供租約。
- 如果租約有效,chunkserver發(fā)送被請(qǐng)求的文件給客戶(hù)端。
GFS的客戶(hù)端讀到的數(shù)據(jù)可能是一致的,也可能是不一致的。這點(diǎn)在后面會(huì)進(jìn)一步描述。
GFS:寫(xiě)操作
GFS執(zhí)行寫(xiě)操作前,先由master決定所寫(xiě)chunk的主副本(primary)。主副本確保所有副本都以相同的順序接收更新操作。與Dynamo相比,主副本的存在使得GFS的數(shù)據(jù)備份非常簡(jiǎn)單。
GFS采用了一個(gè)弱一致性模型,并發(fā)的寫(xiě)操作之間并不相互隔離。沒(méi)有分布式鎖管理器來(lái)阻止客戶(hù)端并發(fā)寫(xiě)同一個(gè)文件。GFS犧牲了一致性,以換取更高的并發(fā)性能。
GFS:追加操作
GFS提供一個(gè)類(lèi)似于O_APPEND的追加操作,追加到chunk的具體位置由GFS決定。GFS保證數(shù)據(jù)至少會(huì)被原子的寫(xiě)一次。客戶(hù)端在寫(xiě)過(guò)程中遭遇失敗時(shí)會(huì)重試,因而可能導(dǎo)致重復(fù)記錄或空白記錄。客戶(hù)端需要處理這些重復(fù)記錄和空白記錄,不過(guò)GFS已經(jīng)提供了一些常見(jiàn)的處理策略。
追加操作不需要全局鎖。但是,數(shù)據(jù)在不同副本的偏移位置可能不一樣(重復(fù)記錄、空白記錄導(dǎo)致的)。
Chubby:概述
Chubby是Bigtable依賴(lài)的一個(gè)分布式鎖服務(wù),為客戶(hù)端提供粗粒度的、建議性質(zhì)的共享/排它鎖。粗粒度的含義是每個(gè)鎖至少持有數(shù)分鐘。建議性質(zhì)意味著,鎖并不阻止對(duì)真實(shí)資源的訪(fǎng)問(wèn),惡意的客戶(hù)端完全可以忽略鎖的存在,直接訪(fǎng)問(wèn)資源。
出于可擴(kuò)展性考慮,Google部署了很多Chubby單元(cell)。每個(gè)數(shù)據(jù)中心有一個(gè)或多個(gè)單元。每個(gè)單元一般有5個(gè)Chubby副本。副本很少失效,即使失效也可以在數(shù)秒鐘之內(nèi)恢復(fù)。Chubby緩存文件、元數(shù)據(jù)、句柄、鎖,一致的緩存使得一個(gè)Chubby單元能夠及時(shí)處理數(shù)萬(wàn)客戶(hù)端的并發(fā)請(qǐng)求。
Chubby:中心鎖服務(wù)
中心鎖服務(wù)是一個(gè)簡(jiǎn)潔的共享資源并發(fā)解決方案。對(duì)開(kāi)發(fā)者來(lái)說(shuō),使用這種的鎖和使用本地的互斥鎖非常相似,兩者的最大區(qū)別在于前者是基于RPC的。
Chubby:不為事務(wù)設(shè)計(jì)
分布式鎖服務(wù)并不意味著可以輕易地實(shí)現(xiàn)事務(wù)邏輯。Chubby不光沒(méi)有實(shí)現(xiàn)細(xì)粒度鎖,而且需要客戶(hù)端處理很多事情。
原PPT中舉的例子沒(méi)有看得很明白,原文如下:
For example, the application must acquire locks following two-phase locking. Two phase locking says that you have to acquire all locks and then, afterwards, release them. Once you have released a lock you must not acquire a new lock. Here's why, ask yourself what state the other process will observe and base his decisions on:
Acquire('src');
Release('src'); /* other process scheduled to run now */;
Acquire('dest');
Release('dest');
似乎是想說(shuō),因?yàn)樾枰瓦h(yuǎn)程Chubby服務(wù)器交互,Release('src')不能原子地完成,所以,釋放鎖的過(guò)程中,其它進(jìn)程看到的狀態(tài)可能是模棱兩可的。但是這個(gè)會(huì)導(dǎo)致問(wèn)題嗎?Chubby已經(jīng)保證了所有的Acquire要么失敗,要么成功,不存在中間狀態(tài),那么其它進(jìn)程判斷鎖的狀態(tài)的時(shí)候,也應(yīng)該以Chubby為準(zhǔn),那就不存在模棱兩可的問(wèn)題。是這樣嗎?求指教!
Chubby:命名服務(wù)(naming service)
Chubby可以用于存儲(chǔ)應(yīng)用的元信息(例如,配置)。
舉一個(gè)例子,一個(gè)分布式的應(yīng)用需要找到它的master的IP地址,這個(gè)信息存儲(chǔ)在Chubby中。如果Chubby是全球部署的,它存儲(chǔ)在/ls/global/myapp/;如果Chubby是本地部署的,它存儲(chǔ)在/ls/local/myapp/。首先,分布式應(yīng)用程序通過(guò)DNS找到全球Chubby或本地Chubby,然后向Chubby請(qǐng)求相應(yīng)路徑的信息。
Chubby:一致問(wèn)題
Chubby提供中心式的鎖服務(wù),但是Chubby本身不能導(dǎo)致單節(jié)點(diǎn)故障,因此Chubby必然包含多個(gè)節(jié)點(diǎn)。
Fischer-Lynch-Patersion(FLP)不可能結(jié)論證明了,在一個(gè)異步系統(tǒng)中,哪怕只有一個(gè)進(jìn)程不可靠,讓一組進(jìn)程對(duì)一個(gè)值達(dá)成一致都是不可能的。怎樣判斷一個(gè)系統(tǒng)是異步的呢?如果消息傳輸?shù)臅r(shí)間不存在上限,且處理器的速度也不確定,那么,這個(gè)系統(tǒng)是一個(gè)異步系統(tǒng)。在這樣的系統(tǒng)中,無(wú)法區(qū)分兩種情況:機(jī)器崩潰了,或機(jī)器太慢。崩潰的機(jī)器需要運(yùn)行恢復(fù)協(xié)議,而太慢的機(jī)器只能等待。幸運(yùn)的是,就像CAP,該問(wèn)題也有相關(guān)理論和實(shí)踐。其中一個(gè)解決方案就是Paxos協(xié)議家族。
Paxos:概述
Paxos的特點(diǎn)概括如下:
- 安全
- 合法性(non-triviality):只有提議的值會(huì)被學(xué)習(xí)到
- 一致同意性:兩個(gè)節(jié)點(diǎn)不能通過(guò)不同的值
- 可終止性:正常運(yùn)行的節(jié)點(diǎn)最終通過(guò)一個(gè)值
- 容錯(cuò)
- 如果少于一半的節(jié)點(diǎn)失敗,剩余的節(jié)點(diǎn)最終能夠達(dá)成共識(shí)
Paxos:分布式狀態(tài)機(jī)
一個(gè)常用的副本方法是使用副本狀態(tài)機(jī)(replicated state machine)。一個(gè)狀態(tài)機(jī)有一個(gè)明確的起始點(diǎn)。接收到的每一個(gè)輸入經(jīng)過(guò)遷移(transition)和輸出函數(shù),產(chǎn)生一個(gè)新的輸出狀態(tài)。如果以相同的順序接收相同的輸出,同一狀態(tài)機(jī)能夠確保多個(gè)副本產(chǎn)生的輸出一致。
對(duì)于分布式的系統(tǒng)來(lái)說(shuō),每一個(gè)節(jié)點(diǎn)都是一個(gè)狀態(tài)機(jī),所有節(jié)點(diǎn)組成一個(gè)分布式狀態(tài)機(jī)集合。現(xiàn)在的主要問(wèn)題在于,由于分布式環(huán)境固有的不確定性,輸入在傳輸過(guò)程中可能會(huì)丟失、出錯(cuò)、亂序,怎樣才能保證所有的輸入無(wú)差錯(cuò)、順序正確地被處理,是一個(gè)難題。
Paxos:提議號(hào)(sequence number)
在一個(gè)異步系統(tǒng)中,消息到達(dá)的順序可能發(fā)生變化,兩個(gè)消息不保證以發(fā)送順序送達(dá)。這個(gè)特點(diǎn)和副本狀態(tài)機(jī)不兼容,因?yàn)楦北緺顟B(tài)機(jī)要求所有的狀態(tài)機(jī)以相同順序處理消息或輸入,以確保所有的狀態(tài)機(jī)產(chǎn)生相同的狀態(tài)。
為了解決這個(gè)問(wèn)題,Paxos的每一個(gè)提議都包含一個(gè)提議號(hào),所有的消息接收者按照提議號(hào)確定消息順序。
Paxos:樸素算法
我們首先嘗試提出一個(gè)簡(jiǎn)單的算法,稱(chēng)之為「樸素算法」,該算法與二階段提交協(xié)議2PC類(lèi)似。
算法首進(jìn)入提議階段。一個(gè)節(jié)點(diǎn)(即proposer)提議一個(gè)值,并將提議發(fā)送給另外一些節(jié)點(diǎn)(即acceptor)。acceptor可以接受或拒絕提議。它們決定后,將結(jié)果返回給proposer。如果proposer收到的所有結(jié)果都是「接受提議」,那么提議被接受,進(jìn)入提交階段,proposer發(fā)起提交過(guò)程,所有acceptor接到propser指令后更新提議值。只要有一個(gè)acceptor拒絕了提議,提議就不會(huì)被提交。
然而,在這個(gè)過(guò)程中,如果一個(gè)acceptor未能成功返回結(jié)果,該怎么處理?根據(jù)FLP,我們無(wú)法把整個(gè)過(guò)程進(jìn)行下去,因?yàn)闊o(wú)法確定acceptor到底是崩潰了,還是處理速度太慢。前者需要重啟協(xié)議過(guò)程,后者只需要等待。整個(gè)協(xié)議就這樣僵持住了。
參考前面的Dynamo實(shí)現(xiàn),我們很容易想到的一個(gè)變通方案就是,確定一個(gè)最小的acceptor集合,只要該集合的acceptor都接受了提議,整個(gè)提議就可以被提交。
Paxos:多數(shù)法定人數(shù)算法
根據(jù)前面的討論,我們進(jìn)一步提出「多數(shù)法定人數(shù)算法」。
這次的算法要求一個(gè)決議的通過(guò)需要獲得至少(n/2+1)個(gè)accptor的選票(即接受提議),其中,n是accptor的總數(shù)。這種方法的基礎(chǔ)是,任何兩個(gè)提議至少會(huì)有一個(gè)公共的acceptor,該acceptor知曉所有歷史提議的結(jié)果,從而可以確保被接受的提議是不矛盾、合法的。
但是,這種算法還不能處理多個(gè)提議并發(fā)的情況。
想象一個(gè)雙提議并發(fā)的場(chǎng)景。
第一個(gè)proposer提議將值設(shè)為'a',收到該提議的acceptor數(shù)量剛好達(dá)到法定人數(shù),這些accptor決定返回accept('a')。但是很不幸,其中一個(gè)acceptor恰好失效,未來(lái)得及發(fā)出accept('a')。該提議于是成為一個(gè)懸案,proposer無(wú)法做出決定,只能繼續(xù)等待。
同時(shí),第二個(gè)proposer提議將值設(shè)為'b',且該提議者的提議號(hào)比第一個(gè)proposer的提議更大,因此,acceptor們會(huì)欣然接受該提議。超過(guò)法定人數(shù)的acceptor確認(rèn)了該提議,因此,其proposer認(rèn)為提議值通過(guò)。
就在這時(shí)候,第一個(gè)提議的失效acceptor恢復(fù),它的懸而未決的accept('a')也發(fā)出了。終于,第一個(gè)proposer的決議也通過(guò)了。
顯然,現(xiàn)在出現(xiàn)了兩個(gè)矛盾的的決議。
出現(xiàn)這個(gè)問(wèn)題的關(guān)鍵在于,acceptor在一個(gè)提議未達(dá)成結(jié)論之前,貿(mào)然接受了另外一個(gè)矛盾的提議。
Paxos:合法提議算法
Paxos對(duì)上述的算法進(jìn)行改進(jìn),確保只會(huì)出現(xiàn)唯一的正確值。它利用了這個(gè)現(xiàn)象,即任何兩個(gè)提議的法定人數(shù)集合至少有一個(gè)共同節(jié)點(diǎn)。這個(gè)/些共同節(jié)點(diǎn)可以暗示后一個(gè)提議的proposer,告知它已經(jīng)存在一個(gè)決議中的建議值了,在決議達(dá)成之前,不應(yīng)該再提出新的提議。
換一種描述就是:在上一個(gè)提議達(dá)成一致之前,針對(duì)同一個(gè)對(duì)象/數(shù)據(jù)的其它的提議會(huì)被拒絕。
Paxos:協(xié)議描述
經(jīng)過(guò)前面的討論,我們終于可以得到了Paxos算法。算法中,一個(gè)完整的Paxos提議分為3個(gè)階段:
- 建議階段
- 一個(gè)節(jié)點(diǎn)決定發(fā)起提議,成為proposer/leader
- proposer生成一個(gè)比以往提議號(hào)更大的提議號(hào)N,提議號(hào)將被發(fā)送給acceptor
- 一個(gè)acceptor可能接受的提議號(hào)必須比之前看到的都大。如果條件滿(mǎn)足,acceptor設(shè)置本地看到的最大提議號(hào)high=N,回復(fù)(OK,上一次接受的提議號(hào),上一次接受的值);否則acceptor拒絕提議
- 接受階段
- proposer檢查同意提議的acceptor數(shù)量是否達(dá)到法定人數(shù),達(dá)到才能繼續(xù)。否則,提議要么終止(提議號(hào)不大于已知提議號(hào)),要么等待(收到的回復(fù)數(shù)量不足)
- 從acceptor們的回復(fù)中,proposer找到「上一次接受的提議號(hào)」最大的回復(fù),其值為V。如果V=NULL,即該acceptor從未接受過(guò)值,那么proposer可以選擇一個(gè)值,否則,它只能提議相同的值V。設(shè)選定的提議值為V'
- proposer發(fā)送(提議號(hào)N,提議值V')
- 如果high>N,acceptor拒絕V'(在這期間可能有其它提議,所以必須再做一次檢查),否則接受V',設(shè)high=N,上一次接受的提議值v=V',上一次接受的提議號(hào)n=N
- 決定階段
- 如果提議無(wú)法被多數(shù)proposer接受,本次提議延遲,重啟
- 否則,proposer認(rèn)為提議結(jié)束,提議值成功設(shè)定
Paxos被認(rèn)為是解決分布式系統(tǒng)一致性問(wèn)題唯一的正確算法。但是Paxos實(shí)現(xiàn)難度很大,且出現(xiàn)競(jìng)爭(zhēng)的情況下,算法效率低下,收斂速度非常慢。目前,已經(jīng)有很多Paxos的簡(jiǎn)化實(shí)現(xiàn)(Chubby、ZooKeeper等),它們或多或少存在一定的缺陷,但是在實(shí)踐中運(yùn)行得相當(dāng)好。
Bigtable:概述
終于到正題了。
Bigtable是Google公開(kāi)的第一個(gè)面向結(jié)構(gòu)化數(shù)據(jù)的分布式存儲(chǔ)系統(tǒng)。它是一個(gè)海量規(guī)模的鍵值(key-value)存儲(chǔ)系統(tǒng),被應(yīng)用于網(wǎng)頁(yè)索引、Google Analytics、Google Earth等產(chǎn)品。它的數(shù)據(jù)模型是一個(gè)稀疏、分布式、持久化的多維排序映射表(map)。每個(gè)記錄的索引是一個(gè)行健(row key)、一個(gè)列(column)、一個(gè)時(shí)間戳(timestamp),值是一個(gè)二進(jìn)制字符串,即:
(row:string,column:string,timestamp:int64)-->string
行健:按照字典順序排序,key是任意的字符串,最大64KB,一般10~100B。對(duì)每一個(gè)row key數(shù)據(jù)的讀寫(xiě)是串行的,即row是保證事務(wù)一致性的單位。連貫key的row被組織成表,是負(fù)載均衡和數(shù)據(jù)分發(fā)的基本單位。
列:列被組織「列家族(column family)」集合,是基本的訪(fǎng)問(wèn)控制單元。存儲(chǔ)在一個(gè)列家族的數(shù)據(jù)通常是同一類(lèi)型的。一個(gè)表中列家族的數(shù)量應(yīng)該盡可能地少,而且操作中,列家族很少發(fā)生變化。列家族的語(yǔ)法是family:qualifier,名字必須是可打印的,qualifer可以是任何字符串。
時(shí)間戳:一個(gè)行健和一個(gè)列組成一個(gè)單元(cell),cell可以包含同一數(shù)據(jù)的多個(gè)版本,每一個(gè)版本由一個(gè)64位整數(shù)表示的時(shí)間戳區(qū)分。不同的版本按照時(shí)間戳降序排列。為了進(jìn)行垃圾回收,客戶(hù)端可以指定至少保存n個(gè)版本的數(shù)據(jù),或者只包含足夠新的數(shù)據(jù)。
行健也是字符串,最大可達(dá)64KB大小,一般也就10~100B。映射表按照行健字典排序。
結(jié)構(gòu)化數(shù)據(jù)(structured data):具有確定數(shù)據(jù)模型,可以存儲(chǔ)在關(guān)系型數(shù)據(jù)庫(kù)。例如關(guān)系型數(shù)據(jù)庫(kù)中的表。
半結(jié)構(gòu)化數(shù)據(jù)(semi-structured data):具有一定的結(jié)構(gòu)性,但是結(jié)構(gòu)靈活性較大。例如XML數(shù)據(jù)。
非結(jié)構(gòu)化數(shù)據(jù)(unstructured data):沒(méi)有結(jié)構(gòu),無(wú)法直接獲知內(nèi)容。例如視頻、圖片。
Bigtable:數(shù)據(jù)分布
Bigtable動(dòng)態(tài)地將表按照行健范圍分割成更小的子表(tablet),tablet是數(shù)據(jù)分布和負(fù)載均衡的最小單位。每一個(gè)table的大小在100~200MB,存儲(chǔ)在tablet server。通過(guò)仔細(xì)地選擇行健,客戶(hù)端可能使查詢(xún)更加區(qū)域化、分散化。Bigtable的查詢(xún)依賴(lài)行健范圍掃描實(shí)現(xiàn),區(qū)域化能夠在查詢(xún)時(shí)能夠降低行健掃描的范圍,從而減少通信機(jī)器的數(shù)量,提高查詢(xún)效率。
用戶(hù)可以將多個(gè)列家族組織成一個(gè)局部組(locality group)。locality group內(nèi)的數(shù)據(jù)在物理層面上會(huì)被存儲(chǔ)到一起。這類(lèi)似于垂直分區(qū)。
舉一個(gè)例子,一個(gè)客戶(hù)端需要頻繁地訪(fǎng)問(wèn)「anchor:」列家族,且很少同時(shí)訪(fǎng)問(wèn)另外一個(gè)列家族「contents:」。用戶(hù)可以將「anchor:」列家族存儲(chǔ)到一個(gè)locality group。這樣,查詢(xún)的時(shí)候,只會(huì)讀取「anchor:」數(shù)據(jù),而「contents:」不會(huì)被讀取。讀取的數(shù)據(jù)量減少了,查詢(xún)的速度快了。
Bigtable:物理存儲(chǔ)
一個(gè)tablet被存儲(chǔ)為多個(gè)SSTable。一個(gè)SSTable是一個(gè)GFS文件,存儲(chǔ)的是不可變的、排序的key-value鍵值對(duì)。數(shù)據(jù)可以原地刪除,但是不可以原地更新。新的記錄會(huì)寫(xiě)到新的文件。
舊文件會(huì)被按需回收。GFS的副本機(jī)制為Bigtable提供了強(qiáng)一致性保證。
客戶(hù)端可以要求SSTable惰性加載到內(nèi)存中,以提高性能。SSTable的塊大小是可配置的。
在Bigtable中,更新被首先提交到commit log文件中,作為redo記錄。最近的更新被存儲(chǔ)到內(nèi)存memtable,較老的更新被存儲(chǔ)到硬盤(pán)SSTable。
隨著操作的進(jìn)行,memtable的大小開(kāi)始增長(zhǎng)。當(dāng)memtable的大小到達(dá)一個(gè)閾值,memtable凍結(jié),新的memtable創(chuàng)建,凍結(jié)的memtable轉(zhuǎn)化為SSTable,保存到GFS中。這個(gè)過(guò)程稱(chēng)之為次合并(minor compaction)。
次合并的目的有兩個(gè):減少內(nèi)存占用,以及加速服務(wù)器恢復(fù)。每一次次合并都會(huì)創(chuàng)建一個(gè)新的SSTable。這些表周期性的在后臺(tái)執(zhí)行歸并合并,將數(shù)個(gè)SSTable和memtable的內(nèi)容進(jìn)行合并,合并完后,原有的SSTable和memtable被丟棄。
所有SSTable合并成一個(gè)SSTable的過(guò)程稱(chēng)之為主合并(major compaction)。次合并產(chǎn)生的SSTable會(huì)包含一些刪除信息。主合并會(huì)將這些刪除信息去除。
Bigtable:壓縮
Bigtable使用一個(gè)反范式的存儲(chǔ)模式,許多客戶(hù)端使用兩階段壓縮策略壓縮數(shù)據(jù)。給定一個(gè)文本格式的數(shù)據(jù)副本,我們可以使用gzip或其它壓縮算法對(duì)數(shù)據(jù)進(jìn)行壓縮,但是這樣的壓縮率只能達(dá)到1:3,而兩階段壓縮算法可以達(dá)到1:10。
Bigtable的壓縮策略首先對(duì)大塊數(shù)據(jù)進(jìn)行壓縮,其假設(shè)是認(rèn)為大塊數(shù)據(jù)的重復(fù)更常見(jiàn)。通常,這樣的大塊數(shù)據(jù)包含同一數(shù)據(jù)的多個(gè)版本,例如同一個(gè)HTML文件的多個(gè)版本,每個(gè)版本只有細(xì)微區(qū)別。接著,Bigtable進(jìn)行第二輪壓縮,將相似的小塊數(shù)據(jù)進(jìn)行壓縮,每一個(gè)小塊是16KB。
2006年的性能數(shù)據(jù)表明,Bigtable壓縮的吞吐量在100200MB/s,解壓縮的吞吐量在4001000MB/s。
Bigtable:系統(tǒng)視角
每一個(gè)Bigtable集群包括一個(gè)master、許多tablet server、一個(gè)GFS集群和Chubby。客戶(hù)端可以緩存tablet的位置信息,以直接訪(fǎng)問(wèn)tablet server讀寫(xiě)數(shù)據(jù),降低master的負(fù)載。
master只負(fù)責(zé)管理任務(wù):
- 監(jiān)視、管理tablet server
- 分配tablet給tablet server(負(fù)載均衡,故障遷移)
- GFS文件垃圾回收
- 協(xié)調(diào)模式變換,如創(chuàng)建新表、增加或移除列家族
Bigtable:tablet索引
tablet的位置信息存儲(chǔ)在一個(gè)稱(chēng)之為METADATA表的特殊Bigtable系統(tǒng)表中。METADATA表包含每一個(gè)用戶(hù)表的位置信息,通過(guò)二元組(table_id,end_row)索引。客戶(hù)端查詢(xún)METADATA表獲取tablet和tablet server信息,并可以緩存這些信息。為了找到METADATA的root tablet(即第一個(gè)tablet)的位置,客戶(hù)端需要詢(xún)問(wèn)Chubby。
這實(shí)際上是一個(gè)遞歸設(shè)計(jì),表信息本身也存儲(chǔ)在表中。
概括地講,整個(gè)結(jié)構(gòu)類(lèi)似于一個(gè)B+樹(shù)。
- 第一層:root tablet文件,位置信息存儲(chǔ)在Chubby文件中。root tablet維護(hù)METADATA表tablet的索引。實(shí)際上root tablet是METADATA的一部分,是其第一個(gè)tablet。root tablet始終作為一個(gè)整體,不會(huì)被細(xì)分,這保證了B+數(shù)不超過(guò)3級(jí)。
- 第二層:每一個(gè)METADATA tablet存儲(chǔ)用戶(hù)數(shù)據(jù)tablet位置信息,存儲(chǔ)的信息包括tablet的行健范圍、tablet所在的tablet server。每一個(gè)METADATA行消耗近1KB內(nèi)存。假設(shè)每一張METADATA的大小限制為128MB(這是一個(gè)適中的大小),整個(gè)三級(jí)索引可以索引$234$個(gè)tablet(即$(128*1024)2$)。
- 第三層:用戶(hù)數(shù)據(jù)tablet。
Bigtable:tablet分配
每一個(gè)tablet一個(gè)時(shí)刻只能分配給一個(gè)tablet server。msater維護(hù)活躍tablet server的信息,以及每一個(gè)tablet的分配信息。當(dāng)一個(gè)tablet沒(méi)有分配給任何一個(gè)tablet server時(shí),master將為其選擇一個(gè)tablet server。
tablet server的信息是通過(guò)Chubby來(lái)維護(hù)的。當(dāng)一個(gè)tablet server上線(xiàn)時(shí),在一個(gè)特殊的Cubby文件夾下建立一個(gè)唯一的文件,并持有該文件的排它鎖。master監(jiān)視這個(gè)特殊文件夾,以發(fā)現(xiàn)tablet server。如果一個(gè)tablet server因?yàn)榫W(wǎng)絡(luò)等原因失去排它鎖,將不再提供服務(wù)。
master需要探測(cè)tablet server的狀態(tài),在tablet server失效時(shí)要盡快對(duì)其上的tablet進(jìn)行重新分配。為此,master周期性地詢(xún)問(wèn)每個(gè)tablet server,了解它們持有的排它鎖的狀態(tài)。
如果一個(gè)tablet server報(bào)告,它已經(jīng)丟失了鎖;或者,在最近的幾個(gè)周期,master都無(wú)法與tablet server取得聯(lián)系,master就會(huì)自己去嘗試獲取該tablet server的排它鎖。如果master無(wú)法獲得排它鎖,說(shuō)明Chubby不可用,或者master自己出現(xiàn)了問(wèn)題,無(wú)論怎樣,整個(gè)系統(tǒng)都無(wú)法正常工作了。如果master可以獲得這個(gè)鎖,那么,Chubby是可用的,表明tablet server要么已經(jīng)死亡,要么無(wú)法與Chubby通信。主服務(wù)器因此從Chubby中刪除這個(gè)tablet server的文件,從而確保這個(gè)tablet server不再能夠提供服務(wù)。一旦一個(gè)服務(wù)器文件被刪除,master就可以把tablet server上所有的tablet標(biāo)記為未分配狀態(tài),然后將它們分配給其它tablet server。GFS可以保證tablet server下線(xiàn)不會(huì)導(dǎo)致tablet數(shù)據(jù)文件丟失。
為了保證一個(gè)BigTable集群不會(huì)輕易受到master和Chubby之間的網(wǎng)絡(luò)故障的影響,master也需要維持與Chubby的會(huì)話(huà)狀態(tài)。一旦master的Chubby會(huì)話(huà)過(guò)期了,這個(gè)master就會(huì)自殺。
當(dāng)一個(gè)master啟動(dòng)時(shí),它需要恢復(fù)所有的tablet分配關(guān)系,然后才能處理tablet的相關(guān)操作。恢復(fù)過(guò)程包括以下步驟:
- 從Chubby獲得master鎖;
- 掃描Chubby的服務(wù)器文件夾,發(fā)現(xiàn)所有活躍的tablet server;
- 向活躍的tablet server詢(xún)問(wèn)tablet的分配信息,并更新它們的當(dāng)前master信息;
- master掃描METADATA表,了解tablet的集合,從而找到所有未分配的tablet。
已有的tablet在幾種情況下會(huì)發(fā)生變化:表創(chuàng)建/刪除/合并/分裂。除了分裂操作外,所有的操作均由master發(fā)起,只有分裂操作由tablet server發(fā)起。tablet server通過(guò)在METADATA表里提交記錄信息來(lái)分裂表;提交完信息后,通知master。如果此間發(fā)生信息丟失,當(dāng)master請(qǐng)求tablet server加載tablet時(shí)能夠發(fā)現(xiàn)新表。
Bigtable與CAP
Bigtable成功證明了,即使在CAP約束下,同樣可以創(chuàng)建大規(guī)模、高擴(kuò)展的分布式系統(tǒng)。實(shí)際上,Bigtable考慮了很多故障因素,不局限于網(wǎng)絡(luò)分區(qū):
- 內(nèi)存和網(wǎng)絡(luò)故障
- 時(shí)鐘不同步
- 服務(wù)器宕機(jī)
- 軟件故障、管理節(jié)點(diǎn)不可用
- 網(wǎng)絡(luò)分區(qū)
Bigtable是強(qiáng)一致的,使用GFS,行操作是原子的。其可用性取決于Chubby,而Chubby的可用性經(jīng)測(cè)量在99.99%(最低)到99.995%(平均)之間。Chubby會(huì)話(huà)幫助系統(tǒng)快速檢測(cè)到網(wǎng)絡(luò)分區(qū),Paxos可以用于主選舉。狀態(tài)存儲(chǔ)在Chubby和GFS中,這使得系統(tǒng)組件的重啟非常迅速。整體上,Bigtable更傾向于CP。
Bigtable相關(guān)開(kāi)源系統(tǒng)
-
HBase
HBase是Bigtable的開(kāi)源實(shí)現(xiàn),構(gòu)建在Hadoop之上。其中,Hadoop提供MapReduce和HDFS分布式文件系統(tǒng);ZooKeeper類(lèi)似Chubby,提供分布式配置管理、一致性、訂閱消息系統(tǒng);Chukwa提供實(shí)時(shí)監(jiān)控;Hive和Pig提供集群搜索的額外支持,其中Hive的查詢(xún)語(yǔ)言HiveQL實(shí)現(xiàn)了SQL92的一個(gè)子集。
-
Facebook Presto
Facebook最近宣布了一個(gè)面向ad-hoc數(shù)據(jù)分析的SQL引擎Presto,該引擎可以工作在HBase之上。Presto號(hào)稱(chēng)CPU效率和性能要高于Hive。Hive最初也是Facebook開(kāi)發(fā)的。
Hive使用MapReduce作業(yè)。MapReduce批作業(yè)順序地執(zhí)行,將中間結(jié)果保存到硬盤(pán)上。而在Presto中,必要時(shí)會(huì)創(chuàng)建Java JVM字節(jié)碼,通過(guò)wroker以流水線(xiàn)的方式處理作業(yè),中間結(jié)果盡可能地存儲(chǔ)在內(nèi)存中。
-
Cloudera Impala
Impala是根據(jù)Google Dremel論文實(shí)現(xiàn)的分布式查詢(xún)引擎,工作于HDFS和HBase之上。
總結(jié)
Bigtable及其類(lèi)似系統(tǒng)的數(shù)據(jù)模型有以下特點(diǎn):
- 鍵值存儲(chǔ),(string, column, timestamp) > (binary)
- 存儲(chǔ)自解釋的二進(jìn)制大文件(basic large object,BLOB)
- 基本的掃描操作、鍵值查詢(xún)、MapReduce
- 切片、垂直分區(qū)
- 通過(guò)行健、locality group的選取可以影響數(shù)據(jù)存儲(chǔ)物理位置
Bigtable及其類(lèi)似系統(tǒng)適用于大數(shù)據(jù)處理,不適用于事務(wù)。
未完待續(xù)...
下一部分將介紹另外一種重要的Google Spanner,參見(jiàn):