騰訊大規模Hadoop叢集實踐

hunterjoy發表於2014-02-27

TDW(Tencent distributed Data Warehouse,騰訊分散式資料倉儲)基於開源軟體Hadoop和Hive進行構建,打破了傳統資料倉儲不能線性擴充套件、可控性差的侷限,並且根據騰訊資料量大、計算複雜等特定情況進行了大量優化和改造。

TDW服務覆蓋了騰訊絕大部分業務產品,單叢集規模達到4400臺,CPU總核數達到10萬左右,儲存容量達到100PB;每日作業數100多萬,每日計算量4PB,作業併發數2000左右;實際儲存資料量80PB,檔案數和塊數達到6億多;儲存利用率83%左右,CPU利用率85%左右。經過四年多的持續投入和建設,TDW已經成為騰訊最大的離線資料處理平臺。

TDW的功能模組主要包括:Hive、MapReduce、HDFS、TDBank、Lhotse等,如圖1所示。TDW Core主要包括儲存引擎HDFS、計算引擎MapReduce、查詢引擎Hive,分別提供底層的儲存、計算、查詢服務,並且根據公司業務產品的應用情況進行了很多深度訂製。TDBank負責資料採集,旨在統一資料接入入口,提供多樣的資料接入方式。Lhotse任務排程系統是整個資料倉儲的總管,提供一站式任務排程與管理。

圖1  TDW的功能模組

圖1 TDW的功能模組 

建設單個大規模叢集的原因

隨著業務的快速增長,TDW的節點數也在增加,對單個大規模Hadoop叢集的需求也越來越強烈。TDW需要做單個大規模叢集,主要是從資料共享、計算資源共享、減輕運營負擔和成本等三個方面考慮。

1. 資料共享。TDW之前在多個IDC部署數十個叢集,主要是根據業務分別部署,這樣當一個業務需要其他業務的資料,或者需要公共資料時,就需要跨叢集或者跨IDC訪問資料,這樣會佔用IDC之間的網路頻寬。為了減少跨IDC的資料傳輸,有時會將公共資料冗餘分佈到多個IDC的叢集,這樣又會帶來儲存空間浪費。

2. 計算資源共享。當一個叢集的計算資源由於某些原因變得緊張時,例如需要資料補錄時,這個叢集的計算資源就捉襟見肘,而同時,另一個叢集的計算資源可能空閒,但這兩者之間沒有做到互通有無。

3. 減輕運營負擔和成本。十幾個叢集同時需要穩定運營,而且當一個叢集的問題解決時,也需要解決其他叢集已經出現的或者潛在的問題。一個Hadoop版本要在十幾個叢集逐一變更,監控系統也要在十幾個叢集上部署。這些都給運營帶來了很大負擔。此外,分散的多個小叢集,資源利用率不高,機器成本較大。

建設單個大規模叢集的方案及優化

面臨的挑戰

TDW從單叢集400臺規模建設成單叢集4000臺規模,面臨的最大挑戰是Hadoop架構的單點問題:計算引擎單點JobTracker負載重,使得排程效率低、叢集擴充套件性不好;儲存引擎單點NameNode沒有容災,使得重啟耗時長、不支援灰度變更、具有丟失資料的風險。TDW單點瓶頸導致平臺的高可用性、高效性、高擴充套件性三方面都有所欠缺,將無法支撐4000臺規模。為了解決單點瓶頸,TDW主要進行了JobTracker分散化和NameNode高可用兩方面的實施。

JobTracker分散化

1.單點JobTracker的瓶頸

TDW以前的計算引擎是傳統的兩層架構,單點JobTracker負責整個叢集的資源管理、任務排程和任務管理,TaskTracker負責任務執行。JobTracker的三個功能模組耦合在一起,而且全部由一個Master節點負責執行,當叢集併發任務數較少時,這種架構可以正常執行,但當叢集併發任務數達到2000、節點數達到4000時,任務排程就會出現瓶頸,節點心跳處理遲緩,叢集擴充套件也會遇到瓶頸。

2.JobTracker分散化方案

TDW借鑑YARN和Facebook版corona設計方案,進行了計算引擎的三層架構優化(如圖2所示):將資源管理、任務排程和任務管理三個功能模組解耦;JobTracker只負責任務管理功能,而且一個JobTracker只管理一個Job;將比較輕量的資源管理功能模組剝離出來交給新的稱為ClusterManager的Master負責執行;任務排程也剝離出來,交給具有資源資訊的ClusterManager負責執行;對效能要求較高的任務排程模組採用更加精細的排程方式。

圖2  JobTracker分散化架構

圖2 JobTracker分散化架構 

新架構下三個角色分別是:ClusterManager負責整個叢集的資源管理和任務排程,JobTracker負責單個Job的管理,TaskTracker負責任務的執行。

(1)兩路心跳。之前的架構下,TaskTracker向JobTracker上報心跳,JobTracker序列地處理這些心跳,心跳處理中進行節點管理、任務管理、任務排程等,心跳繁重,影響任務排程和叢集擴充套件性。新架構下,心跳被拆分成兩路心跳,分別上報任務和資源資訊。

JobTracker獲知任務資訊通過任務上報心跳的方式。任務上報心跳是通過任務所在的TaskTracker啟動一個新的獨立執行緒向對應的JobTracker上報心跳這條途徑,在同一個TaskTracker上,不同Job的任務使用不同的執行緒向不同的JobTracker上報心跳,途徑分散,提升了心跳上報效率。

TaskTracker通過上報心跳的方式將資源資訊彙報給ClusterManager。ClusterManager從TaskTracker的心跳中獲取節點的資源資訊:CPU數量、記憶體空間大小、磁碟空間大小等的總值和剩餘值,根據這些資訊判斷節點是否還能執行更多的任務。同時,ClusterManager通過TaskTracker與其之間維繫的心跳來管理節點的生死存亡。

以前繁重的一路心跳被拆分成了兩路輕量的心跳,心跳間隔由40s優化成1s,叢集的可擴充套件性得到了提升。

(2)資源概念。之前架構只有slot概念,一般根據核數來設定slot數量,對記憶體、磁碟空間等沒有控制。新架構弱化了slot概念,加強了資源的概念。

每個資源請求包括具體的物理資源需求描述,包括記憶體、磁碟和CPU等。向ClusterManager進行資源申請的有三種來源型別:Map、Reduce、JobTracker,每種來源需要的具體資源量不同。在CPU資源上,排程器仍然保留slot概念,並且針對三種來源保證各自固定的資源帽。

例如,對於24核的節點,配置13個核給Map用、6個核給Reduce用、1個核給JobTracker用,則認為該節點上有1個JobTracker slot、13個Map slot、6個Reduce slot。某個Map請求的資源需要2個核,則認為需要兩個Map slot,當一個節點的Map slot用完之後,即使有剩餘的CPU,也不會繼續分配Map予其執行了。記憶體空間、磁碟空間等資源沒有slot概念,剩餘空間大小滿足需求即認為可以分配。在查詢滿足資源請求的節點時,會比較節點的這些剩餘資源是否滿足請求,而且還會優先選擇負載低於叢集平均值的節點。

(3)獨立併發式的下推排程。之前架構下,排程器採用的是基於心跳模型的拉取排程:任務排程依賴於心跳,Map、Reduce的排程耦合在一起,而且對請求優先順序採取全排序方式,時間複雜度為nlog(n),任務排程效率低下。

新架構採用獨立併發式的下推排程。Map、Reduce、JobTracker三種資源請求使用三個執行緒進行獨立排程,對請求優先順序採取堆排序的方式,時間複雜度為log(n)。當有資源滿足請求時,ClusterManager直接將資源下推到請求者,而不再被動地等待TaskTracker通過心跳的方式獲取分配的資源。

例如,一個Job有10個Map,每個Map需要1個核、2GB記憶體空間、10GB磁碟空間,如果有足夠的資源,Map排程執行緒查詢到了滿足這10個Map的節點列表,ClusterManager會把節點列表下推到JobTracker;如果Map排程執行緒第一次只查詢到了滿足5個Map的節點列表,ClusterManager會把這個列表下推到JobTracker,隨後Map排程執行緒查詢到了剩下5個Map的節點列表,ClusterManager再把這個列表下推到JobTracker。

以前基於心跳模型的拉取排程被優化成獨立併發式的下推排程之後,平均排程處理時間由80ms優化至1ms,叢集的排程效率得到了提升。

3. Job提交過程

新架構下,一次Job提交過程,需要Client和ClusterManager、TaskTracker均進行互動(如圖3所示):JobClient先向ClusterManager申請啟動JobTracker所需要的資源;申請到之後,JobClient在指定的TaskTracker上啟動JobTracker程式,將Job提交給JobTracker;JobTracker再向ClusterManager申請Map和Reduce資源;申請到之後,JobTracker將任務啟動命令提交給指定的TaskTracker。

圖3  Job提交過程

圖3 Job提交過程 

4. 存在的問題及應對措施

JobTracker分散化方案給計算引擎帶來高效性和高擴充套件性,但沒有帶來高可用性,單一故障點的問題在此方案中仍然存在,此時的單一故障點問題有別於以前,如下所述。

(1)ClusterManager如果發生故障,不會造成Job狀態丟失而且在短時間內即可恢復。它只儲存資源情況,不儲存狀態,ClusterManager在很短的時間內可以重啟完成。重啟之後,TaskTracker重新向ClusterManager彙報資源,ClusterManager從重啟至完全獲得叢集的資源情況整個階段可以在10秒內完成。

(2)JobTracker如果發生故障,只會影響單個Job,對其他Job不會造成影響。

基於以上兩點,認為新方案的單一故障點問題影響不大,而且考慮方案實施的複雜度和時效性,TDW在JobTracker分散化方案中沒有設計高可用方案,而是通過外圍系統來降低影響:監控系統保證ClusterManager故障及時發現和恢復;Lhotse排程系統從使用者任務級別保證Job重試。

NameNode高可用

1. 單點NameNode的問題

TDW以前的儲存引擎是單點NameNode,在一個業務對應一個叢集的情況下,NameNode壓力較小,出故障的機率也較小,而且NameNode單點故障帶來的影響不會波及全部業務。但當把各個小叢集統一到大叢集,各個業務都儲存之上時,NameNode壓力變大,出故障的機率也變大,NameNode單點故障造成的影響將會非常嚴重。即使是計劃內變更,停止NameNode服務耗時將近2個小時,計劃內的停止服務變更也給使用者帶來了較大的影響。

2. NameNode高可用方案

TDW設計了一種一主兩熱備的NameNode高可用方案。新架構下NameNode角色有三個:一主(ActiveNameNode)兩熱備(BackupNameNode)。ActiveNameNode儲存namespace和block資訊,對DataNode下發命令,並且對客戶端提供服務。BackupNameNode包括standby和newbie兩種狀態:standby提供對ActiveNameNode後設資料的熱備,在ActiveNameNode失效後接替其對外提供服務,newbie狀態是正處於學習階段,學習完畢之後成為standby。

(1)Replicaton協議。為了實現BackupNameNode對ActiveNameNode的後設資料一致,隨時準備接管ActiveNameNode角色,後設資料操作日誌需要在主備間同步。客戶端對後設資料的修改不止在ActiveNameNode記錄事務日誌,事務日誌還需要從ActiveNameNode同步到BackupNameNode,客戶端的每一次寫操作,只有成功寫入ActiveNameNode以及至少一個BackupNameNode(或者ZooKeeper)時,才返回客戶端操作成功。當沒有BackupNameNode可寫入時,把事務日誌同步到ZooKeeper來保證至少有一份事務日誌備份。

客戶端寫操作記錄事務日誌遵循以下幾個原則:

①寫入ActiveNameNode,如果寫入失敗,返回操作失敗,ActiveNameNode自動退出;

②當寫入至少兩個節點(Active-NameNode和Standby/ZooKeeper/LOG_SYNC newbie)時返回操作成功,其他返回失敗;LOG_SYNC newbie表示newbie已經從ActiveNameNode獲取到全量日誌後的狀態;

③當只成功寫入ActiveNameNode,此後的Standby和ZooKeeper均寫入失敗時,返回失敗;

④當只存在ActiveNameNode時,進入只讀狀態。

(2)Learning協議。newbie學習機制確保newbie啟動後通過向ActiveNameNode學習獲取最新的後設資料資訊,學習到與ActiveNameNode同步時變成standby狀態。newbie從ActiveNameNode獲取最新的fsimage和edits檔案列表,ActiveNameNode還會和newbie之間建立事務日誌傳輸通道,將後續操作日誌同步給newbie,newbie將這些資訊載入記憶體,構建最新的後設資料狀態。

(3)事務日誌序號。為了驗證事務日誌是否丟失或者重複,為事務日誌指定遞增連續的記錄號txid。在事務日誌檔案edits中加入txid,保證txid的連續性,日誌傳輸和載入時保證txid連續遞增,儲存記憶體中的後設資料資訊到fsimage檔案時,將當前txid寫入fsimage頭部,載入fsimage檔案到記憶體中時,設定後設資料當前txid為fsimage頭部的txid。安全日誌序號(safe txid)儲存在ZooKeeper上,ActiveNameNode週期性地將txid寫入ZooKeeper作為safe txid,在BackupNameNode轉換為ActiveNameNode時,需要檢查BackupNameNode當前的txid是否小於safe txid,若小於則禁止這次角色轉換。

(4)checkpoint協議。新架構仍然具有checkpoint功能,以減少日誌的大小,縮短重啟時構建後設資料狀態的耗時。由ActiveNameNode維護一個checkpoint執行緒,週期性地通知所有standby做checkpoint,指定其中的一個將產生的fsimage檔案上傳給ActiveNameNode。

(5)DataNode雙報。Block副本所在的節點列表是NameNode後設資料資訊的一部分,為了保證這部分資訊在主備間一致性,DataNode採用雙報機制。DataNode對塊的改動會同時廣播到主備,對主備下發的命令,DataNode區別對待,只執行主機下發的命令而忽略掉備機下發的命令。

(6)引入ZooKeeper。主要用來做主節點選舉和記錄相關日誌:NameNode節點狀態、安全日誌序號、必要時記錄edit log。

3. 主備切換過程

當主退出時主備狀態切換的過程(如圖4所示):當ActiveNameNode節點IP1由於某些原因退出時,兩個備節點IP2和IP3通過向ZooKeeper搶鎖競爭主節點角色;IP2搶到鎖成為ActiveNameNode,客戶端從ZooKeeper上重新獲取主節點資訊,和IP2進行互動,這時即使IP1服務恢復,也是newbie狀態;事務日誌在主備間同步,newbie IP1通過向主節點IP2學習成為standby狀態。

圖4  主退出時主備狀態切換

圖4 主退出時主備狀態切換 

4. 存在的問題

NameNode高可用方案給儲存引擎帶來了高可用性,但在高效性方面做出了一些犧牲,由於事務日誌需要同步,寫效能有20%左右的下降。

其他優化

TDW在實施大叢集過程中,除了主要實施JobTracker分散化和NameNode高可用兩個方案,還進行了一些其他優化。

1. NameNode分散化

隨著儲存量和業務的不斷增長,一個HDFS後設資料空間的訪問壓力與日俱增。通過NameNode分散化來減少一個後設資料空間的訪問壓力。NameNode分散化主要對後設資料資訊進行分拆,對使用者透明,使用者訪問認為處於同一個儲存引擎,底層可以拆分成多個叢集。TDW在Hive層增加使用者到HDFS叢集的路由表,使用者表的資料將寫入對應的HDFS叢集,對外透明,使用者只需使用標準的建表語句即可。TDW根據公司業務的實際應用場景,根據業務線和共享資料等把資料分散到兩個HDFS叢集,有利於資料共享同時也儘量規避叢集間的資料拷貝。採用簡單、改動最少的方案解決了實際的問題。

2. HDFS相容

TDW內部有三個HDFS版本:0.20.1、CDH3u3、2.0,線上主流版本是CDH3u3,主流HDFS版本使用的RPC框架尚未優化成Thrift或者Protocol Buffers等,三個版本互不相容,增加了互相訪問的困難。通過RPC層相容方式實現了CDH3u3和0.20.1之間的互通,通過完全實現兩套介面方式實現了CDH3u3和2.0之間的互通。

3. 防止資料誤刪除

重要資料的誤刪除會給TDW帶來不可估量的影響,TDW為了進一步增加資料儲存可靠性,不僅開啟NameNode回收站特性,還增加兩個特性: 刪除黑白名單,刪除介面修改成重新命名介面,白名單中的目錄可以被刪除,白名單中的IP可以進行刪除操作,其他則不可;DataNode回收站,塊刪除操作不會立即進行磁碟檔案的刪除,而是維護在待刪除佇列裡,過期之後才進行實際的刪除操作,這樣可以保證在一定時間內如果發現重要的資料被誤刪除時可以進行資料恢復,還可以防止NameNode啟動之後後設資料意外缺失而造成資料直接被刪除的風險。

結語

TDW從實際情況出發,採取了一系列的優化措施,成功實施了單個大規模叢集的建設。為了滿足使用者日益增長的計算需求,TDW正在進行更大規模叢集的建設,並向實時化、集約化方向發展。TDW準備引入YARN作為統一的資源管理平臺,在此基礎上構建離線計算模型和Storm、Spark、Impala等各種實時計算模型,為使用者提供更加豐富的服務。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14766526/viewspace-1129479/,如需轉載,請註明出處,否則將追究法律責任。

相關文章