前言
Facebook作為最大的社群網站,目前流量排名世界第二,僅次於Google,要支撐如此龐大的資料量,所需的硬體設備與技術當然不簡單,我們先來看看Facebook有哪些驚人的成就:
Facebook作為最大的社群網站,目前流量排名世界第二,僅次於Google,要支撐如此龐大的資料量,所需的硬體設備與技術當然不簡單,我們先來看看Facebook有哪些驚人的成就:
- Facebook估計擁有超過六萬台伺服器。
- 所有Memcached的執行程式所儲存的資料總量達300TB。
- Hadoop跟Hive叢集是由三千台伺服器組成。每台都是八核心,32GB記憶體,12TB硬碟。結果是24000核心,96TB記憶體與36PB的儲存空間。
- 每天有一千億次的點擊,光log也有130TB。
- 1.雲端平臺Hadoop與相關元件(HBase、MapReduce、HDFS、Hive)
2.HipHop
3.Apache Thrift
4.Memcached
5.BigPipe
6.Scribe
7.Haystack
分散式架構主要目的是讓服務開發人員,不用考慮在這些分散式系統上資料要怎麼放置、運算要怎麼切割,只需要專注在服務的開發就可以了,而資料與運算的切割及分散就交給雲端運算的架構來處理。Hadoop是Apache軟體基金會 (Apache Software Foundation) 底下的開放原始碼計劃 (Open source project),最初是做為Nutch這個開放原始碼的搜尋引擎的一部份。Hadoop是以java寫成,可以提供大量資料的分散式運算環境,而且Hadoop的架構是由Google發表的BigTable及Google File System等文章提出的概念實做而成,所以跟Google內部使用的雲端運算架構相似。
因此我們可以想像,一個雲端平臺必須要有自己的檔案格式與管理策略,因此HDFS就是為了處理分佈於各個伺服器上的檔案系統,而HBase提供了分散式資料庫的功能,MapReduce則提供了在分散式系統上進行更有效率的運算架構,Hive提供了在分散式系統上資料倉儲的功能。Hadoop主要核心完全使用Java開發,而使用者端則提供C++/Java/Shell/Command等程式開發介面,目前可執行於Linux、Mac OS/X、Windows和Solaris作業系統,以及一般商用等級的伺服器。 下圖就是Hadoop的組成元件:
《圖一》
MapReduce
MapReduce誕生源由是Google需要進行大規模資料處理,而在這個過程中,發現了處理大量資料時會面臨某些共同問題,如需要使用許多機器協同計算,以及處理輸入資料時有兩項作業:Map和Reduce。 這兩項作業主要是受到函數編程的啟發,以Map/Reduce為基礎的應用程式,能夠運作於數千台PC所組成的大型叢集上,並以一種容錯的方式平行處理P級別的資料量。 在函數編程中很早就有了Map和Reduce觀念,其實類似於演算法中各個擊破的作法(Divide and Conquer),也就是將問題分解成很多個小問題之後再做總和。Map函數的輸入是一個(Keg,Value)序對組,輸出則為另一組中繼過渡的(Key,Value)序對組。而Reduce函數則負責針對相同的中繼過渡的(Key,Value)序對組合併其所有相關聯的中繼值,並產生輸出結果的鍵/值序對組,如下圖所示。
《圖二》
簡而言之,MapReduce提供程式設計者一個在分散式系統上處理問題的框架,程式設計者需要設計Map與Reduce函數,決定如何切割與合併問題以達到最佳的效能,剩餘的底層處理則交給MapReduce框架。MapReduce是由Google所發展的軟體框架,目的是對電腦叢集上的大型資料集執行分散式運算,讓使用者可以把心力放在定義Map和Reduce函數,進行運算的Mapper和Reducer會由系統會自動指派不同的運算節點擔任,所以程式設計時完全不用做資料和運算的切割 (decomposition),運算資源會由JobTracker分配到各個運算節點上的TaskTracker,並指派不同的節點擔任Mapper和Reducer。透過MapReduce可以用於大型資料處理,例如:搜尋、索引製作與排序,大型資料集的資料採礦與機器學習,大型網站的網站存取日誌分析等應用。
在龐大的分散式架構下,有伺服器或資料損壞是十分常見的事,由於有MapReduce在處理細節,因此在處理過程中能夠大大降低因資料錯誤造成整個程式需重新啟動的狀況,因為龐大的資料量往往需要數十小時至數天的運算時間,因此能夠避免不必要的時間浪費。
HDFS
Hadoop Distributed File System (HDFS) 將分散的儲存資源整合成一個具容錯能力、高效率且超大容量的儲存環境,在Hadoop系統中大量的資料和運算時產生的暫存檔案,都是存放在這個分散式的檔案系統上。HDFS的設計理念是在分散式的儲存環境裡,提供單一的目錄系統 (Single Namespace),一個典型的超大型分散式檔案系統中,通常會有數萬個節點、數億個檔案、以及數十Peta Bytes的資料量,而這樣的分散式檔案系統具備的資料存取特性為Write Once Read Many存取模式。 也就是檔案一旦建立、寫入之後就不允許修改,因此當資料有異動時,檔案系統會重新建立一個新的檔案,在這之中,每個檔案被分割成許多區塊(block)與異地備份,每個區塊的大小通常為128 MB,系統會將每個區塊複製許多複本(replica),並分散儲存於不同的資料節點(DataNode)上。
除此之外,HDFS中很重要的概念是認為移動運算到資料端通常比移動資料到運算端來的成本低,這是由於資料的位置資訊會被考慮在內,因此運算作業可以移至資料所在位置,處理資料的檔案複本預設是每個檔案儲存3份,該設定可由開發人員自訂。 如前面提到的MapReduce在系統分配運算工作時,會將運算工作分配到存放有運算資料的節點上進行,減少大量資料透過網路傳輸的時間。HDFS採用的是一般等級伺服器,因此透過複製資料的方式以因應硬體的故障,當偵測到錯誤時,即可從複製的備份資料執行資料回復。下圖為HDFS架構。
《圖三》
HBase
簡而言之,HBase的目標是作為Hadoop所使用的資料庫,這可讓我們需要在隨機且即時的讀寫超大資料集時所使用。HBase是一種分散式儲存系統,其類似RDBM資料表的資料結構(Multi-Dimensional Map),並具備高可用性、高效能,以及容易擴充容量及效能的特性。HBase適用於利用數以千計的一般等級伺服器上,來儲存Petabytes級的資料,其中以Hadoop分散式檔案系統(HDFS)為基礎,提供類似Bigtable的功能,HBase同時也提供了MapReduce程式設計的能力。
HBase使用列 (row) 和行 (column) 為索引存取資料值,因此查詢的時候比較像在使用map容器 (container);HBase的另一個特點是每一筆資料都有一個時間戳記 (timestamp),因此同一個欄位可依不同時間存在多筆資料。一個HBase的資料表 (table) 是由許多row及數個column family組成,每個列都有一個row key做為索引;一個column family就是一個column label的集合 (set),裡面可有很多組label,這些label可以視需要隨時新增,而不用重新設定整個資料表如下表所示。在存取資料表的時候,通常就使用 (‘row key’, ‘family:label’) 或 (‘row key’, ‘family:label’, ‘timestamp’) 的組合取出需要的欄位元。下表為HBase的資料結構。
《圖四》
HBase為了方便分散資料和運算工作,又將整個資料表分為許多region,一個region是由一到數個列所組成的,可以分別存放在不同HBase主機上,這些存放region的主機就是region server,另外還有master server用來紀錄每一個region對應的region server;master server也會自動將不能提供服務的region server上的region重新分配到其他的region server上。
Hive
Hive是架構在Hadoop上的資料倉儲系統,目的是提供在分散是環境下的資料倉儲功能,我們先來簡單瞭解一下資料倉儲與資料庫的差異。資料庫與資料倉儲最大差異點是資料庫進行資料的新增、刪除、修改、查詢等功能其主要目的於管理資料庫的存取;而資料倉儲重視的是資訊的獲得,以問題決策分析導向,強調多維度視野,視覺化的提供決策者資訊可用性,資料倉儲與傳統資料庫差異比較如下表所示。
《圖五》
而Hive提供了類似SQL的語法稱為HQL讓使用者更容易操作Hive,同時Hive也提供了MapReduce外掛功能,來補足Hive本身指令的不足,Hive在Facebook主要是作為資料探勘的功能,例如當Facebook在推出新功能時,就會利用Hive研究新功能與舊功能兩者之間使用者行為的差異,藉以決定是否該推出新功能。當時Facebook計畫推出他們的“Like”按鈕時,他們擔心會不會發生“自相蠶食”的結果,會不會因此反而減少了文字評論,而不是提升了參與度?為了驗證這一點,他們運行了A/B測試來比較用戶行為,給一組用戶使用新功能(Like按鈕),而另一個對照組則沒有。這需要在一個互接連的社區內進行測試,“內生的組織”,在組織之外的連接很少。他們使用了兩組南美國家來進行比較,哥倫比亞、委內瑞拉 vs 阿根廷、智利。測試的結果是使用Like按鈕的時候評論增加了4.46%,而對照組的資料則是0.63%。這一類測試所產生的巨大的資料集正是Facebook使用Hadoop來處理資料的例子。
HipHop
Hiphop是一個由PHP到C++的轉換程式,一個重新實現的PHP運行庫和許多常用PHP擴展的重寫版本構成,目的是在加速和優化PHP。眾所周知,Facebook的前端主要是用PHP寫的。PHP非常簡單,易學易用,好讀好調適,因此新工程師成長很快,有利地促進了Facebook的快速創新。PHP是一種指令碼語言,其好處是程式設計效率高,能夠支援產品的快速反覆運算。但是與傳統的編譯語言相比,指令碼語言的CPU和記憶體使用效率不好。隨著Ajax技術的廣泛採用,加上SNS對動態要求較高,這些缺點更顯得突出。對於每月超過4000億次PV的Facebook來說,如何實現擴展,尤其具有挑戰性。常見的辦法是直接用C++重寫PHP應用中比較複雜的部分,作為PHP擴展。從技術角度講這也沒有問題,但是增加了技能需求,能夠在整個應用上工作的工程師數量就大大減少了。最後,Facebook選擇了HipHop,工程師可以編寫代碼,用PHP編寫組合最後頁面的邏輯,並能夠繼續快速反覆運算,同時後端服務使用C++, Erlang, Java, Python編寫,提供新聞提要、搜索、聊天和其他核心功能。
HipHop將PHP代碼轉換為高度優化的C++代碼,然後再用g++編譯器編譯。它可以保持語義等效地執行原始程式碼,但為了提高性能,犧牲了一些很少用到的特性,比如eval()。HipHop開發中的主要困難在於,在PHP和C++這兩種很不一樣的語言之間怎麼實現轉換。雖然PHP也可以寫一些很巧妙的動態特性,但是大多數PHP代碼還是非常簡單的。if (...) {...} else {..} 比foo($x) { include $x; } 肯定更常見。HipHop生成的代碼盡可能地使用函數和變數的靜態繫結。同時,還使用類型推演來選出變數最可能對應的某個類型,從而節省記憶體。下圖為HipHop運作流程:
《圖六》
轉換過程分三步驟:
1.靜態分析。收集聲明關係和依賴關係等資訊。
2.類型推演。選擇最合適的類型,是C++?還是String, Array, classes, Object或者Variant。
3.代碼生成。大部分直接將PHP語句和運算式對應為C++的語句和運算式。
HipHop在保持了PHP優點的同時,也兼得了C++的性能優勢。專案總共有30萬行代碼,5000多個單元測試。所有這些都將以PHP開源許可證形式發佈到GitHub。
Apache Thrift
Thrift源自於facebook之手,在2007年facebook提交Apache基金會將Thrift作為一個開源專案,對於當時的facebook來說創造thrift是為了解決facebook系統中各系統間大資料量的傳輸通訊以及系統之間語言環境不同需要跨平臺的特性。所以thrift可以支援多種程式語言,例如: C++, C#, Cocoa, Erlang, Haskell, Java, Ocami, Perl, PHP, Python, Ruby, Smalltalk. 在多種不同的語言之間通訊,thrift可以作為二進位的高性能的通訊中介軟體,支援資料(物件)序列化和多種類型的RPC服務。Thrift適用於程式對程式靜態的資料交換,需要先確定好他的資料結構,他是完全靜態化的,當資料結構發生變化時,必須重新編輯IDL檔,代碼生成,再編譯載入的流程,跟其他IDL工具相比較可以視為是Thrift的弱項,Thrift適用於搭建大型資料交換及存儲的通用工具,對於大型系統中的內部資料傳輸相對於JSON和xml無論在性能、傳輸大小上有明顯的優勢。
Thrift 具有自己內部定義的傳輸協定規範(TProtocol)和傳輸資料標準(TTransports),通過IDL腳本對傳輸資料的資料結構(struct) 和傳輸資料的業務邏輯(service)根據不同的運行環境快速的構建相應的代碼,並且通過自己內部的序列化機制對傳輸的資料進行簡化和壓縮提高併發、大型系統中資料交互的成本,下圖描繪了Thrift的整體架構,分為6個部分:
1.你的業務邏輯實現(Your Code)
2.用戶端和服務端對應的Service
3.執行讀寫操作的計算結果
4.TProtocol
5.TTransports
6.底層I/O通信
《圖七》
假定需要傳輸相同的內容,但使用不同的方式從1、傳輸內容所產生的大小 2、傳輸過程中服務端和用戶端所產生的開銷,這2個方便進行比較。使用Thrift和其他方式的所產生的內容大小比較結果如下:
《圖八》
在上圖中我們能明顯看出,最臃腫的是RMI,其次是xml,使用Thrift的TCompactProtocol協定和Google 的 Protocol Buffers 相差的不算太多,相比而言還是Google 的 Protocol Buffers效果最佳。
而使用Thrift 中的協定和其他方式的所產生的運行開銷比較結果如下:
《圖九》
在上圖中我們能明顯看出,最占資源是REST2中協定,使用Thrift的TCompactProtocol協定和Google 的 Protocol Buffers 相差的不算太多,相比而言Thrift的TCompactProtocol協議效果最佳。(下期待續)
參考資料
1.做出Facebook規模,你所需要的技術元件總覽, http://www.inside.com.tw/2011/04/21/facebook-platform-component
2.開放原始碼的雲端運算平台技術(1) 初探Hadoop開放原始碼平台環境,http://www.runpc.com.tw/content/cloud_content.aspx?id=105318
3.Facebook性能大提升的秘密:HipHop, http://news.csdn.net/a/20100203/216872.html
4.Apache Thrift入門1-架構&介紹, http://www.javabloger.com/article/apache-thrift-architecture.html
5.What is Facebook's architecture? http://www.quora.com/What-is-Facebooks-architecture
6.雲端運算平台—Hadoop作者:周秉誼 / 臺灣大學計算機及資訊網路中心作業管理組碩士後研究人員,http://www.cc.ntu.edu.tw/chinese/epaper/0011/20091220_1106.htm
7.Facebook談Hadoop,Hive,HBase和 A/B測試,http://www.cc.ntu.edu.tw/chinese/epaper/0011/20091220_1106.htm
Memcached
memcached是一套分散式的快取系統,目前被許多軟體(如MediaWiki)所使用。這是一套開放原始碼軟體,以BSD license授權釋出。memcached的API使用三十二位元的循環冗餘校驗(CRC-32)計算鍵值後,將資料分散在不同的機器上。當表格滿了以後,接下來新增的資料會以LRU機制替換掉。由於memcached通常只是當作快取系統使用,所以使用memcached的應用程式在寫回較慢的系統時(像是後端的資料庫)需要額外的程式碼更新memcached內的資料。
將純粹使用資料庫查詢的程式碼加上memcached支援是很簡單的,假設這是原來的程式碼:
function get_foo (int userid) {
result = db_select("SELECT * FROM users WHERE userid = ?", userid);
return result;
}
加上memcached的快取機制後:
function get_foo (int userid) {
result = memcached_fetch("userrow:" + userid);
if (!result) {
result = db_select("SELECT * FROM users WHERE userid = ?", userid);
memcached_add("userrow:" + userid, result);
}
return result;
}
上述的程式會先到memcached檢查是否有userrow:userid的資料,如果有則直接傳回結果,如果不存在時再去資料庫查詢,並將結果放到memcached內。
在memcached內已經有快取資訊時將資料庫的資料更新後,上述的程式會抓到舊的資料,這是屬於Cache coherency的問題。其中一種解決的方法是在更新資料庫時,同時更新memcached內的資訊:
function update_foo(int userid, string dbUpdateString) {
result = db_execute(dbUpdateString);
if (result) {
data = createUserDataFromDBString(dbUpdateString);
memcached_set("userrow:"+userid, data);
}
}
前面有提過,Facebook的memcached大約有300TB,因此許多資料不需要再重複讀取DB,因此反應時間可以大幅提昇。
BigPipe
網頁載入的速度十分重要,尤其對於擁有遍佈全球的5億用戶的Facebook這樣的大型網站,有著同時大量請求、極大量資料等現實情況,速度就成了必須克服的難題之一。2010年初的時候,Facebook的前端性能研究小組開始了他們的優化項目,經過了六個月的努力,成功的將個人空間主頁面載入耗時由原來的5 秒減少為現在的2.5 秒。這是一個非常了不起的成就,也給用戶來帶來了很好的體驗。在優化專案中,工程師提出了一種新的頁面載入技術,稱之為Bigpipe。
面對網頁越來越大的情況,尤其是大量的css檔和js檔需要載入,傳統的頁面載入模型很難滿足這樣的需求,直接結果就是頁面載入速度變慢,這絕不是我們希望看到的。目前的技術實現中,使用者提出頁面訪問請求後,頁面的完整載入流程如下:
1.用戶訪問網頁,流覽器發送一個HTTP請求到網路服務器。
2.伺服器解析這個請求,然後從存儲層取資料,接著生成一個html檔內容,並在一個HTTP Response中把它傳送給用戶端。
3.HTTP response在網路中傳輸。
4.流覽器解析這個Response ,創建一個DOM樹,然後下載所需的CSS和JS文件。
5.下載完CSS 檔後,流覽器解析他們並且應用在相應的內容上。
6.下載完JS 後,流覽器解析和執行他們。
memcached是一套分散式的快取系統,目前被許多軟體(如MediaWiki)所使用。這是一套開放原始碼軟體,以BSD license授權釋出。memcached的API使用三十二位元的循環冗餘校驗(CRC-32)計算鍵值後,將資料分散在不同的機器上。當表格滿了以後,接下來新增的資料會以LRU機制替換掉。由於memcached通常只是當作快取系統使用,所以使用memcached的應用程式在寫回較慢的系統時(像是後端的資料庫)需要額外的程式碼更新memcached內的資料。
將純粹使用資料庫查詢的程式碼加上memcached支援是很簡單的,假設這是原來的程式碼:
function get_foo (int userid) {
result = db_select("SELECT * FROM users WHERE userid = ?", userid);
return result;
}
加上memcached的快取機制後:
function get_foo (int userid) {
result = memcached_fetch("userrow:" + userid);
if (!result) {
result = db_select("SELECT * FROM users WHERE userid = ?", userid);
memcached_add("userrow:" + userid, result);
}
return result;
}
上述的程式會先到memcached檢查是否有userrow:userid的資料,如果有則直接傳回結果,如果不存在時再去資料庫查詢,並將結果放到memcached內。
在memcached內已經有快取資訊時將資料庫的資料更新後,上述的程式會抓到舊的資料,這是屬於Cache coherency的問題。其中一種解決的方法是在更新資料庫時,同時更新memcached內的資訊:
function update_foo(int userid, string dbUpdateString) {
result = db_execute(dbUpdateString);
if (result) {
data = createUserDataFromDBString(dbUpdateString);
memcached_set("userrow:"+userid, data);
}
}
前面有提過,Facebook的memcached大約有300TB,因此許多資料不需要再重複讀取DB,因此反應時間可以大幅提昇。
BigPipe
網頁載入的速度十分重要,尤其對於擁有遍佈全球的5億用戶的Facebook這樣的大型網站,有著同時大量請求、極大量資料等現實情況,速度就成了必須克服的難題之一。2010年初的時候,Facebook的前端性能研究小組開始了他們的優化項目,經過了六個月的努力,成功的將個人空間主頁面載入耗時由原來的5 秒減少為現在的2.5 秒。這是一個非常了不起的成就,也給用戶來帶來了很好的體驗。在優化專案中,工程師提出了一種新的頁面載入技術,稱之為Bigpipe。
面對網頁越來越大的情況,尤其是大量的css檔和js檔需要載入,傳統的頁面載入模型很難滿足這樣的需求,直接結果就是頁面載入速度變慢,這絕不是我們希望看到的。目前的技術實現中,使用者提出頁面訪問請求後,頁面的完整載入流程如下:
1.用戶訪問網頁,流覽器發送一個HTTP請求到網路服務器。
2.伺服器解析這個請求,然後從存儲層取資料,接著生成一個html檔內容,並在一個HTTP Response中把它傳送給用戶端。
3.HTTP response在網路中傳輸。
4.流覽器解析這個Response ,創建一個DOM樹,然後下載所需的CSS和JS文件。
5.下載完CSS 檔後,流覽器解析他們並且應用在相應的內容上。
6.下載完JS 後,流覽器解析和執行他們。
《圖一》
完整流程如上圖。圖中左側表示伺服器,右側表示流覽器。流覽器先發送請求,然後伺服器進行查找資料,生成頁面,返回html代碼,最後流覽器進行頁面的呈現。這種模式有非常明顯的缺陷:流程中的操作有著嚴格的順序,如果前面的一個操作沒有執行結束,後面的操作就不能執行,即操作之間是不能重疊。這樣就造成性能的瓶頸:伺服器生成一個頁面的內容時,流覽器是空閒的,顯示空白內容;而當流覽器載入呈現頁面內容時,伺服器又是空閒的,時間與性能的浪費由此產生。
《圖二》
上圖為現有的服務模型,橫軸表示花費的時間。黃色表示在伺服器的生成頁面內容的時間,白色表示網路傳輸時間,藍色表示在流覽器渲染頁面的時間。可以看出,現有的模式造成很大的時間浪費。再考慮下圖中的情況,圖中綠色表示伺服器從存儲層取查資料花費的時間,在極大量資料下,當執行一條很費時的查詢語句時(如下圖右側),伺服器就就阻塞在那裡沒有其他操作,而流覽器更是得不到任何回饋。這會造成非常不友好的用戶體驗,用戶不知道什麼原因使他們等待很長時間。
《圖三》
面對上述問題,我們看看BigPipe的解決辦法。BigPipe提出分塊的概念,即根據頁面內容位置的不同,將整個頁面分成不同的區塊—稱為pagelet。該技術的設計者Changhao Jiang是研究電子電路的博士,可能從微處理器上得到了啟發,將眾多pagelet載入的不同階段像流水線一樣在瀏覽器和伺服器上執行,這樣就做到了瀏覽器和伺服器的並行化,從而達到重疊伺服器端執行時間和瀏覽器端執行時間的目的。使用BigPipe不僅可以節省時間,使載入的時間縮短,而且可以同過pagelet的分步輸出,使一部分的頁面內容更快的輸出,從而獲得更好的用戶體驗。BigPipe中,使用者提出頁面訪問請求後,頁面的完整載入流程如下:
1.Request parsing:伺服器解析和檢查http request
2.Datafetching:伺服器從存儲層獲取資料
3.Markup generation:伺服器生成html 標記
4.Network transport : 網路傳輸response
5.CSS downloading:流覽器下載CSS
6.DOM tree construction and CSS styling:流覽器生成DOM 樹,並且使用CSS
7.JavaScript downloading: 流覽器下載頁面引用的JS檔
8.JavaScript execution: 流覽器執行頁面JS代碼
這個8 個流程幾乎與上文中提到現有的模式沒有區別,但這整個流程只是一個pagelet 的完整流程,而多個pagelet 的不同操作階段就可以像流水線一樣進行執行了。
《圖四》
從上圖中,可以看出BigPipe對原有的模式進行的改進。流覽器發送訪問請求,然後流覽器分步返回不同的pagelet的內容,BigPipe 打破了原有的循序執行,將頁面分成不同的pagelet ,如此一來,所有的pagelet 的執行時間累加起來還是原有的時間。但是, 通過疊加不同pagelet 的不同階段的執行時間,使總執行時間大大減少,這就是Bigpipe減少頁面載入時間的秘密。
FaceBook的頁面被分成了很多不同的pagelets,如下圖:
《圖五》
經過上面的討論,我們可以發現,使用BigPipe技術優化頁面可以有四個好處:
1.減少頁面的載入時間。
2.使頁面分步輸出,改善用戶體驗。
3.使頁面結構化,提高可讀性,更加便於維護。
4.每個pagelet都是相互獨立的,如果有一個pagelet的內容不能載入,並不會影響其他的pagelet的內容顯示。
BigPipe 的原理非常簡單,並不會引入很多額外的負擔,適用範圍很廣,容易上手。幾乎所有的網頁都可以採用BigPipe的理念去進行優化,尤其對於是有著極大量資料和網頁較大的網站,將會以低成本帶來高回報。一般來講,網站越大,腳本和樣式表越多,流覽器版本越舊,網路環境越差,優化的結果越可觀。
Scribe
Scribe是用來收集日誌的服務器,它可以擴展到大規模的機器集群中,無論是網絡故障還是服務器節點故障,都不會對日誌收集造成影響。大規模集群系統中每個節點服務器上都運行了一個Scribe服務,這個Scribe服務器可以收集資訊然後將資訊發送到一個中央Scribe服務器(也可以是多個中央Scribe服務器)如果中央Scribe服務器(或中央服務器組)出現故障不可用的話,各個節點的Scibe服務器就會將日誌資訊寫到本地磁盤,待中央Scribe服務器恢復正常時再發送。中央Scribe服務器會將這些資訊寫檔保存到最終的磁盤位址,一般是nfs檔系統或者一個分佈式檔系統中,有時也會把這些日誌文件傳輸到其他層的Scribe服務器組中。
Scribe的獨特之處是客戶端日誌實例包含兩個字串:類別和信息(a category and a message)。類別(category)是對預期目標資訊的高層次描述,可以在Scribe服務器中進行配置,這樣就允許我們可以通過更改設定檔的方式轉移數據而不需要更改代碼。Scribe服務器也允許基於類別前綴(category prefix)進行配置,缺省狀態下可以在檔路徑中插入類別名稱,相當有靈活性和可擴展性,可通過“存儲(store)“抽象。Stores可以通過一個設定檔靜態配置,也可以在運行時無需停止服務器進行更改。
Scribe是對一個使用非阻斷C++服務器的thrift服務的實現,Facebook在上千台服務器上運行了Scribe服務,每天收集傳輸數十億的資訊。對於普通Facebook用戶而言, Scribe並無多大實際用處。但面向開源社區外公佈Scribe原始程式碼後,將有利於改變Facebook不願公佈原始程式碼的企業形象。
Haystack
Haystack是Facebook圖片管理架構,一般圖片應用系統有兩個關鍵點:
1.Metadata資訊存儲。由於圖片數量巨大,單機存放不了所有的Metadata資訊,假設每個圖片檔的Metadata佔用100位元組,260 billion圖片Metadata佔用的空間為260G * 100 = 26000GB。
2.減少圖片讀取的IO次數。在普通的Linux檔案系統中,讀取一個檔包括三次磁碟IO:讀取目錄中繼資料到記憶體,把檔的inode節點裝載到記憶體,最後讀取實際的檔內容。由於檔數太多,無法將所有目錄及檔案的inode資訊緩存到記憶體,因此磁片IO次數很難達到每個圖片讀取只需要一次磁碟IO的理想狀態。
3.圖片緩存。圖片寫入以後就不再修改,因此,需要對圖片進行緩存並且將緩存放到離用戶最近的位置,一般會使用CDN技術。
Facebook圖片系統初期的架構如下:
《圖六》
這個架構採用CDN作為圖片緩存,底層使用基於NAS的存儲,Photo Store Server 通過NFS掛載NAS中的圖片檔提供服務。使用者的圖片請求首先調度到最近的CDN節點,如果CDN緩存命中,直接將圖片內容返回給使用者;否則CDN請求後端的存儲系統,緩存並將圖片內容返回使用者。Facebook的經驗表明,SNS社交網站中CDN緩存的效果有限,存在大量的”長尾”請求。雖然user profile圖片請求的CDN命中率高達99.8%,但是其它圖片請求CDN命中率只有92%,大約1/10的請求最後落到了後端的存儲系統。這個架構的問題如下:
1.圖片存儲的IO次數過多。這個架構沒有解決圖片物理檔個數太多的問題,因此,無法將目錄和檔的inode資訊全部裝載到記憶體中,且後端存儲系統的訪問比較分散,很難緩存,每次圖片讀取需要多次(一般為3次)IO操作。
2.依賴于CDN提供商。Facebook使用Akamai & Limelight的CDN服務,不可控因素較多,成本也比較高。
Facebook Haystack新架構主要解決圖片存取IO次數過多的檔,主要的思路是多個邏輯檔共用同一個物理檔。Haystack架構及讀請求處理流程圖如下:
《圖七》
Haystack架構主要有三個部分:Haystack Directory,Haystack Store以及Haystack Cache。Haystack Store是物理存儲節點,以物理卷軸(physical volume)的形式組織存儲空間,每個物理卷軸一般很大,比如100GB,這樣10TB的資料也只有100個物理卷軸。每個物理卷軸對應一個物理檔,因此,每個存儲節點上的物理檔元資訊都很小。多個物理存儲節點上的物理卷軸組成一個邏輯卷軸(logical volume),用於備份。Haystack Directory存放邏輯卷軸和物理卷軸的對應關係,假設每個卷軸的大小為100GB,對應關係的條數為20PB / 100GB = 0.2MB,佔用的記憶體可以忽略。Haystack cache主要用於解決對CDN提供商過於依賴的問題,提供最近增加的圖片的緩存服務。
Haystack圖片讀取請求大致流程為:使用者訪問一個頁面時,Web Server請求Haystack Directory構造一個URL:http://
HayStack的幾點優勢:
1.採用羽量級的HayStack Directory維護邏輯卷到多個物理卷的映射關係,方便的實現了副本技術,以實現系統容錯。
2.簡化檔的中繼資料結構,以追加寫的方式往物理卷中存儲圖片,效率高。同時將圖片key與位置的映射關係全部保留在記憶體中,通過一次lookup即可獲取圖片的位置。
3.物理卷中所有的圖片都對應有index檔(固定大小,結構簡單),從而每次系統重啟時,物理卷的映射資訊能快速的通過index檔構建。
4.引入delete flag、compaction、batch upload以及進一步提高存儲的效率。
總結
Facebook所使用的相關技術,是一個綜合的結果,每一項技術都是為了處理其特殊的問題所採用或自行研發的,但彼此依然有相依性,例如Hadoop是整體分散式平臺架構,Thrift是為了解決各系統語言之間的溝通,因此Scribe, Haystack, BigPipe等技術需要互相溝通時,就會需要Thrift。下面以Facebook所提供的服務為構面,來列舉該服務用到了哪些技術:
1.前端程式採用PHP撰寫。寫完之後透過Facebook做的HipHop翻譯成C++程式碼,然後用g++來編譯。這樣的結果是前端在生成網頁以及執行網站邏輯的時候可以超快,高效能。
2.商業邏輯的元件都用Apache Thrift,以服務形式提供。撰寫語言可能是PHP, C++或是JAVA。
3.由於使用了Thrift來提供服務,運行JAVA程式碼的伺服器都是用Facebook自行研發的軟體,而不採用tomcat或Jetty這種,又再提升了些效能。
4.資料儲存的部份用了MySQL, Memcached, cassandra, 還有HBase。Memcached拿來做MySQL的暫存,也用作其他一般用途的暫存。近來,cassandra使用率有下降趨勢,而HBase在Facebook內的使用有日益提高的趨勢。
5.離線處理資料用的是Hadoop跟Hive。
6.紀錄檔,點擊數與feed等等是用Scribe來整合,並存在Scribe-HDFS裡。如果要分析,就用MapReduce。
7.為了加速瀏覽器上畫出網頁這件事,Facebook自製了BigPipe這個技術。
8.Varnish Cache用來做HTTP proxying。
9.Facebook數十億張的照片由Haystack來處理。這是Facebook自行研發的技術,低階且僅支援新增寫入動作。
10.Facebook訊息用了自己的動態叢集管理架構。商業邏輯跟儲存一併封裝成一個Cell,每個Cell處理一部份的使用者,因此使用者增加只需要增加Cell。儲存的部份用的是HBase。
11.Facebook訊息中的搜尋是透過在HBase上建立了反向索引。
12.聊天室是用Erlang開發的Epoll來完成,一樣透過Thrift服務界面來存取。
參考資料
1.做出Facebook規模,你所需要的技術元件總覽, http://www.inside.com.tw/2011/04/21/facebook-platform-component
2.BigPipe學習研究,http://kb.cnblogs.com/page/99459/
3.Finding a needle in Haystack: Facebook's photo storage, http://muratbuffalo.blogspot.com/2010/12/finding-needle-in-haystack-facebooks.html
4.Memcached, http://zh.wikipedia.org/wiki/Memcached
5.What is Facebook's architecture? http://www.quora.com/What-is-Facebooks-architecture
6.Facebook Scribe簡介, http://fan.renren.it/a/qitazonghe/other/2011/0226/78667.html
7.Facebook如何管理150億張照片http://www.bookfm.net/discussion/discussionview.html?did=100145
8.Facebook Haystack圖片儲存架構,http://www.nosqlnotes.net/archives/116
9.淺談Facebook圖片儲存系?HayStack概要, http://storage.it168.com/a2011/0515/1190/000001190566.shtml
沒有留言:
張貼留言