翻译:Google大表(BigTable)
生活随笔
收集整理的這篇文章主要介紹了
翻译:Google大表(BigTable)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
翻譯:Google大表(BigTable) 大表(Bigtable):結構化數據的分布存儲系統 [url]http://labs.google.com/papers/bigtable-osdi06.pdf[/url]
{中是譯者評論,程序除外} {本文的翻譯可能有不準確的地方,詳細資料請參考原文.}
摘要
bigtable是設計來分布存儲大規模結構化數據的,從設計上它可以擴展到上2^50字節,分布存儲在幾千個普通服務器上.Google的很多項目使用BT來存儲數據,包括網頁查詢,google earth和google金融.這些應用程序對BT的要求各不相同:數據大小(從URL到網頁到衛星圖象)不同,反應速度不同(從后端的大批處理到實時數據服務).對于不同的要求,BT都成功的提供了靈活高效的服務.在本文中,我們將描述BT的數據模型.這個數據模型讓用戶動態的控制數據的分布和結構.我們還將描述BT的設計和實現. 1.介紹 在過去兩年半里,我們設計,實現并部署了BT.BT是用來分布存儲和管理結構化數據的.BT的設計使它能夠管理2^50 bytes(petabytes)數據,并可以部署到上千臺機器上.BT完成了以下目標:應用廣泛,可擴展,高性能和高可用性(high availability). 包括google analytics, google finance, orkut, personalized search, writely和google earth在內的60多個項目都使用BT.這些應用對BT的要求各不相同,有的需要高吞吐量的批處理,有的需要快速反應給用戶數據.它們使用的BT集群也各不相同,有的只有幾臺機器,有的有上千臺,能夠存儲2^40字節(terabytes)數據. BT在很多地方和數據庫很類似:它使用了很多數據庫的實現策略.并行數據庫[14]和內存數據庫[13]有可擴展性和高性能,但是BT的界面不同.BT不支持完全的關系數據模型;而是為客戶提供了簡單的數據模型,讓客戶來動態控制數據的分布和格式{就是只存儲字串,格式由客戶來解釋},并允許客戶推斷底層存儲數據的局部性{以提高訪問速度}.數據下標是行和列的名字,數據本身可以是任何字串.BT的數據是字串,沒有解釋{類型等}.客戶會在把各種結構或者半結構化的數據串行化{比如說日期串}到數據中.通過仔細選擇數據表示,客戶可以控制數據的局部化.最后,可以使用BT模式來控制數據是放在內存里還是在硬盤上.{就是說用模式,你可以把數據放在離應用最近的地方.畢竟程序在一個時間只用到一塊數據.在體系結構里,就是:locality, locality, locality} 第二節描述數據模型細節.第三節關于客戶API概述.第四節簡介BT依賴的google框架.第五節描述BT的實現關鍵部分.第6節敘述提高BT性能的一些調整.第7節提供BT性能的數據.在第8節,我們提供BT的幾個使用例子,第9節是經驗教訓.在第10節,我們列出相關研究.最后是我們的結論. 2.數據模型
BT是一個稀疏的,長期存儲的{存在硬盤上},多維度的,排序的映射表.這張表的索引是行關鍵字,列關鍵字和時間戳.每個值是一個不解釋的字符數組.{數據都是字符串,沒類型,客戶要解釋就自力更生吧}. (row:string, column:string,time:int64)->string {能編程序的都能讀懂,不翻譯了} //彼岸翻譯的第二節 我們仔細查看過好些類似bigtable的系統之后定下了這個數據模型。舉一個具體例子(它促使我們做出某些設計決定), 比如我們想要存儲大量網頁及相關信息,以用于很多不同的項目;我們姑且叫它Webtable。在Webtable里,我們將用URL作為行關鍵字,用網頁的某些屬性作為列名,把網頁內容存在contents:列中并用獲取該網頁的時間戳作為標識,如圖一所示。 圖一:一個存儲Web網頁的范例列表片斷。行名是一個反向URL{即com.cnn.www}。contents列族{原文用 family,譯為族,詳見列族}存放網頁內容,anchor列族存放引用該網頁的錨鏈接文本。CNN的主頁被Sports Illustrater{即所謂SI,CNN的王牌體育節目}和MY-look的主頁引用,因此該行包含了名叫“anchor:cnnsi.com”和 “anchhor:my.look.ca”的列。每個錨鏈接只有一個版本{由時間戳標識,如t9,t8};而contents列則有三個版本,分別由時間 戳t3,t5,和t6標識。 行 表中的行關鍵字可以是任意字符串(目前支持最多64KB,多數情況下10-100字節足夠了)。在一個行關鍵字下的每一個讀寫操作都是原子操作(不管讀寫這一行里多少個不同列),這是一個設計決定,這樣在對同一行進行并發操作時,用戶對于系統行為更容易理解和掌控。 Bigtable通過行關鍵字的字典序來維護數據。一張表可以動態劃分成多個連續行。連續行在這里叫做“子表”{tablet},是數據分布和負載均衡的單位。這樣一來,讀較少的連續行就比較有效率,通常只需要較少機器之間的通信即可。用戶可以利用這個屬性來選擇行關鍵字,從而達到較好數據訪問地域性{locality}。舉例來說,在Webtable里,通過反轉URL中主機名的方式,可以把同一個域名下的網頁組織成連續行。具體來說,可以把maps.google.com/index.html中的數據存放在關鍵字com.google.maps/index.html下。按照相同或屬性相近的域名來存放網頁可以讓基于主機和基于域名的分析更加有效。 列族 一組列關鍵字組成了“列族”,這是訪問控制的基本單位。同一列族下存放的所有數據通常都是同一類型(同一列族下的數據可壓縮在一起)。列族必須先創建,然后在能在其中的列關鍵字下存放數據;列族創建后,族中任何一個列關鍵字均可使用。我們希望,一張表中的不同列族不能太多(最多幾百個),并且列族在運作中絕少改變。作為對比,一張表可以有無限列。 列關鍵字用如下語法命名:列族:限定詞。 列族名必須是看得懂{printable}的字串,而限定詞可以是任意字符串。比如,Webtable可以有個列族叫language,存放撰寫網頁的語言。我們在language列族中只用一個列關鍵字,用來存放每個網頁的語言標識符。該表的另一個有用的列族是anchor;給列族的每一個列關鍵字代表一個錨鏈接,如圖一所示。而這里的限定詞則是引用該網頁的站點名;表中一個表項存放的是鏈接文本。 訪問控制,磁盤使用統計,內存使用統計,均可在列族這個層面進行。在Webtable舉例中,我們可以用這些控制來管理不同應用:有的應用添加新的基本數據,有的讀取基本數據并創建引申的列族,有的則只能瀏覽數據(甚至可能因為隱私權原因不能瀏覽所有數據)。 時間戳 Bigtable表中每一個表項都可以包含同一數據的多個版本,由時間戳來索引。Bigtable的時間戳是64位整型。可以由Bigtable來賦值,表示準確到毫秒的“實時”;或者由用戶應用程序來賦值。需要避免沖突的應用程序必須自己產生具有唯一性的時間戳。不同版本的表項內容按時間戳倒序排列,即最新的排在前面。 為了簡化對于不同數據版本的數據的管理,我們對每一個列族支持兩個設定,以便于Bigtable對表項的版本自動進行垃圾清除。用戶可以指明只保留表項的最后n個版本,或者只保留足夠新的版本(比如,只保留最近7天的內容)。 在Webtable舉例中,我們在contents:列中存放確切爬行一個網頁的時間戳。如上所述的垃圾清除機制可以讓我們只保留每個網頁的最近三個版本。 //我開始翻譯3,4節? 3.API
BT的API提供了建立和刪除表和列族的函數.還提供了函數來修改集群,表和列族的元數據,比如說訪問權限. // Open the table
Table *T = OpenOrDie(”/bigtable/web/webtable”);
// Write a new anchor and delete an old anchor
RowMutation r1(T, “com.cnn.www”);
r1.Set(”anchor:[url]www.c-span.org[/url]”, “CNN”);
r1.Delete(”anchor:[url]www.abc.com[/url]”);
Operation op;
Apply(&op, &r1);
圖 2: 寫入Bigtable. 在BT中,客戶應用可以寫或者刪除值,從每個行中找值,或者遍歷一個表中的數據子集.圖2的C++代碼是使用RowMutation抽象表示來進行一系列的更新(為保證代碼精簡,沒有包括無關的細節).調用Apply函數,就對Webtable進行了一個原子修改:它為[url]http://www.cnn.com/[/url]增加了一個錨點,并刪除了另外一個錨點. Scanner scanner(T);
ScanStream *stream;
stream = scanner.FetchColumnFamily(”anchor”);
stream->SetReturnAllVersions();
scanner.Lookup(”com.cnn.www”);
for (; !stream->Done(); stream->Next()) {
printf(”%s %s %lld %s\n”,
scanner.RowName(),
stream->ColumnName(),
stream->MicroTimestamp(),
stream->Value());
}
圖3: 從Bigtable讀數據. 圖3的C++代碼是使用Scanner抽象來遍歷一個行內的所有錨點.客戶可以遍歷多個列族.有很多方法可以限制一次掃描中產生的行,列和時間戳.例如,我們可以限制上面的掃描,讓它只找到那些匹配正則表達式*.cnn.com的錨點,或者那些時間戳在當前時間前10天的錨點. BT還支持其他一些更復雜的處理數據的功能.首先,BT支持單行處理.這個功能可以用來對存儲在一個行關鍵字下的數據進行原子的讀-修改-寫操作.BT目前不支持跨行關鍵字的處理,但是它有一個界面,可以用來讓客戶進行批量的跨行關鍵字處理操作.其次,BT允許把每個表項用做整數記數器.最后,BT支持在服務器的地址空間內執行客戶端提供的腳本程序.腳本程序的語言是google開發的Sawzall[28]數據處理語言.目前,我們基于的Sawzall的API還不允許客戶腳本程序向BT內寫數據,但是它允許多種形式的數據變換,基于任何表達式的過濾和通過多種操作符的摘要. BT可以和MapReduce[12]一起使用.MapReduce是google開發的大規模并行計算框架.我們為編寫了一套外層程序,使BT可以作為MapReduce處理的數據源頭和輸出結果. 4.建立BT的基本單元
BT是建立在其他數個google框架單元上的.BT使用google分布式文件系統(GFS)[17]來存儲日志和數據文件{yeah, right, what else can it use, FAT32?}.一個BT集群通常在一個共享的機器池中工作,池中的機器還運行其他的分布式應用{雖然機器便宜的跟白菜似的,可是一樣要運行多個程序,命苦的象小白菜},BT和其他程序共享機器{BT的瓶頸是IO/內存,可以和CPU要求高的程序并存}.BT依賴集群管理系統來安排工作,在共享的機器上管理資源,處理失效機器并監視機器狀態{典型的server farm結構,BT是上面的應用之一}. BT內部存儲數據的格式是google SSTable格式.一個SSTable提供一個從關鍵字到值的映射,關鍵字和值都可以是任意字符串.映射是排序的,存儲的{不會因為掉電而丟失},不可改寫的.可以進行以下操作:查詢和一個關鍵字相關的值;或者根據給出的關鍵字范圍遍歷所有的關鍵字和值.在內部,每個SSTable包含一列數據塊(通常每個塊的大小是64KB,但是大小是可以配置的{索引大小是16 bits,應該是比較好的一個數}).塊索引(存儲在SSTable的最后)用來定位數據塊;當打開SSTable的時候,索引被讀入內存{性能}.每次查找都可以用一個硬盤搜索完成{根據索引算出數據在哪個道上,一個塊應該不會跨兩個道,沒必要省那么點空間}:首先在內存中的索引里進行二分查找找到數據塊的位置,然后再從硬盤讀去數據塊.最佳情況是:整個SSTable可以被放在內存里,這樣一來就不必訪問硬盤了.{想的美,前面是誰口口聲聲說要跟別人共享機器來著?你把內存占滿了別人上哪睡去?} BT還依賴一個高度可用的,存儲的分布式數據鎖服務Chubby[8]{看你怎么把這個high performance給說圓嘍}.一個Chubby服務由5個活的備份{機器}構成,其中一個被這些備份選成主備份,并且處理請求.這個服務只有在大多數備份都活著并且互相通信的時候才是活的{繞口令?去看原文吧,是在有出錯的前提下的冗余算法}.當有機器失效的時候,Chubby使用Paxos算法[9,23]來保證備份的一致性{這個問題還是比較復雜的,建議去看引文了解一下問題本身}.Chubby提供了一個名字空間,里面包括了目錄和小文件{萬變不離其宗}.每個目錄或者文件可以當成一個鎖來用,讀寫文件操作都是原子化的.Chubby客戶端的程序庫提供了對Chubby文件的一致性緩存{究竟是提高性能還是降低性能?如果訪問是分布的,就是提高性能}.每個Chubby客戶維護一個和Chubby服務的會話.如果一個客戶不能在一定時間內更新它的會話,這個會話就過期失效了{還是針對大server farm里機器失效的頻率設計的}.當一個會話失效時,其擁有的鎖和打開的文件句柄都失效{根本設計原則:失效時回到安全狀態}.Chubby客戶可以在文件和目錄上登記回調函數,以獲得改變或者會話過期的通知.{翻到這里,有沒有人聞到java的味道了?} BT使用Chubby來做以下幾個任務:保證任何時間最多只有一個活躍的主備份;來存儲BT數據的啟動位置(參考5.1節);發現小表(tablet)服務器,并完成tablet服務器消亡的善后(5.2節);存儲BT數據的模式信息(每張表的列信息);以及存儲訪問權限列表.如果有相當長的時間Chubby不能訪問,BT就也不能訪問了{任何系統都有其弱點}.最近我們在使用11個Chubby服務實例的14個BT集群中度量了這個效果,由于Chubby不能訪問而導致BT中部分數據不能訪問的平均百分比是0.0047%,這里Chubby不能訪問的原因是Chubby本身失效或者網絡問題.單個集群里,受影響最大的百分比是0.0326%{基于文件系統的Chubby還是很穩定的}. 翻譯:Google大表(BigTable) 第三部分 //更新:彼岸翻譯了第5章的后面部分? 6.優化
前面一章描述了BT的實現,我們還需要很多優化工作來獲得用戶需要的高性能,高可用性和高可靠性.本章描述實現的一些部分,以強調這些優化. 局部性群組 客戶可以將多個列族組合成局部性群族.對每個子表中的每個局部性群組都會生成一個單獨的SSTable.將通常不會一起訪問的列族分割成不同的局部性群組,將會提高讀取效率.例如,Webtable中的網頁元數據(語言和校驗和之類的)可以在一個局部性群組,網頁內容可以在另外一個群組:如果一個應用要讀取元數據,它就沒有必要去訪問頁面內容. 此外,每個群組可以設定不同的調試參數.例如,一個局部性群組可以被設定在內存中.內存中的局部性群組的SSTable在被子表服務器裝載進內存的時候,使用的裝載策略是懶惰型的.一旦屬于該局部性群組的列族被裝載進內存,再訪問它們就不必通過硬盤了{讀不懂?知道機器翻譯有多難了吧?人翻譯都不行}.這個特性對于需要頻繁訪問的小塊數據特別有用:在BT內部,我們用這個特性來訪問元數據表中的地址列族. 壓縮 客戶可以控制是否壓縮一個局部性群組的SSTable.每個SSTable塊(塊的大小由局部性群組的調試參數確定),都會使用用戶指定的壓縮格式.盡管這樣分塊壓縮{比全表壓縮}浪費了少量空間,但是在讀取SSTable的一小部分數據的時候,就不必解壓整個文件了{那是,你們文件巨大,硬盤又便宜}.很多客戶使用兩遍的訂制壓縮方式.第一遍是Bentley and McIlroy’s方式[6],該方式在一個大的掃描窗口中將常見的長串進行壓縮.第二遍是一種快速壓縮算法,在一個16KB的小掃描窗口中尋找重復數據.兩個算法都很快,現有機器上壓縮速率為100到200MB/s,解壓速率是400到1000MB/s. 盡管我們選擇壓縮算法的重點是速率,而非空間效率,這種兩遍的壓縮方式空間效率也令人驚嘆{老大,別吹了,您老是在壓字符串哪!你去壓壓運行代碼看看?}.例如,在Webtable,我們用這種壓縮方式來存儲網頁內容.針對實驗的目的,我們對每個文檔只存儲一個版本,而非存儲所有能訪問的版本.該模式獲得了10比1的壓縮率.這比一般的Gzip的3比1或者4比1的HTML頁面壓縮率好很多,因為Webtable的行是這樣安排的:從一個主機獲取的頁面都存在臨近的地方,這種特性讓Bentley-McIlroy算法可以從一個主機那里來的頁面里找到大量的重復內容.不僅是Webtable,其他很多應用也通過選擇行名來將類似內容聚集在一起,因此壓縮效果非常的好{針對數據和程序特性選擇的壓縮算法}.當在BT中存儲同一數據的多個版本的時候,壓縮效率更高. 使用緩存來提高讀取性能 為了提高讀操作性能,子表服務機構使用兩層緩存.掃描緩存是高層,它緩存子表服務器代碼從SSTable獲取的關鍵字-值對.塊緩存是底層,緩存的是從GFS讀取的SSTable塊.對于經常要重復讀取同一部分數據的應用程序來說,掃描緩存很有用.對于經常要讀取前面剛讀過的數據附近的其他數據(例如,順序讀{性能提升老花招:預取},或者在一個熱門的行中的同一局部性群組中,隨機讀取不同的列)的應用程序來說,塊緩存很有用{后面這句比較拗口,是說一個局部性群組里的列都在緩存里,可以隨機讀取}. Bloom過濾器{需要讀參考文獻才知道是什么意思.從標題看,bloom是一種雜湊函數,有相對低的不命中率,所以可以用它來猜測一個關鍵字對應的存儲數據在哪里,從而減少訪問硬盤的次數,以提高性能} 如5.3節所述,一個讀操作要知道一個子表的所有狀態,必須從所有SSTable中讀取數據.如果這些SSTable不在內存,那么就需要多次訪問硬盤.我們通過允許客戶對特定局部性群組的SSTable指定bloom過濾器[7],來降低訪問硬盤的次數.使用bloom過濾器,我們就可以猜測一個SSTable是否可能包含特定行和列的數據.對于某些特定應用程序,使用少量內存來存儲bloom過濾器換來的,是顯著減少的訪問磁盤次數.我們使用bloom過濾器也使當應用程序要求訪問不存在的行或列時,我們不會訪問硬盤. 修改日志{commit-log}的實現 如果我們把對每個子表的修改日志都另存一個文件的話,就會產生非常多的文件,這些文件會并行的寫入GFS.根據每個GFS底層實現的細節,這些寫操作在最終寫入實際日志文件的時候,可能造成大量的硬盤尋道動作.此外,由于群組不大,為每個子表建立一個新的日志文件也降低了群組修改的優化程度.為了避免這些問題,我們將對每個子表服務器的修改動作添加到一個修改日志中,將多個子表的修改混合到一個實際日志文件中[18,20]. 使用單個日志顯著提高了正常使用中的性能,但是將恢復的工作復雜化了.當一個子表服務器死掉時,它以前服務的子表們將會被移動到很多其他的子表服務器上:它們每個都裝載很少的幾個子表.要恢復子表的狀態,新的子表服務器要按照原來的服務器寫的修改日志來重新進行修改.但是,這些修改記錄是混合在一個實際日志文件中的.一種方法是把日志文件都讀出來,然后只重復需要進行的修改項.但是,用這種方法,假如有100臺機器都裝載了要恢復的子表,那么這個日志文件要讀取100次(每個服務器一次). 避免這個問題的方法是先把日志按照關鍵字排序.在排序以后,所有的修改項都是連續的了,只要一次尋道操作,然后順序讀取.為了并行的排序,我們將日志分割成64MB的段,并在不同的子表服務器上并行的排序.這個排序工作是由主服務器來協同的,當一個子表服務器表示需要從某些日志文件中開始恢復修改,這個過程就開始了. 有時,向GFS中寫修改日志文件會導致性能不穩定,原因很多(例如,正在寫的時候,一個GFS服務器不行了,或者訪問某三個GFS服務器的網絡路由斷了或者擁塞).為了在GFS延遲的高峰時還能保證修改順利進行,每個子表服務器實際上有兩個線程:各自寫不同的日志文件;兩個線程里只有一個活躍.如果一個線程的寫操作性能不好,就切換到另外一個線程,修改的記錄就寫入新的活躍線程的日志文件.每個日志項都有序列號,在恢復的時候,由于線程切換而導致的重復的序列號將被忽略. 加速子表的恢復 如果主服務器將一個子表從一個子表服務器移動到另外一個服務器,第一個子表服務器對子表進行輕度壓縮.該壓縮減少了子表服務器的日志文件中沒有被緊縮的狀態,從而減少了恢復時間.壓縮完成以后,該服務器就停止服務該子表.然后,在卸載該子表前,該服務器再次進行一次(通常很快)輕度壓縮,以消除在前面一次壓縮時遺留下來的未緊縮的狀態.第二次壓縮做完以后,子表就可以被裝載到另外一個服務器上,而不必請求從日志中恢復了. 利用不變性{immutability,不可寫,可以并行讀取} 除了SSTable緩存以外,由于所有生成的SSTable都是不變的,所以BT的很多其他部分都變的簡單了.例如,當從SSTable讀的時候,就不必進行同步.這樣一來,對行的并行操作就可以非常有效的實現了.內存表是唯一一個被讀和寫操作同時訪問的可變數據結構.為了減少在讀操作中對內存表的競爭,內存表是寫復制的,這樣一來就可以并行進行讀寫操作. 因為SSTable是不變的,因此永久消除被刪除的數據的問題,就轉換成對過時的SSTable進行垃圾收集的問題了.每個子表的SSTable們都在元數據表進行注冊.主服務器對SSTable集合進行標記-掃除的垃圾收集工作[25],元數據表保存了根SSTable集合。 最后,SSTable的不變性使分裂子表的操作更加快速。我們不必為每個分裂出來的子表建立新的SSTable集合,而是讓分裂的子表集合共享原來子表的SSTable集合。
{中是譯者評論,程序除外} {本文的翻譯可能有不準確的地方,詳細資料請參考原文.}
摘要
bigtable是設計來分布存儲大規模結構化數據的,從設計上它可以擴展到上2^50字節,分布存儲在幾千個普通服務器上.Google的很多項目使用BT來存儲數據,包括網頁查詢,google earth和google金融.這些應用程序對BT的要求各不相同:數據大小(從URL到網頁到衛星圖象)不同,反應速度不同(從后端的大批處理到實時數據服務).對于不同的要求,BT都成功的提供了靈活高效的服務.在本文中,我們將描述BT的數據模型.這個數據模型讓用戶動態的控制數據的分布和結構.我們還將描述BT的設計和實現. 1.介紹 在過去兩年半里,我們設計,實現并部署了BT.BT是用來分布存儲和管理結構化數據的.BT的設計使它能夠管理2^50 bytes(petabytes)數據,并可以部署到上千臺機器上.BT完成了以下目標:應用廣泛,可擴展,高性能和高可用性(high availability). 包括google analytics, google finance, orkut, personalized search, writely和google earth在內的60多個項目都使用BT.這些應用對BT的要求各不相同,有的需要高吞吐量的批處理,有的需要快速反應給用戶數據.它們使用的BT集群也各不相同,有的只有幾臺機器,有的有上千臺,能夠存儲2^40字節(terabytes)數據. BT在很多地方和數據庫很類似:它使用了很多數據庫的實現策略.并行數據庫[14]和內存數據庫[13]有可擴展性和高性能,但是BT的界面不同.BT不支持完全的關系數據模型;而是為客戶提供了簡單的數據模型,讓客戶來動態控制數據的分布和格式{就是只存儲字串,格式由客戶來解釋},并允許客戶推斷底層存儲數據的局部性{以提高訪問速度}.數據下標是行和列的名字,數據本身可以是任何字串.BT的數據是字串,沒有解釋{類型等}.客戶會在把各種結構或者半結構化的數據串行化{比如說日期串}到數據中.通過仔細選擇數據表示,客戶可以控制數據的局部化.最后,可以使用BT模式來控制數據是放在內存里還是在硬盤上.{就是說用模式,你可以把數據放在離應用最近的地方.畢竟程序在一個時間只用到一塊數據.在體系結構里,就是:locality, locality, locality} 第二節描述數據模型細節.第三節關于客戶API概述.第四節簡介BT依賴的google框架.第五節描述BT的實現關鍵部分.第6節敘述提高BT性能的一些調整.第7節提供BT性能的數據.在第8節,我們提供BT的幾個使用例子,第9節是經驗教訓.在第10節,我們列出相關研究.最后是我們的結論. 2.數據模型
BT是一個稀疏的,長期存儲的{存在硬盤上},多維度的,排序的映射表.這張表的索引是行關鍵字,列關鍵字和時間戳.每個值是一個不解釋的字符數組.{數據都是字符串,沒類型,客戶要解釋就自力更生吧}. (row:string, column:string,time:int64)->string {能編程序的都能讀懂,不翻譯了} //彼岸翻譯的第二節 我們仔細查看過好些類似bigtable的系統之后定下了這個數據模型。舉一個具體例子(它促使我們做出某些設計決定), 比如我們想要存儲大量網頁及相關信息,以用于很多不同的項目;我們姑且叫它Webtable。在Webtable里,我們將用URL作為行關鍵字,用網頁的某些屬性作為列名,把網頁內容存在contents:列中并用獲取該網頁的時間戳作為標識,如圖一所示。 圖一:一個存儲Web網頁的范例列表片斷。行名是一個反向URL{即com.cnn.www}。contents列族{原文用 family,譯為族,詳見列族}存放網頁內容,anchor列族存放引用該網頁的錨鏈接文本。CNN的主頁被Sports Illustrater{即所謂SI,CNN的王牌體育節目}和MY-look的主頁引用,因此該行包含了名叫“anchor:cnnsi.com”和 “anchhor:my.look.ca”的列。每個錨鏈接只有一個版本{由時間戳標識,如t9,t8};而contents列則有三個版本,分別由時間 戳t3,t5,和t6標識。 行 表中的行關鍵字可以是任意字符串(目前支持最多64KB,多數情況下10-100字節足夠了)。在一個行關鍵字下的每一個讀寫操作都是原子操作(不管讀寫這一行里多少個不同列),這是一個設計決定,這樣在對同一行進行并發操作時,用戶對于系統行為更容易理解和掌控。 Bigtable通過行關鍵字的字典序來維護數據。一張表可以動態劃分成多個連續行。連續行在這里叫做“子表”{tablet},是數據分布和負載均衡的單位。這樣一來,讀較少的連續行就比較有效率,通常只需要較少機器之間的通信即可。用戶可以利用這個屬性來選擇行關鍵字,從而達到較好數據訪問地域性{locality}。舉例來說,在Webtable里,通過反轉URL中主機名的方式,可以把同一個域名下的網頁組織成連續行。具體來說,可以把maps.google.com/index.html中的數據存放在關鍵字com.google.maps/index.html下。按照相同或屬性相近的域名來存放網頁可以讓基于主機和基于域名的分析更加有效。 列族 一組列關鍵字組成了“列族”,這是訪問控制的基本單位。同一列族下存放的所有數據通常都是同一類型(同一列族下的數據可壓縮在一起)。列族必須先創建,然后在能在其中的列關鍵字下存放數據;列族創建后,族中任何一個列關鍵字均可使用。我們希望,一張表中的不同列族不能太多(最多幾百個),并且列族在運作中絕少改變。作為對比,一張表可以有無限列。 列關鍵字用如下語法命名:列族:限定詞。 列族名必須是看得懂{printable}的字串,而限定詞可以是任意字符串。比如,Webtable可以有個列族叫language,存放撰寫網頁的語言。我們在language列族中只用一個列關鍵字,用來存放每個網頁的語言標識符。該表的另一個有用的列族是anchor;給列族的每一個列關鍵字代表一個錨鏈接,如圖一所示。而這里的限定詞則是引用該網頁的站點名;表中一個表項存放的是鏈接文本。 訪問控制,磁盤使用統計,內存使用統計,均可在列族這個層面進行。在Webtable舉例中,我們可以用這些控制來管理不同應用:有的應用添加新的基本數據,有的讀取基本數據并創建引申的列族,有的則只能瀏覽數據(甚至可能因為隱私權原因不能瀏覽所有數據)。 時間戳 Bigtable表中每一個表項都可以包含同一數據的多個版本,由時間戳來索引。Bigtable的時間戳是64位整型。可以由Bigtable來賦值,表示準確到毫秒的“實時”;或者由用戶應用程序來賦值。需要避免沖突的應用程序必須自己產生具有唯一性的時間戳。不同版本的表項內容按時間戳倒序排列,即最新的排在前面。 為了簡化對于不同數據版本的數據的管理,我們對每一個列族支持兩個設定,以便于Bigtable對表項的版本自動進行垃圾清除。用戶可以指明只保留表項的最后n個版本,或者只保留足夠新的版本(比如,只保留最近7天的內容)。 在Webtable舉例中,我們在contents:列中存放確切爬行一個網頁的時間戳。如上所述的垃圾清除機制可以讓我們只保留每個網頁的最近三個版本。 //我開始翻譯3,4節? 3.API
BT的API提供了建立和刪除表和列族的函數.還提供了函數來修改集群,表和列族的元數據,比如說訪問權限. // Open the table
Table *T = OpenOrDie(”/bigtable/web/webtable”);
// Write a new anchor and delete an old anchor
RowMutation r1(T, “com.cnn.www”);
r1.Set(”anchor:[url]www.c-span.org[/url]”, “CNN”);
r1.Delete(”anchor:[url]www.abc.com[/url]”);
Operation op;
Apply(&op, &r1);
圖 2: 寫入Bigtable. 在BT中,客戶應用可以寫或者刪除值,從每個行中找值,或者遍歷一個表中的數據子集.圖2的C++代碼是使用RowMutation抽象表示來進行一系列的更新(為保證代碼精簡,沒有包括無關的細節).調用Apply函數,就對Webtable進行了一個原子修改:它為[url]http://www.cnn.com/[/url]增加了一個錨點,并刪除了另外一個錨點. Scanner scanner(T);
ScanStream *stream;
stream = scanner.FetchColumnFamily(”anchor”);
stream->SetReturnAllVersions();
scanner.Lookup(”com.cnn.www”);
for (; !stream->Done(); stream->Next()) {
printf(”%s %s %lld %s\n”,
scanner.RowName(),
stream->ColumnName(),
stream->MicroTimestamp(),
stream->Value());
}
圖3: 從Bigtable讀數據. 圖3的C++代碼是使用Scanner抽象來遍歷一個行內的所有錨點.客戶可以遍歷多個列族.有很多方法可以限制一次掃描中產生的行,列和時間戳.例如,我們可以限制上面的掃描,讓它只找到那些匹配正則表達式*.cnn.com的錨點,或者那些時間戳在當前時間前10天的錨點. BT還支持其他一些更復雜的處理數據的功能.首先,BT支持單行處理.這個功能可以用來對存儲在一個行關鍵字下的數據進行原子的讀-修改-寫操作.BT目前不支持跨行關鍵字的處理,但是它有一個界面,可以用來讓客戶進行批量的跨行關鍵字處理操作.其次,BT允許把每個表項用做整數記數器.最后,BT支持在服務器的地址空間內執行客戶端提供的腳本程序.腳本程序的語言是google開發的Sawzall[28]數據處理語言.目前,我們基于的Sawzall的API還不允許客戶腳本程序向BT內寫數據,但是它允許多種形式的數據變換,基于任何表達式的過濾和通過多種操作符的摘要. BT可以和MapReduce[12]一起使用.MapReduce是google開發的大規模并行計算框架.我們為編寫了一套外層程序,使BT可以作為MapReduce處理的數據源頭和輸出結果. 4.建立BT的基本單元
BT是建立在其他數個google框架單元上的.BT使用google分布式文件系統(GFS)[17]來存儲日志和數據文件{yeah, right, what else can it use, FAT32?}.一個BT集群通常在一個共享的機器池中工作,池中的機器還運行其他的分布式應用{雖然機器便宜的跟白菜似的,可是一樣要運行多個程序,命苦的象小白菜},BT和其他程序共享機器{BT的瓶頸是IO/內存,可以和CPU要求高的程序并存}.BT依賴集群管理系統來安排工作,在共享的機器上管理資源,處理失效機器并監視機器狀態{典型的server farm結構,BT是上面的應用之一}. BT內部存儲數據的格式是google SSTable格式.一個SSTable提供一個從關鍵字到值的映射,關鍵字和值都可以是任意字符串.映射是排序的,存儲的{不會因為掉電而丟失},不可改寫的.可以進行以下操作:查詢和一個關鍵字相關的值;或者根據給出的關鍵字范圍遍歷所有的關鍵字和值.在內部,每個SSTable包含一列數據塊(通常每個塊的大小是64KB,但是大小是可以配置的{索引大小是16 bits,應該是比較好的一個數}).塊索引(存儲在SSTable的最后)用來定位數據塊;當打開SSTable的時候,索引被讀入內存{性能}.每次查找都可以用一個硬盤搜索完成{根據索引算出數據在哪個道上,一個塊應該不會跨兩個道,沒必要省那么點空間}:首先在內存中的索引里進行二分查找找到數據塊的位置,然后再從硬盤讀去數據塊.最佳情況是:整個SSTable可以被放在內存里,這樣一來就不必訪問硬盤了.{想的美,前面是誰口口聲聲說要跟別人共享機器來著?你把內存占滿了別人上哪睡去?} BT還依賴一個高度可用的,存儲的分布式數據鎖服務Chubby[8]{看你怎么把這個high performance給說圓嘍}.一個Chubby服務由5個活的備份{機器}構成,其中一個被這些備份選成主備份,并且處理請求.這個服務只有在大多數備份都活著并且互相通信的時候才是活的{繞口令?去看原文吧,是在有出錯的前提下的冗余算法}.當有機器失效的時候,Chubby使用Paxos算法[9,23]來保證備份的一致性{這個問題還是比較復雜的,建議去看引文了解一下問題本身}.Chubby提供了一個名字空間,里面包括了目錄和小文件{萬變不離其宗}.每個目錄或者文件可以當成一個鎖來用,讀寫文件操作都是原子化的.Chubby客戶端的程序庫提供了對Chubby文件的一致性緩存{究竟是提高性能還是降低性能?如果訪問是分布的,就是提高性能}.每個Chubby客戶維護一個和Chubby服務的會話.如果一個客戶不能在一定時間內更新它的會話,這個會話就過期失效了{還是針對大server farm里機器失效的頻率設計的}.當一個會話失效時,其擁有的鎖和打開的文件句柄都失效{根本設計原則:失效時回到安全狀態}.Chubby客戶可以在文件和目錄上登記回調函數,以獲得改變或者會話過期的通知.{翻到這里,有沒有人聞到java的味道了?} BT使用Chubby來做以下幾個任務:保證任何時間最多只有一個活躍的主備份;來存儲BT數據的啟動位置(參考5.1節);發現小表(tablet)服務器,并完成tablet服務器消亡的善后(5.2節);存儲BT數據的模式信息(每張表的列信息);以及存儲訪問權限列表.如果有相當長的時間Chubby不能訪問,BT就也不能訪問了{任何系統都有其弱點}.最近我們在使用11個Chubby服務實例的14個BT集群中度量了這個效果,由于Chubby不能訪問而導致BT中部分數據不能訪問的平均百分比是0.0047%,這里Chubby不能訪問的原因是Chubby本身失效或者網絡問題.單個集群里,受影響最大的百分比是0.0326%{基于文件系統的Chubby還是很穩定的}. 翻譯:Google大表(BigTable) 第三部分 //更新:彼岸翻譯了第5章的后面部分? 6.優化
前面一章描述了BT的實現,我們還需要很多優化工作來獲得用戶需要的高性能,高可用性和高可靠性.本章描述實現的一些部分,以強調這些優化. 局部性群組 客戶可以將多個列族組合成局部性群族.對每個子表中的每個局部性群組都會生成一個單獨的SSTable.將通常不會一起訪問的列族分割成不同的局部性群組,將會提高讀取效率.例如,Webtable中的網頁元數據(語言和校驗和之類的)可以在一個局部性群組,網頁內容可以在另外一個群組:如果一個應用要讀取元數據,它就沒有必要去訪問頁面內容. 此外,每個群組可以設定不同的調試參數.例如,一個局部性群組可以被設定在內存中.內存中的局部性群組的SSTable在被子表服務器裝載進內存的時候,使用的裝載策略是懶惰型的.一旦屬于該局部性群組的列族被裝載進內存,再訪問它們就不必通過硬盤了{讀不懂?知道機器翻譯有多難了吧?人翻譯都不行}.這個特性對于需要頻繁訪問的小塊數據特別有用:在BT內部,我們用這個特性來訪問元數據表中的地址列族. 壓縮 客戶可以控制是否壓縮一個局部性群組的SSTable.每個SSTable塊(塊的大小由局部性群組的調試參數確定),都會使用用戶指定的壓縮格式.盡管這樣分塊壓縮{比全表壓縮}浪費了少量空間,但是在讀取SSTable的一小部分數據的時候,就不必解壓整個文件了{那是,你們文件巨大,硬盤又便宜}.很多客戶使用兩遍的訂制壓縮方式.第一遍是Bentley and McIlroy’s方式[6],該方式在一個大的掃描窗口中將常見的長串進行壓縮.第二遍是一種快速壓縮算法,在一個16KB的小掃描窗口中尋找重復數據.兩個算法都很快,現有機器上壓縮速率為100到200MB/s,解壓速率是400到1000MB/s. 盡管我們選擇壓縮算法的重點是速率,而非空間效率,這種兩遍的壓縮方式空間效率也令人驚嘆{老大,別吹了,您老是在壓字符串哪!你去壓壓運行代碼看看?}.例如,在Webtable,我們用這種壓縮方式來存儲網頁內容.針對實驗的目的,我們對每個文檔只存儲一個版本,而非存儲所有能訪問的版本.該模式獲得了10比1的壓縮率.這比一般的Gzip的3比1或者4比1的HTML頁面壓縮率好很多,因為Webtable的行是這樣安排的:從一個主機獲取的頁面都存在臨近的地方,這種特性讓Bentley-McIlroy算法可以從一個主機那里來的頁面里找到大量的重復內容.不僅是Webtable,其他很多應用也通過選擇行名來將類似內容聚集在一起,因此壓縮效果非常的好{針對數據和程序特性選擇的壓縮算法}.當在BT中存儲同一數據的多個版本的時候,壓縮效率更高. 使用緩存來提高讀取性能 為了提高讀操作性能,子表服務機構使用兩層緩存.掃描緩存是高層,它緩存子表服務器代碼從SSTable獲取的關鍵字-值對.塊緩存是底層,緩存的是從GFS讀取的SSTable塊.對于經常要重復讀取同一部分數據的應用程序來說,掃描緩存很有用.對于經常要讀取前面剛讀過的數據附近的其他數據(例如,順序讀{性能提升老花招:預取},或者在一個熱門的行中的同一局部性群組中,隨機讀取不同的列)的應用程序來說,塊緩存很有用{后面這句比較拗口,是說一個局部性群組里的列都在緩存里,可以隨機讀取}. Bloom過濾器{需要讀參考文獻才知道是什么意思.從標題看,bloom是一種雜湊函數,有相對低的不命中率,所以可以用它來猜測一個關鍵字對應的存儲數據在哪里,從而減少訪問硬盤的次數,以提高性能} 如5.3節所述,一個讀操作要知道一個子表的所有狀態,必須從所有SSTable中讀取數據.如果這些SSTable不在內存,那么就需要多次訪問硬盤.我們通過允許客戶對特定局部性群組的SSTable指定bloom過濾器[7],來降低訪問硬盤的次數.使用bloom過濾器,我們就可以猜測一個SSTable是否可能包含特定行和列的數據.對于某些特定應用程序,使用少量內存來存儲bloom過濾器換來的,是顯著減少的訪問磁盤次數.我們使用bloom過濾器也使當應用程序要求訪問不存在的行或列時,我們不會訪問硬盤. 修改日志{commit-log}的實現 如果我們把對每個子表的修改日志都另存一個文件的話,就會產生非常多的文件,這些文件會并行的寫入GFS.根據每個GFS底層實現的細節,這些寫操作在最終寫入實際日志文件的時候,可能造成大量的硬盤尋道動作.此外,由于群組不大,為每個子表建立一個新的日志文件也降低了群組修改的優化程度.為了避免這些問題,我們將對每個子表服務器的修改動作添加到一個修改日志中,將多個子表的修改混合到一個實際日志文件中[18,20]. 使用單個日志顯著提高了正常使用中的性能,但是將恢復的工作復雜化了.當一個子表服務器死掉時,它以前服務的子表們將會被移動到很多其他的子表服務器上:它們每個都裝載很少的幾個子表.要恢復子表的狀態,新的子表服務器要按照原來的服務器寫的修改日志來重新進行修改.但是,這些修改記錄是混合在一個實際日志文件中的.一種方法是把日志文件都讀出來,然后只重復需要進行的修改項.但是,用這種方法,假如有100臺機器都裝載了要恢復的子表,那么這個日志文件要讀取100次(每個服務器一次). 避免這個問題的方法是先把日志按照關鍵字排序.在排序以后,所有的修改項都是連續的了,只要一次尋道操作,然后順序讀取.為了并行的排序,我們將日志分割成64MB的段,并在不同的子表服務器上并行的排序.這個排序工作是由主服務器來協同的,當一個子表服務器表示需要從某些日志文件中開始恢復修改,這個過程就開始了. 有時,向GFS中寫修改日志文件會導致性能不穩定,原因很多(例如,正在寫的時候,一個GFS服務器不行了,或者訪問某三個GFS服務器的網絡路由斷了或者擁塞).為了在GFS延遲的高峰時還能保證修改順利進行,每個子表服務器實際上有兩個線程:各自寫不同的日志文件;兩個線程里只有一個活躍.如果一個線程的寫操作性能不好,就切換到另外一個線程,修改的記錄就寫入新的活躍線程的日志文件.每個日志項都有序列號,在恢復的時候,由于線程切換而導致的重復的序列號將被忽略. 加速子表的恢復 如果主服務器將一個子表從一個子表服務器移動到另外一個服務器,第一個子表服務器對子表進行輕度壓縮.該壓縮減少了子表服務器的日志文件中沒有被緊縮的狀態,從而減少了恢復時間.壓縮完成以后,該服務器就停止服務該子表.然后,在卸載該子表前,該服務器再次進行一次(通常很快)輕度壓縮,以消除在前面一次壓縮時遺留下來的未緊縮的狀態.第二次壓縮做完以后,子表就可以被裝載到另外一個服務器上,而不必請求從日志中恢復了. 利用不變性{immutability,不可寫,可以并行讀取} 除了SSTable緩存以外,由于所有生成的SSTable都是不變的,所以BT的很多其他部分都變的簡單了.例如,當從SSTable讀的時候,就不必進行同步.這樣一來,對行的并行操作就可以非常有效的實現了.內存表是唯一一個被讀和寫操作同時訪問的可變數據結構.為了減少在讀操作中對內存表的競爭,內存表是寫復制的,這樣一來就可以并行進行讀寫操作. 因為SSTable是不變的,因此永久消除被刪除的數據的問題,就轉換成對過時的SSTable進行垃圾收集的問題了.每個子表的SSTable們都在元數據表進行注冊.主服務器對SSTable集合進行標記-掃除的垃圾收集工作[25],元數據表保存了根SSTable集合。 最后,SSTable的不變性使分裂子表的操作更加快速。我們不必為每個分裂出來的子表建立新的SSTable集合,而是讓分裂的子表集合共享原來子表的SSTable集合。
轉載于:https://blog.51cto.com/designing/73714
總結
以上是生活随笔為你收集整理的翻译:Google大表(BigTable)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 何洁音乐会今晚开唱 大手笔打造pure
- 下一篇: 故障和心态