細想支持谷歌主頁搜索框需要的技術:背后的算法,緩存的搜索詞,和其他一些隨之而來的特性,比如當你輸入一個位于數據存儲中的查詢時,基本相當于絕大多數網絡的一個全文本快照。當你和成千上萬的其他人同時提交搜索時,這個快照也正在不斷地隨著這些變化被更新著。與此同時,數據是由數以千計的獨立服務器進程處理的,每個都各司其職,從計算出給你提供的相關聯廣告,到決定搜索結果的排列順序。
支持谷歌搜索引擎的存儲系統必須能夠承受每天由運行于數以千計的服務器上的成千上萬的獨立進程所發出的數百萬計的讀寫請求,幾乎不能停機來備份或維護,還必須不斷擴容以容納由谷歌網頁抓取機器人添加的日益擴大的眾多頁面。總體下來,谷歌每天要處理超過20PB。
這可不是谷歌可以從一個現成的存儲架構就能完成的。而且對于運行超大規模的數據中心的其他網絡和云計算巨頭來說也是如此,比如亞馬遜和Facebook。雖然大多數數據中心已經通過在一個存儲區網絡添加更多硬盤容量來解決擴充存儲的問題,更多的存儲服務器,通常是更多的數據庫服務器,因為云環境的性能限制,這些方法卻失效了。在云環境下,任何時候都可能有成千上萬的活躍用戶的數據,而且數據的讀寫在任何時刻都能達到數千TB。
這不僅僅是一個關于磁盤讀寫速度的簡單問題。以這些卷上的數據流來講,主要的問題是存儲網絡的吞吐量;即使有最好的交換機和存儲服務器,傳統的SAN架構也能成為數據處理的性能瓶頸。
接下來就是老生常談的擴大存儲的成本問題。超大規模網絡公司增加容量的頻率(舉個例子,亞馬遜現在每天為其數據中心增加的容量相當于整個公司在2001年全年的容量,根據亞馬遜副總裁杰姆斯·漢密爾頓的說法),用大多數數據中心的同樣做法來擺平所需的存儲,依照所需的管理,硬件和軟件成本,花費將是巨大的。這種花費在關系數據庫被添加到混合數據庫時甚至更高,這取決于一個組織對它們的分割和復制如何處理。
對于這種不斷擴展和持久存儲的需求,驅使互聯網巨頭——谷歌,亞馬遜,Facebook,微軟等等——采取一種不同的存儲解決方案:基于對象存儲的分布式文件系統。這些系統至少都部分受到其他分布式集群文件系統的啟發,如Red Hat的全局文件系統和IBM的通用并行文件系統。
這些云巨頭的分布式文件系統的架構把元數據(關于內容的數據)從它存儲的數據中分開。這能通過多個副本對數據進行大量并行讀寫操作,并且拋掉了像“文件鎖定”這樣的概念。
這些分布式文件系統的影響遠遠超出了它們為超大規模數據中心而創建的范疇——它們會直接影響那些使用公共云服務的公司(比如亞馬遜的EC2,谷歌的AppEngine和微軟的Azure)如何開發和部署程序。公司,大學和政府機構尋找一種快速存儲和提供大量數據訪問的方法正日益變成受云巨頭們啟發的數據存儲系統的新階段。因此有必要了解一下它們的發展史和過程中所做的工程折衷方案。
谷歌文件系統
谷歌是最早面對存儲容量問題的主流網絡公司中的一家。在2003年,谷歌工程師們找到了問題的答案,就是建立一個可為谷歌數據中心戰略定制的分布式文件系統——谷歌文件系統(GFS)。
谷歌文件系統幾乎是所有公司云服務的基礎。它能夠處理數據存儲,包括公司的BigTable數據庫和為谷歌的AppEngine“平臺即服務”的數 據儲存,并且為谷歌搜索引擎和其他程序提供數據。谷歌創建谷歌文件系統的設計決定推動了大量云架構下的軟件工程技術,反之亦然。谷歌往往把程序數據儲存在 大量的文件里,并把文件作為“生產者-消費者隊列”使用,數以百計的機器收集的數據可能被寫入同一個文件。這個文件可能會由另一個合并或分析數據的應用程 序處理——或許甚至是在數據正被寫入的時候。
“這當中的某些服務器一定會出錯——因此谷歌文件系統被設計為能夠容忍這種錯誤,不會丟失(太多)數據”。
谷歌為自己保留了大量技術細節,原因很明顯。但是由谷歌研究員Sanjay Ghemawat,首席工程師Howard Gobioff和高級工程師Shun-Tak Leung在2003首次發表的報告中提到,谷歌文件系統在設計上是帶有一些非常具體的優先考慮的:谷歌想把大量便宜的服務器和硬盤驅動器變成一個可以儲 存數百TB數據的能夠在出錯時自行管理可靠的數據存儲。并且它需要被設計成按谷歌的方式收集和讀取數據,允許多個應用程序同時把大批量數據添加到系統上, 且能以高速訪問。
就像是一個RAID 5存儲陣列通過多磁盤放置數據進行出錯保護,谷歌文件系統把文件分成固定大小的塊,復制到整個服務器集群。因為它們是用著廉價硬盤的電腦,其中一些服務器肯定會出錯——因此谷歌文件系統被設計為能夠容忍這種錯誤,不會丟失(太多)數據。
但是RAID和GFS的相同點就到此為止了,因為那些服務器可以分布于網絡——既可以在第一個單獨的物理數據中心也可以分散于不同的數據中心,取決 于數據的用途。GFS設計主要用于批量處理大量數據。重點是高速讀取數據,而不是到文件中某個部分的訪問速度,也不是數據寫入到文件系統的速度。GFS提 供如此高輸出是以犧牲更高密度的讀寫和更快速度的數據寫入為代價的。正如Ghemawat和公司在文件中所說,“在文件中任意位置的小的寫入是支持的,但 不一定非要高效。”
這種分布式的性質,隨著GFS處理數據量的龐大——數百萬的文件,當中很多都超過100MB而且通常都會變成GB——需要一些取舍,以便讓GFS和 你通常安裝在一臺服務器上的文件系統有很大的不同。因為成百上千的獨立進程可能同時對一個文件進行寫入和讀取,GFS需要支持“原子性”數據——在不影響 其他程序的情況下回滾出錯的寫入。而且它需要以非常低的同步開銷保持數據的完整性以避免拖垮性能。
GFS由三層組成:GFS客戶端,處理程序數據請求;管理服務器,用內存中的索引追蹤數據文件名和所在區塊的位置;還有數據存儲服務器本身。最初, 為簡單起見,GFS為每個集群使用一個單獨的管理服務器,因此系統被設計成讓管理服務器盡可能避開數據訪問。谷歌已經發開出了一個分布式管理服務器系統, 可以控制數百臺管理服務器,每一臺都能處理大約1億個文件。
當GFS客戶端收到一個特定數據文件的請求,它需要從管理服務器請求數據的位置。管理服務器提供其中一個副本的位置,之后客戶端就可以直接與存儲服務器進行溝通,用來讀寫剩下的其他部分。管理服務器就不再參與其中了,除非有錯誤發生。
為確保數據是高度可用的,GFS舍棄了其他一些東西——比如各副本間的一致性。GFS確實堅持數據的原子性——如果寫入失敗,它將返回一個錯誤,然 后將寫入回滾到元數據,并產生一個舊數據的副本。但是管理服務器在數據寫入上的介入缺失意味著當數據寫入到系統時,它不能立刻讓副本遍布整個GFS集群。 在處理對數據同時訪問和網絡限制的必要性之外,該系統遵循谷歌所謂的“寬松一致性模型”。
這意味著GFS對于在必要時從舊的副本提供陳舊的數據完全不在乎——只要數據最終得以更新。管理服務器的追蹤變化,或“突變”,當變化發生時,區塊中的數據會用版本號來指示。由于一些副本被留下了(或變“舊了”),GFS管理服務器會確保這些區塊在更新前不會送至客戶端。
但這并不一定發生在已經連接到那些區塊的部分。元數據的變更在管理服務器處理這些變更,并將它們反映在元數據前是不可見的。元數據也需要在多個位置 生成副本,以防管理服務器出錯——那樣的話整個文件系統就丟失了。而且如果在寫入過程中管理服務器有錯誤發生,變更同樣會消失。由于谷歌處理數據的方式, 這并不是一個大問題:程序使用的大部分的數據很少變化,而且當變化發生時,數據通常是擴充的而不是原地修改的。
當GFS在為2003年運行的谷歌應用設計出來時,離谷歌開始遭遇擴展性問題并不遠。甚至是在公司收購YouTube之前,GFS開始碰壁——很大 原因是谷歌新添加的應用在64M文件大小下工作的不是很好。為了繞過它,谷歌轉向了Bigtable,一種基于表格的數據存儲,那依稀類似于數據庫,位于 GFS之上。Bigtable大多是一次寫入,因此變更被作為對表的擴展進行存儲的——谷歌將其用于如對Google Docs進行版本控制的類似應用上。
如果你不是在谷歌工作,那上述內容太過于學術性了(雖然它可以幫助AppEngine,谷歌云存儲和谷歌其他服務的用戶更好地了解臺面下是怎么事 兒)。雖然谷歌云存儲通過一個網絡接口提供了一個公開方式來儲存和訪問位于GFS上的文件,但是操控GFS的真正接口和工具并不是公開的。但報告稱GFS 引領了更廣泛使用的分布式文件系統的發展,如:Hadoop分布式文件系統。
Hadoop分布式文件系統(HDFS)
Hadoop是用Java開發的,作為Apache基金會的一個開源項目,它在網絡公司和其他有“大數據”問題的公司間已經有了如下的口碑,它被稱 之為“二十一世界的瑞士軍刀”。所有這些宣傳意味著,你很可能會發現你遲早要以某種形式用Hadoop處理問題而不是用其他的分布式文件系統——特別是當 微軟開始將其列入Windows Server的擴展中的時候。
Hadoop是由開發者Doug Cutting在他兒子給一只玩具大象起名后用它命名的,“靈感”來自于GFS和谷歌的MapReduce分布式計算環境。在2004年,Cutting 和其他工作于Apache Nutch搜索引擎項目的人試圖尋求一種可以將抓取器和索引帶向“網絡規模”的方式,Cutting閱讀了谷歌關于GFS和MapReduce的論文并開 始動手開發自己的項目。雖然對于Hadoop的大多數熱情來自于它由MapReduce啟發的分布式處理管理衍生出的分布式數據處理能力,但使用 Hadoop分布式文件系統還是因為它能對大量數據進行處理。
Hadoop是在Apache許可證下開發的,有許多商業和自由發行版可用。我用的版本來自Cloudera公司(Doug Cutting現在的東家)——Cloudera發行版包括了Apache Hadoop(CDH),Cloudera企業平臺的開源版本,和Cloudera服務和配置特別版,它可免費支持50個節點。
HortonWorks,該公司與微軟合作幫助后者把Hadoop移植到Azure和Windows Server,有其自己的基于Hadoop和HortonWorks數據平臺,是一個受限的“技術預覽版”。同樣還有Apache Core的Debian包,和許多其他開源的或商業的基于Hadoop的某種形式的產品。
HDFS可被用于支持在大量廉價硬件和大數據下廣泛的應用。但由于其架構,它不完全適合于通用數據存儲,并且放棄了一定的靈活性。HDFS必須廢除 某些經常與文件系統有關的事情,以確保它能更好地處理在分布著數百甚至數千臺物理機器上的大量數據——如對數據交互訪問這種事情。
雖然Hadoop運行于Java上,但是除了它的Java API之外還有許多種方式和HDFS進行交互。有一種C語言版本的API,通過Hadoop的命令行界面,文件可以通過HTTP請求瀏覽。還有 MountableHDFS,一個基于FUSE的擴展,允許HDFS被大多數操作系統作為一個文件系統掛載。開發者們正在制作一個WebDAV接口,讓系 統可以進行基于網絡的數據寫入。
HDFS嚴格遵循了由谷歌的GFS奠定的架構路線,延續了它的三層,單管理服務器模型。每個Hadoop集群有一個叫做“名字節點”的管理服務器, 它來追蹤關于位置和每個64M存儲“塊”副本的狀態的元數據。數據通過集群中的“數據節點”復制——從屬系統處理數據的讀寫。默認情況下每個塊都會被復制 三次,而且復制的次數還可以通過改變集群設置來增加。
像GFS一樣,HDFS讓管理服務器盡可能快地避開讀寫循環,避免產生性能瓶頸。當從HDFS上訪問數據的請求產生時,名字節點發回與這個請求最近 的數據節點上的塊的位置信息。名字節點還可以通過一個“心跳”協議追蹤每個數據節點的健康度并停止向不響應的數據節點發送請求,把它們標記為“死的”。
在切換后,名字節點就不處理任何更進一步的交互。對數據節點上數據的編輯被報告回名字節點并記錄在日志里,之后用變動的數據副本對其他數據節點進行 復制。同GFS一樣,這導致了一致性上相應的懶散形式,而且雖然名字節點將為最近修改的數據塊發送新的請求,正在進行的工作仍然會碰到它們被分配到的數據 節點上的陳舊數據。
那不應該是經常發生的,然而,因為HDFS數據應該被“寫入一次”——變動通常是擴充數據,而不是改動現有數據,為了更簡單的一致性。而且由于Hadoop應用的性質,數據往往會大批量地寫入HDFS。
當一個客戶端發送要寫入HDFS的數據時,它首先被客戶端程序安置在一個臨時的本地文件中,直到寫入的數據達到了數據塊的大小——默認64MB。之 后客戶端聯系名字節點并獲得一個數據節點和要寫入數據的塊位置。這一過程對每個塊的數據重復進行,一次一個塊。這減少了產生網絡阻塞的數量,但也減慢了寫 入過程。但是HDFS是用于讀取的,而不是寫入。
HDFS可以減少網絡寫入流量的另一個辦法是在于它處理復制的方式。通過激活一個叫做“機架感知”的HDFS特性來管理分布的副本,管理員可以為每 個節點指定一個機架序號,通過網絡配置腳本中的一個變量指定它的物理位置。默認情況下,所有的節點都在同一個“機架”中。但是當機架感知被配置以 后,HDFS把每個塊上的一個副本放置于同一個數據中心機架的另一個節點上,另一個則在不同的機架上,來減少網絡中數據寫入量——基于如下理由,就是一整 個機架出錯的幾率比一個單一節點出錯的幾率要小。理論上,它整體改善了HDFS的寫入性能而沒有犧牲掉可靠性。
與GFS早期版本一樣,對于一個要成為高度可用的分布式系統,HDFS的名字節點創建一個單一的故障點。如果名字節點中的元數據丟失了,整個 HDFS環境就變成不可讀了——就像一個缺少了文件分配表的硬盤。HDFS支持使用“備份節點”,它能與內存中的名字節點的元數據保持版本同步,并儲存前 一系統狀態的快照,以便能夠在需要時回滾。快照也可以被分開儲存在叫做“檢查節點”的地方。然而,根據HDFS的文檔,目前還不支持自動重啟一個破壞的名 字節點,而且備份節點不會自動移除和替代管理服務器。
HDFS和GFS都是用搜索引擎式任務的思想而開發的。但是對于面向更多通用計算類型的云服務,“寫入一次”的方法和其他妥協辦法使得大數據查詢性能不盡理想——這就是為什么亞馬遜開發了自己的分布式存儲平臺,叫做Dynamo。