Bigtable爲Google設計的一個分佈式結構化數據存儲系統,用來處理Google的海量數據。Google內包括Web索引、Google地球等項目都在使用Bigtable存儲數據。儘管這些應用需求差異很大,但是Bigtable還是提供了一個靈活的、高性能的解決方案。
-----------------------------------------------------------------------------------------------------------------------------------
一、簡介
* 設計目標:可靠的處理PB級別的數據,適用性廣泛、可擴展、高性能和高可用性。
* 很多方面Bigtable和數據庫類似,其也使用了數據庫很多實現策略,但是Bigtable提供了和這些系統完全不同的接口。Bigtable不支持完整的關係數據模型,但爲用戶提供了一種簡單的數據模型,用戶可以動態控制數據的分佈和格式
二、數據模型
* Bigtable是一個稀疏的、分佈式的、持久化存儲的多維排序Map(Key=>Value)。Map的索引(Key)是行關鍵字、列關鍵字和時間戳,Map的值(Value)都是未解析的Byte數組:
- Key (row:string, col:string, time:int64) => Value (string)
* 下圖是Bigtable存儲網頁信息的一個例子:
- 行:"com.cn.www"爲網頁的URL
- 列:"contents:"爲網頁的文檔內容,"anchor:"爲網頁的錨鏈接文本(anchor:爲列族,包含2列cnnsi.com和my.look.ca)
- 時間戳:t3、t5、t6、t8和t9均爲時間戳
1、行
* 行和列關鍵字都爲字符串類型,目前支持最大64KB,但一般10~100個字節就足夠了
* 對同一個行關鍵字的讀寫操作都是原子的,這裏類似Mysql的行鎖,鎖粒度並沒有達到列級別
* Bigtable通過行關鍵字的字典序來組織數據,表中每行都可以動態分區。每個分區叫做一個"Tablet",故Tablet是數據分佈和負載均衡調整的最小單位。這樣做的好處是讀取行中很少幾列數據的效率很高,而且可以有效的利用數據的位置相關性(局部性原理)
2、列族
* 列關鍵字組成的集合叫做"列族",列族是訪問控制的基本單位,存放在同一列族的數據通常都屬於同一類型。
* 一張表列族不能太多(最多幾百個),且很少改變,但列卻可以有無限多
* 列關鍵字的命名語法:列族:限定詞。
* 訪問控制、磁盤和內存的使用統計都是在列族層面進行的
3、時間戳
* 在Bigtable中,表的每個數據項都可包含同一數據的不同版本,不同版本通過時間戳來索引(64位整型,可精確到毫秒)
* 爲了減輕各版本數據的管理負擔,每個列族有2個設置參數,可通過這2個參數可以對廢棄版本數據進行自動垃圾收集,用戶可以指定只保存最後n個版本數據
三、API
* 在表操作方面,提供建表、刪表、建列族、刪列族,以及修改集羣、表和列族元數據(如訪問權限等)等基本API。一個例子:
* 在數據操作方面,提供寫入、刪除、讀取、遍歷等基礎API。一個例子:
* 根據具體需求,Bigtable還開發出支持一些其他的特性,比如:1 支持單行上的事務處理,2 允許把數據項做整數計數器 3 允許用戶在Bigtable服務器地址空間上執行腳本程序
四、基礎構件
* Bigtable是建立在其他幾個Google基礎構件上的,有GFS、SSTable、Chubby等
1、基礎存儲相關
* Bigtable使用GFS存儲日誌文件和數據文件,集羣通常運行在共享機器池(cloud)中,依靠集羣管理系統做任務調度、資源管理和機器監控等
2、數據文件格式相關
* Bigtable的內部儲存文件爲Google SSTable格式的,SSTable是一個持久化、排序的、不可更改的Map結構
* 從內部看,SSTable是一系列的數據塊,並通過塊索引定位,塊索引在打開SSTable時加載到內存中,用於快速查找到指定的數據塊
3、分佈式同步相關
* Bigtable還依賴一個高可用的、序列化的分佈式鎖服務組件Chubby(類zookeeper)。
* Chubby服務維護5個活動副本,其中一個選爲Master並處理請求,並通過Paxos算法來保證副本一致性。另外Chubby提供一個名字空間,提供對Chubby文件的一致性緩存等
* Bigtable使用Chubby來完成幾個任務,比如:1 確保任意時間只有一個活動Master副本,2 存儲數據的自引導指令位置,3 查找Tablet服務器信息等 4 存儲訪問控制列表等
五、實現
* Bigtable包括3個主要的組件:鏈接到用戶程序的庫,1個Master服務器和多個Tablet服務器。Tablet服務器可根據工作負載動態增減
* Master服務器:爲Tablet服務器分配Tablets,對Tablet服務器進行負載均衡,檢測Tablet服務器的增減等
* Tablet服務器:管理一個Tablets集合(十到上千個Tablet),並負責它們的讀寫操作。與一般Single-Master類型的分佈式存儲系統類似,客戶端可直接和Tablet服務器通信並進行讀寫,故Master的負載並不大
* 初始情況下,每個表只含一個Tablet,隨着表數據的增長,它會被自動分割成多個Tablet,使得每個Table一般爲100~200MB
1、Tablet的位置信息
* 我們使用三層的、類B+樹的結構存儲Tablet的位置信息,如下圖所示:
* 第一層爲存儲於Chubby中的Root Tablet位置信息。Root Tablet包含一個MetaData表,MetaData表每個Tablet包含一個用戶Tablet集合
* 在MetaData表內,每個Tablet的位置信息都存儲在一個行關鍵字下,這個行關鍵字由Tablet所在表的標識符和最後一行編碼而成
* MetaData表每一行都存儲約1KB內存數據,即在一個128MB的MetaData表中,採用這種3層存儲結構,可標識2^32個Tablet地址
* 用戶程序使用的庫會緩存Tablet的位置信息,如果某個Tablet位置信息沒有緩存或緩存失效,那麼客戶端會在樹狀存儲結構中遞歸查詢。故通常會通過預取Tablet地址來減少訪問開銷
2、Tablet的分配
* 在任何時刻,一個Tablet只能分配給一個Tablet服務器,這個由Master來控制分配(一個Tablet沒分配,而一個Tablet服務器用足夠空閒空間,則Master會發給該Tablet服務器裝載請求)
* Bigtable通過Chubby跟蹤Tablet服務器的狀態。當Tablet服務器啓動時,會在Chubby註冊文件節點並獲得其獨佔鎖,當Tablet服務器失效或關閉時,會釋放這個獨佔鎖
* 當Tablet服務器不提供服務時,Master會通過輪詢Chubby上Tablet服務器文件鎖的狀態檢查出來,確認後會刪除其在Chubby註冊的節點,使其不再提供服務。最後Master會重新分配這個Tablet服務器上的Tablet到其他未分配的Tablet集合內
* 當集羣管理系統啓動一個Master服務器之後,這個Master會執行以下步驟:
- 1 從Chubby獲取一個唯一的Master鎖,保證Chubby只有一個Master實例
- 2 掃描Chubby上的Tablet文件鎖目錄,獲取當前運行的Tablet服務器列表
- 3 和所有Tablet服務器通信,獲取每個Tablet服務器上的Tablet分配信息
- 4 掃描MetaData表獲取所有Tablet集合,如果發現有還沒分配的Tablet,就會將其加入未分配Tablet集合等待分配
3、Tablet的服務
* 如圖所示,Tablet的持久化狀態信息保存在GFS上。更新操作會提交Redo日誌,更新操作分2類:
- 最近提交的更新操作會存放在一個排序緩存中,稱爲memtable
- 較早提交的更新操作會存放在SSTable中,落地在GFS上
* Tablet的恢復:Tablet服務器從MetaData中讀取這個Tablet的元數據,元數據裏面就包含了組成這個Tablet的SSTable和RedoPoint,然後通過重複RedoPoint之後的日誌記錄來重建(類似Mysql的binlog)
* 對Tablet服務器寫操作:首先檢查操作格式正確性和權限(從Chubby拉取權限列表)。之後有效的寫記錄會提交日誌,也支持批量提交,最後寫入的內容插入memtable內
* 對Tablet服務器讀操作:也首先檢查格式和權限,之後有效的讀操作在一系列SSTable和memtable合併的視圖內執行(都按字典序排序,可高效生成合並視圖)
4、Compactions
* 當memtable增大達到一個門限值時,這個memtable會轉換爲SSTable並創建新的memtable,這個過程稱爲Minor Compaction。
* Minor Compaction過程爲了減少Tablet服務器使用的內存,以及在災難恢復時減少從提交日誌讀取的數據量
* 如果Minor Compaction過程不斷進行下去,SStable數量會過多而影響讀操作合併多個SSTable,所以Bigtable會定期合併SStable文件來限制其數量,這個過程稱爲Major Compaction。
* 除此之外,Major Compaction過程生產的新SStable不會包含已刪除的數據,幫助Bigtable來回收已刪除的資源
六、優化
1、局部性羣族
* 用戶可將多個列族組合成一個局部性羣族,Tablet中每個局部性羣族都會生產一個SSTable,將通常不會一起訪問的分割成不同局部性羣族,可以提高讀取操作的效率
* 此外,可以局部性羣族爲單位專門設定一些調優參數,如是否存儲於內存等
2、壓縮
* 用戶可以控制一個局部性羣族的SSTable是否壓縮
* 很多用戶使用”兩遍可定製“的壓縮方式:第一遍採用Bentley and Mcllroy(大掃描窗口內常見長字符串壓縮),第二遍採用快速壓縮算法(小掃描窗口內重複數據),這種方式壓縮速度達到100~200MB/s,解壓速度達到400~1000MB/s,空間壓縮比達到10:1
3、緩存
* Tablet服務器使用二級緩存策略來提高讀操作性能。兩級的緩存針對性不同:
* 第一級緩存爲掃描緩存:緩存Tablet服務器通過SSTable接口獲取的Key-Value對(時間局部性)
* 第二季緩存爲塊緩存:緩存從GFS讀取的SSTable塊(空間局部性)
4、布隆過濾器
* 一個讀操作必須讀取構成Tablet狀態的所有SSTable數據,故如果這些SSTable不在內存便需多次訪問磁盤
* 我們允許用戶使用一個Bloom過濾器來查詢SStable是否包含指定的行和列數據,付出少量Bloom過濾器內存存儲代價,換來顯著減少訪問磁盤次數
5、Commit日誌實現
* 如果每個Tablet操作的Commit日誌單獨寫一個文件,會導致日誌文件數過多,寫入GFS會產生大量的磁盤Seek操作而產生負面影響
* 優化:設置爲每個Tablet服務器寫一個公共的日誌文件,裏面混合了各個Tablet的修改日誌。
* 這個優化顯著提高普通操作性能,卻讓恢復工作複雜化。當一臺Tablet服務器掛了,需要將其上面的tablet均勻恢復到其他Tablet服務器,則其他服務器都得讀取完整的Commit日誌。爲了避免多次讀Commit日誌,我們將日誌按關鍵字排序(table, row, log_seq),讓同一個Tablet的操作日誌連續存放
6、Tablet恢復提速
* Master轉移Tablet時,源Tablet服務器會對這個Tablet做一次Minor Compaction,減少Tablet服務器日誌文件沒有歸併的記錄,從而減少了恢復時間
7、利用不變性
* 在使用Bigtable時,除了SSTable緩存外其他部分產生的SSTable都是不變的,可以利用這個不變性對系統簡化
七、性能評估
* 實驗設計:N臺Tablet服務器集羣(N=1、50、250、500...),每臺Tablet服務器1G內存,數據寫入一個含1786臺機器的GFS集羣。使用N臺Client產生工作負載,這些機器都連入一個兩層樹狀網絡,根節點帶寬約100~200Gbps。
* 一共有6組基準測試:序列寫、隨機寫、序列讀、隨機讀、隨機讀(內存)和掃描,測試結果如下圖所示:
測試均爲讀/寫1000字節value的數據,圖1顯示了1/50/250/500臺Tablet服務器,每臺服務器的每秒操作次數,圖2曲線顯示隨着Tablet服務器數量增加,所有服務器的每秒操作次數總和
* 對於圖1單個Tablet服務器性能維度,有下面幾個特點:
- 隨機讀性能最慢,這是因爲每個隨機讀操作都要通過網絡從GFS集羣拉回64KB(1塊)數據到Tablet服務器
- 隨機讀(內存)性能很快,因爲這些讀操作的數據都從Tablet服務器的內存讀取
- 序列讀性能好於隨機讀,因爲每次從GFS取出64KB數據,這些數據會緩存,序列讀很多落到同個塊上而減少GFS讀取次數
- 寫操作比讀操作高,因爲寫操作實質上爲Tablet服務器直接把寫入內容追加到Commit日誌文件尾部(隨機寫和序列寫性能相近的原因),最後再採用批量提交的方式寫入GFS
- 掃描的性能最高,因爲Client的每一次RPC調用都會返回大量value數據,抵消了RPC調用消耗
* 對於圖2Tablet服務器集羣性能維度,有下面幾個特點:
- 隨着Tablet服務器的增加,系統整體吞吐量有了夢幻般的增加,之所以會有這樣的性能提升,主要是因爲基準測試的瓶頸是單臺Tablet服務器的CPU
- 儘管如此,性能的增加也不是線性的,這是由於多臺Tablet服務器間負載不均衡造成的
- 隨機讀的性能提升最小,還是由於每個1000字節value的讀操作都會導致一個64KB塊的網絡傳輸,消耗了網絡的共享帶寬
八、實際應用
* 截止到2006年,Google內部一共運行了388個非測試的Bigtable集羣,約24500臺Tablet服務器,這些應用以及應用數據大致如下:
* 如上圖所示,可以瞭解到Google分析,Google地圖,Google個性化查詢等應用的Bigtable使用情況
九、經驗教訓
* 很多類型的錯誤都會導致大型分佈式系統受損,而不僅僅是網絡中斷等“常規”錯誤。我們使用修改協議來解決這些問題(容錯性),如在RPC機制中加入Checksum等
* 需要在徹底瞭解一個新特性如何使用後,再決定添加這個新特性是否是重要的。
* 系統級的監控對Bigtable非常重要,能有效跟蹤集羣狀態、檢查引發集羣高時延的潛在因素等
* 簡單的設計和編碼給維護和調試帶來了巨大的好處