bigtable原理
數據模型
A Bigtable is a sparse, distributed, persistent multi-dimensional sorted map. The map is indexed by a row key, column key, and a timestamp; each value in the map is an uninterpreted arrays of bytes.
Bigtable是稀疏的、分布式的、持久化的、多維度的、順序的map,我們可以將Bigtable的數據模型抽象為一系列的鍵值對,滿足的映射關系為:key(row:string, column:string, time:int64) -> value(string)。
Bigtable的Key有三維,分別是行鍵(row key)、列鍵(column key)和時間戳(timestamp),行鍵和列鍵都是字節串,時間戳是64位整型。
為了方便管理,列又被分為多個列族(column family,是訪問控制的單元),列鍵按照family:qualifier格式命名的。
行鍵、列鍵和時間戳分別作為table的一級、二級、三級索引,即一個table包含若干個row key,每個row key包含若干個列族,每個列族又包含若干個列,對於具有相同行鍵和列鍵的數據(cell),Bigtable會存儲這個數據的多個版本,這些版本通過時間戳來區分。
如下圖:
該表包含兩個行鍵u1和u2,每個行鍵又含兩個列族User和Social,User列族包含的列鍵有name、email、phone;Social列族包含的列鍵有friend、classmate;
- (u1, name, v1)->Ricky 表示一個鍵值對;
- (u1, email, v1)->ricky@gmail.com 和 (u1, email, v2)->ricky@yahoo.com 是兩個不同的鍵值對。
綜上,傳統的map由一系列鍵值對組成,在Bigtable中,對應的鍵是由多個數據復合而成的,即row key,column key和timestamp。
最后:
Bigtable按照行鍵的字典序存儲數據,因為系統龐大且為分布式,所以排序這個特性會帶來很大的好處,行的空間鄰近性可以確保當我們在掃描表時,我們感興趣的記錄會大概率的匯聚到一起。
Tablet是Bigtable分配和負載均衡的單元,Bigtable的表根據行鍵自動划分為片。最初表都只有一個tablet,但隨着表的不斷增大,原始的tablet自動分割為多個tablet,片的大小控制在100-200MB。
支撐技術
Bigtable的實現依托於Google的幾個基礎組件:
- Google File System(GFS),一個分布式文件系統,用於存儲日志和文件;
- Google Sorted Strings Table(SSTable),一個不可修改的有序鍵值映射表,提供查詢、遍歷的功能;
- Chubby,一個高可靠用於分布式的鎖服務,其目的是解決分布式一致性的問題,通過Paxos算法實現。Chubby用於片定位,片服務器的狀態監控,訪問控制列表存儲等任務。注:Chubby並不是開源的,但 Yahoo!借鑒Chubby的設計思想開發了Zookeeper,並將其開源。
bigtable集群
Bigtable集群包括三個主要部分:
- 供客戶端使用的庫,客戶端需要讀寫數據時,直接與片服務器聯系。因為客戶端並不需要從主服務器獲取片的位置信息,所以大多數客戶端從來不需要訪問主服務器,主服務器的負載一般很輕。
- 主服務器(master server),主服務器負責將片分配給片服務器,監控片服務器的添加和刪除,平衡片服務器的負載,處理表和列族的創建等。注意,主服務器不存儲任何片,不提供任何數據服務,也不提供片的定位信息。
- 片服務器(tablet server),每個片服務器負責一定量的片,處理對片的讀寫請求,以及片的分裂或合並。每個片實際由若干SSTable文件和memtable組成,而且這些SSTable和memtable都是已排序的。片服務器可以根據負載隨時添加和刪除。這里片服務器並不真實存儲數據,而相當於一個連接Bigtable和GFS的代理,客戶端的一些數據操作都通過片服務器代理間接訪問GFS。
注意,主服務器負責將片分配給片服務器,而具體的數據服務則全權由片服務器負責。但是不要誤以為片服務器真的存儲了數據(除了內存中memtable的數據),數據的真實位置只有GFS才知道,主服務器將片分配給片服務器的意思應該是,片服務器獲取了片的所有SSTable文件名,片服務器通過一些索引機制可以知道所需要的數據在哪個SSTable文件,然后從GFS中讀取SSTable文件的數據,這個SSTable文件可能分布在好幾台chunkserver上。
當片服務器啟動時,它會在Chubby的某個特定目錄下創建並獲取一個鎖文件(互斥鎖),這個鎖文件的名稱是唯一表示該tablet server的。master server通過監控這個目錄獲取當前存活着的tablet server的信息。
- 如果tablet server失去了鎖(比如網絡問題),那么tablet server也就不再為對應的tablet服務了。
- 如果鎖文件存在,那么tablet server會主動獲取鎖。
- 如果鎖文件不存在,那么tablet server就永遠不會再服務對應的tablet了,所以tablet server就會kill自己。
- 當tablet server要終止時,它會自己釋放占有的鎖,master server就會把該tablet server上的tablet分配給其它的tablet server。
那么maser server是如何獲知tablet server不再服務了呢?master server會定期輪詢每個tablet server的鎖狀態。如果tablet server報告自己失去了已經失去了鎖,或者master server不能獲取tablet server的狀態,那么master server就會嘗試去獲取tablet server對應的鎖文件。如果master server獲取到了鎖文件,並且Chubby是處於正常工作的狀態的,此時master server就確認tablet server已經無法再提供服務了,master server刪除相應的鎖文件並把tablet server對應的tablet分配給新的tablet server。
如果master server與Chubby之間出現了網絡問題,那么master server也會kill自己。但是這並不會影響tablet與tablet server之間的分配關系。
master server的啟動需要經歷一下幾個階段。
- master server需要從Chubby獲取鎖,這樣可以確保在同一時刻只有一個master server在工作。
- master server掃描Chubby下特定的目錄(即tablet server創建鎖文件的目錄),獲取存活着的tablet server的信息。
- master server與存活着的tablet server通信,獲取已被分配到tablet server的tablet信息。
- master server掃描METADATA tablet,獲取所有的tablet信息,然后把未分配的tablet分配給tablet server。
片的定位
前面提到主服務器不提供片的位置信息,那么客戶端是如何訪問片的呢?Bigtable使用一個類似B+樹的數據結構存儲片的位置信息。
定位系統:
- Chubby file,保存着root tablet的位置。這個Chubby文件屬於Chubby服務的一部分,一旦Chubby不可用,就意味着丟失了root tablet的位置,整個Bigtable也就不可用了。
- root tablet,root tablet其實是元數據表(METADATA table)的第一個分片,它保存着元數據表其它片的位置。root tablet很特別,為了保證樹的深度不變,root tablet從不分裂。
- 其它的元數據片,它們和root tablet一起組成完整的元數據表。每個元數據片都包含了許多用戶片的位置信息。
可以看出整個定位系統其實只是兩部分,一個Chubby文件,一個元數據表。每個分片也都是由專門的片服務器負責,這就是不需要主服務器提供位置信息的原因。客戶端會緩存片的位置信息,如果在緩存里找不到一個片的位置信息,就需要查找這個三層結構了,包括訪問一次Chubby服務,訪問兩次片服務器。
元數據表
元數據表(METADATA table)是一張特殊的表,它被用於數據的定位以及一些元數據服務,不可謂不重要。
- 元數據表的行鍵=片所屬表名的id+片最后一行行鍵而成,所以每個片在元數據表中占據一條記錄(一行),而且行鍵既包含了其所屬表的信息也包含了其所擁有的行的范圍;
- 除了知道元數據表的地址部分是常駐內存以外,還可以發現元數據表有一個列族稱為location,我們已經知道元數據表每一行代表一個片,那么為什么需要一個列族來存儲地址呢?因為每個片都可能由多個SSTable文件組成,列族可以用來存儲任意多個SSTable文件的位置。一個合理的假設就是每個SSTable文件的位置信息占據一列,列名為location:filename。當然不一定非得用列鍵存儲完整文件名,更大的可能性是把SSTable文件名存在值里。獲取了文件名就可以向GFS索要數據了。
- 元數據表不止存儲位置信息,也就是說列族不止location,這些數據暫時不是咱們關心的。
客戶端會緩存tablet的位置信息,客戶端在獲取tablet的位置信息時,會涉及到兩種情況:
- 如果客戶端沒有緩存目標tablet的位置信息,那么就會沿着root tablet定位到最終的tablet,整個過程需要3次網絡往返(round-trips)。
- 如果客戶端緩存了目標tablet的位置信息,但是到了目標tablet后發現原來緩存的tablet位置信息過時了,那么會重新從root tablet開始定位tablet,整個過程需要6個network round-trips。
片的存儲和讀寫
片的數據最終還是寫到GFS里的,片在GFS里的物理形態就是若干個SSTable文件。下圖展示了讀寫操作基本情況
當片服務器收到一個寫請求,片服務器首先檢查請求是否合法。如果合法,先將寫請求提交到日志去,然后將數據寫入內存中的memtable。memtable相當於SSTable的緩存,當memtable成長到一定規模會被凍結,Bigtable隨之創建一個新的memtable,並且將凍結的memtable轉換為SSTable格式寫入GFS,這個操作稱為minor compaction。
當片服務器收到一個讀請求,同樣要檢查請求是否合法。如果合法,這個讀操作會查看所有SSTable文件和memtable的合並視圖,因為SSTable和memtable本身都是已排序的,所以合並相當快。
每一次minor compaction都會產生一個新的SSTable文件,SSTable文件太多讀操作的效率就降低了,所以Bigtable定期執行merging compaction操作,將幾個SSTable和memtable合並為一個新的SSTable。BigTable還有個更厲害的叫major compaction,它將所有SSTable合並為一個新的SSTable。