Bigtable:結構化數據的分佈式存儲系統

相關說明

Bigtable是一個用於管理結構化數據的分佈式存儲系統,其設計目的是爲了通過數千個服務器管理大規模數據。谷歌許多的項目例如,web索引、谷歌地球和谷歌金融都使用了Bigtable來存儲大規模數據。這些應用對Bigtable提出了非常不同的要求,包括數據大小(從URL到網頁到衛星圖像)和延遲要求(從後端批量處理到實時數據服務)。儘管有這些不同的需求,但Bigtable還是爲這些谷歌產品提供了靈活、高性能的解決方案。在論文中,描述了Bigtable提供的簡單數據模型,他爲客戶提供了對數據佈局和格式的動態控制,並描述了Bigtable的設計和實現。

Bigtable實現了四個目標:廣適用性,可擴展性,高性能和高可用性。在很多方面,Bigtable類似於數據庫,它與數據庫共享許多實現策略,並行數據庫和主存儲數據庫實現了可擴展性和高性能,但Bigtable提供了與此類系統不同的接口。Bigtable不支持完整的關係數據模型;相反,他爲客戶提供了一個簡單的數據模型,支持對數據佈局和格式的動態控制,並允許客戶端推斷底層存儲中表示的數據的位置屬性。使用可以是任意字符串的行和列名稱來索引數據。Bigtable還將數據視爲未解釋的字符串,儘管客戶經常講各種形式的結構化和半結構化數據序列化爲這些字符串。客戶可以通過在模式中仔細選擇來控制數據的位置。最後,Bigtable架構參數允許客戶端動態控制從內存還是從磁盤提供數據。

數據結構

Bigtable是稀疏的,分佈式的,持久的多維有序映射。 地圖由行鍵,列鍵和時間戳索引; 映射中的每個值都是未解釋的字節數組。

(row:string, column:string, time:int64) → string

在研究了類Bigtable系統的各種潛在用途後,我們確定了這個數據模型。 作爲推動我們的一些設計決策的一個具體例子,假設我們想要保留許多不同項目可以使用的大量網頁和相關信息的副本; 讓我們將這個特定的表稱爲Webtable。 在Webtable中,我們將URL用作行鍵,將網頁的各個方面用作列名,並將網頁的內容存儲在內容中:在獲取它們的時間戳下的列,如圖所示。

[外鏈圖片轉存失敗(img-xMUmHjYy-1567404854819)(/Users/wuzi/Desktop/bigtable_figure1.png)]

Rows

表中的行鍵是任意字符串(當前最大爲64KB,但對於大多數用戶來說,10-100字節是典型的大小)。 單個行鍵下的每次數據讀取或寫入都是原子的(無論行中讀取或寫入的不同列的數量如何),這一設計決策使客戶端更容易在出現併發時推斷系統的行爲 更新到同一行。

Bigtable按行鍵維護按字典順序排列的數據。 表的行範圍是動態分區的。 每個行範圍稱爲tablet,它是分配和負載平衡的單位。 因此,短行範圍的讀取是有效的,並且通常需要僅與少量機器通信。 客戶端可以通過選擇行鍵來利用此屬性,以便它們獲得數據訪問的良好位置。 例如,在Webtable中,通過反轉URL的主機名組件,將同一域中的頁面組合在一起成爲連續的行。 例如,我們會在關鍵com.google.maps / index.html下存儲maps.google.com/index.html的數據。 將來自同一域的頁面存儲在彼此附近使得一些主機和域分析更有效。

Column Families

列鍵被分組爲稱爲列族的集合,這些集合構成了訪問控制的基本單元。存儲在列族中的所有數據通常具有相同的類型(我們將同一列族中的數據壓縮在一起)。 必須先創建列族,然後才能將數據存儲在該族中的任何列鍵下; 在創建族之後,可以使用族中的任何列鍵。 我們的意圖是表中不同列系列的數量很小(最多數百個),並且系列在運行期間很少發生變化。 相反,表格可能具有無限數量的列。

使用以下語法命名列鍵:family:qualifier。 列族名稱必須是可打印的,但限定符可以是任意字符串。 Webtable的示例列族是語言,它存儲編寫網頁的語言。 我們在語言系列中只使用一個列鍵,它存儲每個網頁的語言ID。 該表的另一個有用的列系列是錨; 此係列中的每個列鍵代表一個錨點,如圖1所示。限定符是引用站點的名稱; 單元格內容是鏈接文本。

訪問控制以及磁盤和內存帳戶都在列族級別執行。 在我們的Webtable示例中,這些控件允許我們管理幾種不同類型的應用程序:一些用於添加新的基礎數據,一些用於讀取基礎數據並創建派生列族,還有一些僅允許查看現有數據(可能不會 甚至出於隱私原因查看所有現有數據)。

Timestamps

Bigtable中的每個單元格都可以包含相同數據的多個版本; 這些版本由時間戳索引。 Bigtable時間戳是64位整數。 它們可以由Bigtable分配,在這種情況下,它們以微秒錶示“實時”,或由客戶端應用程序明確分配。需要避免衝突的應用程序必須自己生成唯一的時間戳。 不同版本的單元以遞減的時間戳存儲,以便可以首先讀取最新版本。

爲了減少對版本化數據的管理,我們支持兩個每列系列設置,告訴Bigtable自動垃圾收集單元版本。 客戶端可以指定僅保留單元的最後n個版本,或者僅保留足夠新版本(例如,僅保留在過去七天中寫入的值)。

在我們的Webtable示例中,我們將存儲在contents:列中的已爬網頁面的時間戳設置爲實際爬網這些頁面版本的時間。 上面描述的垃圾收集機制讓我們只保留每個頁面的最新三個版本。

Building Blocks

Bigtable建立在其他幾個谷歌基礎設施上。 Bigtable使用分佈式Google文件系統(GFS)來存儲日誌和數據文件。Bigtable集羣通常在運行各種其他分佈式應用程序的共享計算機池中運行,而Bigtable進程通常與來自其他應用程序的進程共享相同的計算機。Bigtable依賴於集羣管理系統,用於調度作業,管理共享計算機上的資源,處理計算機故障以及監視計算機狀態。

Google SSTable文件格式在內部用於存儲Bigtable數據。 SSTable提供從鍵到值的持久的,有序的不可變映射,其中鍵和值都是任意字節串。提供操作以查找與指定鍵相關聯的值,並迭代指定鍵範圍內的所有鍵/值對。在內部,每個SSTable都包含一系列塊(通常每個塊的大小爲64KB,但這是可配置的)。 塊索引(存儲在SSTable的末尾)用於定位塊; 打開SSTable時,索引將加載到內存中。可以使用單個磁盤搜索執行查找:我們首先通過在內存索引中執行二進制搜索,然後從磁盤讀取相應的塊來找到適當的塊。 可選地,SSTable可以完全映射到內存中,這使我們可以在不觸摸磁盤的情況下執行查找和掃描。

Bigtable依賴於一種名爲Chubby的高可用性和持久性分佈式鎖定服務。Chubby服務由五個活動副本組成,其中一個被選爲主服務器並主動服務請求。當大多數副本正在運行並且可以相互通信時,該服務是活動的。Chubby使用Paxos算法來保證其副本在失敗時保持一致。 Chubby提供了一個由目錄和小文件組成的命名空間。每個目錄或文件都可以用作鎖,對文件的讀寫是原子的。 Chubby客戶端庫提供了對Chubby文件的一致緩存。每個Chubby客戶端都使用Chubby服務維護會話。 如果客戶端會話無法在租約到期時間內續訂其會話租約,則會話期滿。當客戶端的會話到期時,它會丟失任何鎖定並打開句柄。 Chubby客戶端還可以在Chubby文件和目錄上註冊回調,以通知更改或會話到期。

Bigtable使用Chubby執行各種任務:確保任何時候最多隻有一個活躍的主人;存儲Bigtable數據的引導位置; 發現tablet服務器並最終確定tablet服務器down的數量;存儲Bigtable架構信息(每個表的列族信息); 並存儲訪問控制列表。如果Chubby長時間不可用,Bigtable將無法使用。

實現

Bigtable實現有三個主要組件:鏈接到每個客戶端的庫,一個主服務器和許多tablet服務器。Tablet服務器可以從羣集中動態添加(或刪除)以適應工作負載的變化。

主服務器負責將Tablet分配給Tablet服務器,檢測Tablet服務器的添加和到期,平衡Tablet服務器負載以及GFS中文件的垃圾收集。 此外,它還處理架構更改,例如表和列族創建。

每個Tablet服務器管理一組Tablet(通常我們每臺Tablet服務器有10到1000個Tablet)。 Tablet服務器處理對已加載的Tablet的讀寫請求,並且還會分割已經過大的Tablet。

與許多單主分佈式存儲系統一樣,客戶端數據不會通過主服務器:客戶端直接與Tablet服務器通信以進行讀寫操作。 由於Bigtable客戶端不依賴主服務器來獲取Tablet位置信息,因此大多數客戶端從不與主服務器通信。 結果,主體在實踐中輕載。

Bigtable集羣存儲了許多表。 每個表格由一組Tablet組成,每個Tablet包含與行範圍相關的所有數據。 最初,每個表只包含一個Tablet。 隨着表格的增長,它會自動拆分爲多個Tablet,默認情況下每個大小約爲100-200 MB。

Tablet Location

我們使用類似於B + - 樹[10]的三級層次結構來存儲Tablet位置信息(如圖2)。

[外鏈圖片轉存失敗(img-2fJjJvjd-1567404854820)(/Users/wuzi/Desktop/bigtable_tablet_location.png)]

第一級是存儲在Chubby中的文件,其中包含根Tablet的位置。根Tablet包含特殊METADATA表中所有Tablet的位置。每個METADATA Tablet都包含一組用戶Tablet的位置。根Tablet只是METADATA表中的第一個Tablet,但是經過專門處理 - 它從不拆分 - 以確保Tablet位置層次結構不超過三個級別。

METADATA表將Tablet的位置存儲在行鍵下,該行鍵是Tablet的表標識符及其結束行的編碼。每個METADATA行在內存中存儲大約1KB的數據。對於128 MB METADATA Tablet的適度限制,我們的三級位置方案足以處理234個Tablet(或128 MBTablet中的261個字節)。

客戶端庫緩存Tablet位置。 如果客戶端不知道Tablet的位置,或者緩存位置信息的發送者是否不正確,則它會遞歸地向上移動Tablet位置層次結構。如果客戶端的緩存爲空,則位置算法需要三次網絡往返,包括從Chubby讀取一次。如果客戶端的緩存是陳舊的,則位置算法最多可能需要六次往返,因爲只有在未命中時纔會發現過時的緩存條目(假設METADATA Tablet不會頻繁移動)。

雖然Tablet位置存儲在內存中,因此不需要GFS訪問,但我們通過使用客戶端庫預取Tablet位置來進一步降低此成本:只要讀取METADATA表,它就會讀取多個Tablet的元數據。我們還將輔助信息存儲在METADATA表中,包括每個Tablet所有事件的日誌(例如服務器開始提供時)。 此信息有助於調試和性能分析。

Tablet Assignment

每個Tablet一次分配到一臺Tablet服務器。 主設備會跟蹤Tablet服務器的集合,以及Tablet到Tablet服務器的當前分配,包括哪些Tablet未分配。如果Tablet未分配,並且Tablet服務器具有足夠的Tablet空間,則主計算機會通過向Tablet服務器發送Tablet加載請求來分配Tablet。

Bigtable使用Chubby來跟蹤Tablet服務器。 當Tablet服務器啓動時,它會創建並獲取對特定Chubby目錄中唯一命名文件的獨佔鎖定。主服務器監視此目錄(服務器目錄)以發現Tablet服務器。 如果Tablet服務器失去其獨佔鎖定,它將停止爲其Tablet提供服務:例如,由於網絡分區導致服務器丟失其Chubby的會話。(Chubby提供了一種有效的機制,允許Tablet服務器檢查它是否仍然保持其鎖定而不會產生網絡流量。)只要文件仍然存在,Tablet服務器就會嘗試在其文件上重新獲取一個特定的鎖定。 如果該文件不再存在,則Tablet服務器將永遠無法再次服務,因此它會自行終止。每當Tablet服務器終止時(例如,因爲集羣管理系統正在從集羣中移除Tablet服務器的機器),它就會嘗試釋放其鎖定,以便主服務器更快地重新分配其Tablet。

主負責檢測Tablet服務器何時不再服務其Tablet,以及儘快重新分配這些Tablet。要檢測Tablet服務器何時不再爲其Tablet提供服務,主服務器會定期向每個Tablet服務器詢問其鎖定狀態。如果Tablet服務器報告它已失去鎖定,或者主服務器在最近幾次嘗試期間無法訪問服務器,則主服務器會嘗試獲取服務器文件的獨佔鎖定。如果主服務器能夠獲得鎖定,則Chubby處於活動狀態且Tablet服務器已經死機或無法訪問Chubby,因此主服務器確保Tablet服務器永遠不會通過刪除其服務器文件再次服務。刪除服務器文件後,主服務器可以將之前分配給該服務器的所有Tablet移動到一組未分配的Tablet中。爲了確保Bigtable集羣不會對主服務器和Chubby之間的網絡問題造成漏洞,如果其Chubby會話到期,主服務器將自行終止。 但是,如上所述,主故障不會改變Tablet到Tablet服務器的分配。

當集羣管理系統啓動主服務器時,它需要先發現當前的Tablet分配,然後才能更改它們。 主站在啓動時執行以下步驟。 (1)主機在Chubby中獲取一個唯一的主鎖,這會阻止當前的主實例化。 (2)主服務器掃描Chubby中的服務器目錄以查找實時服務器。 (3)主設備與每個實時Tablet服務器通信,以發現已爲每個服務器分配了哪些Tablet。 (4)主機掃描METADATA表以瞭解Tablet組。 每當此掃描遇到尚未分配的Tablet時,主控制器會將Tablet添加到一組未分配的Tablet,這使Tablet有資格進行Tablet分配。

一個複雜的問題是,在分配METADATA Tablet之前,不能對METADATA表進行掃描。 因此,在開始此掃描(步驟4)之前,如果在步驟3中未發現根Tablet的分配,則主服務器會將根Tablet添加到一組未分配的Tablet。此添加確保將分配根Tablet。 由於根Tablet包含所有METADATA Tablet的名稱,因此主機在掃描根Tablet後會知道所有這些Tablet。

只有在創建或刪除表格時纔會更改現有Tablet的集合,將兩個現有Tablet合併爲一個較大的Tablet,或者將現有Tablet拆分爲兩個較小的Tablet。主能夠跟蹤這些變化,因爲它會啓動除最後一個之外的所有變化。 Tablet拆分是專門處理的,因爲它們是由Tablet服務器啓動的。 Tablet服務器通過在METADATA表中記錄新Tablet的信息來提交拆分。當拆分已經提交時,它通知主。 如果拆分通知丟失(由於Tablet服務器或主服務器已經死亡),主服務器會在請求Tablet服務器加載現在已拆分的Tablet時檢測新Tablet。Tablet服務器將通知主這次拆分,因爲它在METADATA表中找到的Tablet條目將僅指定主機要求加載的Tablet的一部分。

Tablet Serving

平板電腦的持久狀態存儲在GFS中,如圖3所示。

[外鏈圖片轉存失敗(img-sJ2wzSCS-1567404854821)(/Users/wuzi/Desktop/bigtable_tablet_serving.png)]

更新將提交到存儲重做記錄的提交日誌。 在這些更新中,最近提交的更新存儲在稱爲memtable的排序緩衝區的內存中; 較舊的更新存儲在一系列SSTable中。要恢復Tablet,Tablet服務器會從METADATA表中讀取其元數據。 此元數據包含構成Tablet和一組重做點的SSTable列表,這些重做點指向可能包含Tablet數據的任何提交日誌。服務器將SSTable的索引讀入內存,並通過應用自重做點以來已提交的所有更新來重構memtable。

當寫操作到達Tablet服務器時,服務器檢查它是否格式正確,並且發件人有權執行認證。通過從Chubby文件中讀取允許的寫入器列表來執行授權(這幾乎總是在Chubby客戶端緩存中命中)。 將有效的突變寫入提交日誌。組提交用於提高許多小突變的吞吐量。 寫入提交後,其內容將插入到memtable中。

當讀取操作到達Tablet服務器時,類似地檢查其是否良好和正確的授權。在SSTables序列和memtable的合併視圖上執行有效的讀操作。 由於SSTable和memtable是按字典順序排序的數據結構,因此可以有效地形成合並視圖。在分割和合並Tablet時,可以繼續進行讀取和寫入操作。

Compactions

當寫操作執行時,memtable的大小會增加。 當memtable大小達到閾值時,memtable被凍結,創建一個新的memtable,凍結的memtable轉換爲SSTable並寫入GFS。這個壓縮過程有兩個目標:它縮小了Tablet服務器的內存使用量,並減少了恢復期間必須從提交日誌中讀取的數據量(如果此服務器死機)。 在發生壓縮時,可以繼續進行讀寫操作。

每次輕微壓縮都會創建一個新的SSTable。 如果此行爲繼續未檢查,則讀取操作可能需要合併來自任意數量的SSTable的更新。相反,我們通過在後臺定期執行合併壓縮來限制此類文件的數量。合併壓縮讀取一些SSTable和memtable的內容,並寫出一個新的SSTable。 壓縮完成後,可以丟棄輸入SSTable和memtable。

將所有SSTable重寫爲一個SSTable的合併壓縮稱爲主要壓縮。 非主要壓縮產生的SSTables可以包含特殊的刪除條目,這些條目可以抑制仍然存在的舊SSTable中的已刪除數據。另一方面,主要的壓縮產生不包含刪除信息或刪除數據的SSTable。Bigtable通過其所有Tablet進行循環,並定期對其進行重大壓縮。 這些主要的壓縮允許Bigtable回收已刪除數據使用的資源,並允許它確保已刪除的數據及時從系統中消失,這對於存儲敏感數據的服務非常重要。

總結

Bigtable的論文的部分內容,如上所述,基本是英文論文的翻譯過程,大致熟悉了Bigtable所做的事情的主要的流程,有Tablet的分配流程,有Tablet如何保存的流程,有Tablet服務器的工作內容,還有Bigtable的文件單位SSTable的保存過程。基本的屬於一個分佈式,多維的映射表的面向大數據存儲的分佈式結構化數據表。後續內容會分析單機版的leveldb的實現過程,來進一步闡述該思路的實踐過程。由於本人才疏學淺,如有錯誤請批評指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章