Tachyon:Spark生態系統中的分布式內存文件系統
Tachyon是Spark生態系統內快速崛起的一個新項目。 本質上, Tachyon是個分布式的內存文件系統, 它在減輕Spark內存壓力的同時,也賦予了Spark內存快速大量數據讀寫的能力。Tachyon把內存存儲的功能從Spark中分離出來, 使Spark可以更專注計算的本身, 以求通過更細的分工達到更高的執行效率。 本文將先向讀者介紹Tachyon在Spark生態系統中的使用, 也將分享百度在大數據平臺上利用Tachyon取得的性能改善的用例,以及在實際使用Tachyon過程中遇到的一些問題和解決方案。最后我們將介紹一下 Tachyon的一些新功能。
Tachyon簡介
Spark平臺以分布式內存計算的模式達到更高的計算性能,在最近引起了業界的廣泛關注,其開源社區也十分活躍。以百度為例,在百度內部計算平臺 已經搭建并運行了千臺規模的Spark計算集群,百度也通過其BMR的開放云平臺對外提供Spark計算平臺服務。然而,分布式內存計算的模式也是一柄雙 刃劍,在提高性能的同時不得不面對分布式數據存儲所產生的問題,具體問題主要有以下幾個:
- 當兩個Spark作業需要共享數據時,必須通過寫磁盤操作。比如:作業1要先把生成的數據寫入HDFS,然后作業2再從HDFS把數據讀出來。在此,磁盤的讀寫可能造成性能瓶頸。
- 由于Spark會利用自身的JVM對數據進行緩存,當Spark程序崩潰時,JVM進程退出,所緩存數據也隨之丟失,因此在工作重啟時又需要從HDFS把數據再次讀出。
- 當兩個Spark作業需操作相同的數據時,每個作業的JVM都需要緩存一份數據,不但造成資源浪費,也極易引發頻繁的垃圾收集,造成性能的降低。
仔細分析這些問題后,可以確認問題的根源來自于數據存儲,由于計算平臺嘗試自行進行存儲管理,以至于Spark不能專注于計算本身,造成整體執行 效率的降低。Tachyon的提出就是為了解決這些問題:本質上,Tachyon是個分布式的內存文件系統,它在減輕Spark內存壓力的同時賦予了 Spark內存快速大量數據讀寫的能力。Tachyon把存儲與數據讀寫的功能從Spark中分離,使得Spark更專注在計算的本身,以求通過更細的分 工達到更高的執行效率。
圖1: Tachyon的部署
圖1顯示了Tachyon的部署結構。Tachyon被部署在計算平臺(Spark,MR)之下以及存儲平臺(HDFS, S3)之上,通過全局地隔離計算平臺與存儲平臺, Tachyon可以有效地解決上文列舉的幾個問題,:
- 當兩個Spark作業需要共享數據時,無需再通過寫磁盤,而是借助Tachyon進行內存讀寫,從而提高計算效率。
- 在使用Tachyon對數據進行緩存后,即便在Spark程序崩潰JVM進程退出后,所緩存數據也不會丟失。這樣,Spark工作重啟時可以直接從Tachyon內存讀取數據了。
- 當兩個Spark作業需要操作相同的數據時,它們可以直接從Tachyon獲取,并不需要各自緩存一份數據,從而降低JVM內存壓力,減少垃圾收集發生的頻率。
Tachyon系統架構
在上一章我們介紹了Tachyon的設計,本章我們來簡單看看Tachyon的系統架構以及實現。 圖2顯示了Tachyon在Spark平臺的部署:總的來說,Tachyon有三個主要的部件:Master, Client,與Worker。在每個Spark Worker節點上,都部署了一個Tachyon Worker,Spark Worker通過Tachyon Client訪問Tachyon進行數據讀寫。所有的Tachyon Worker都被Tachyon Master所管理,Tachyon Master通過Tachyon Worker定時發出的心跳來判斷Worker是否已經崩潰以及每個Worker剩余的內存空間量。
圖2: Tachyon在Spark平臺的部署
圖3顯示了Tachyon Master的結構,其主要功能如下:首先,Tachyon Master是個主管理器,處理從各個Client發出的請求,這一系列的工作由Service Handler來完成。這些請求包括:獲取Worker的信息,讀取File的Block信息, 創建File等等;其次,Tachyon Master是個Name Node,存放著所有文件的信息,每個文件的信息都被封裝成一個Inode,每個Inode都記錄著屬于這個文件的所有Block信息。在Tachyon 中,Block是文件系統存儲的最小單位,假設每個Block是256MB,如果有一個文件的大小是1GB,那么這個文件會被切為4個Block。每個 Block可能存在多個副本,被存儲在多個Tachyon Worker中,因此Master里面也必須記錄每個Block被存儲的Worker地址;第三,Tachyon Master同時管理著所有的Worker,Worker會定時向Master發送心跳通知本次活躍狀態以及剩余存儲空間。Master是通過 Master Worker Info去記錄每個Worker的上次心跳時間,已使用的內存空間,以及總存儲空間等信息。
圖3: Tachyon的Master設計
圖4顯示了Tachyon Worker的結構,它主要負責存儲管理:首先,Tachyon Worker的Service Handler處理來自Client發來的請求,這些請求包括:讀取某個Block的信息,緩存某個Block,鎖住某個Block,向本地內存存儲要求 空間等等。第二,Tachyon Worker的主要部件是Worker Storage,其作用是管理Local Data(本地的內存文件系統)以及Under File System(Tachyon以下的磁盤文件系統,比如HDFS)。第三,Tachyon Worker還有個Data Server以便處理其他的Client對其發起的數據讀寫請求。當由請求達到時,Tachyon會先在本地的內存存儲找數據,如果沒有找到則會嘗試去其 他的Tachyon Worker的內存存儲中進行查找。如果數據完全不在Tachyon里,則需要通過Under File System的接口去磁盤文件系統(HDFS)中讀取。
圖4: Tachyon的Worker設計
圖5顯示了Tachyon Client的結構,它主要功能是向用戶抽象一個文件系統接口以屏蔽掉底層實現細節。首先,Tachyon Client會通過Master Client部件跟Tachyon Master交互,比如可以向Tachyon Master查詢某個文件的某個Block在哪里。Tachyon Client也會通過Worker Client部件跟Tachyon Worker交互, 比如向某個Tachyon Worker請求存儲空間。在Tachyon Client實現中最主要的是Tachyon File這個部件。在Tachyon File下實現了Block Out Stream,其主要用于寫本地內存文件;實現了Block In Stream主要負責讀內存文件。在Block In Stream內包含了兩個不同的實現:Local Block In Stream主要是用來讀本地的內存文件,而Remote Block In Stream主要是讀非本地的內存文件。請注意,非本地可以是在其它的Tachyon Worker的內存文件里,也可以是在Under File System的文件里。
圖5: Tachyon的Client設計
現在我們通過一個簡單的場景把各個部件都串起來:假設一個Spark作業發起了一個讀請求,它首先會通過Tachyon Client去Tachyon Master查詢所需要的Block所在的位置。如果所在的Block不在本地的Tachyon Worker里,此Client則會通過Remote Block In Stream向別的Tachyon Worker發出讀請求,同時在Block讀入的過程中,Client也會通過Block Out Stream把Block寫入到本地的內存存儲里,這樣就可以保證下次同樣的請求可以由本機完成。
Tachyon在百度內部的使用
在百度內部,我們使用Spark SQL進行大數據分析工作, 由于Spark是個基于內存的計算平臺,我們預計絕大部分的數據查詢應該在幾秒或者十幾秒完成以達到互動查詢的目的。可是在Spark計算平臺的運行中, 我們卻發現查詢都需要上百秒才能完成,其原因如圖6所示:我們的計算資源(Data Center 1)與數據倉庫(Data Center 2)可能并不在同一個數據中心里面,在這種情況下,我們每一次數據查詢都可能需要從遠端的數據中心讀取數據,由于數據中心間的網絡帶寬以及延時的問題,導 致每次查詢都需要較長的時間(>100秒)才能完成。更糟糕的是,很多查詢的重復性很高,同樣的數據很可能會被查詢多次,如果每次都從遠端的數據中 心讀取,必然造成資源浪費。
為了解決這個問題,我們借助Tachyon把數據緩存在本地,盡量避免跨數據中心調數據。當Tachyon被部署到Spark所在的數據中心后, 每次數據冷查詢時,我們還是從遠端數據倉庫拉數據,但是當數據再次被查詢時,Spark將從同一數據中心的Tachyon中讀取數據,從而提高查詢性能。 實驗表明:如果從非本機的Tachyon讀取數據,耗時降到10到15秒,比原來的性能提高了10倍;最好的情況下,如果從本機的Tachyon讀數據, 查詢僅需5秒,比原來的性能提高了30倍,效果相當明顯。
在使用了這個優化后,熱查詢性能達到了互動查詢的要求,可是冷查詢的用戶體驗還是很差。分析了用戶行為后,我們發現用戶查詢的模式比較固定:比如 很多用戶每天都會跑同一個查詢,只是所使用過濾數據的日期會發生改變。借助這次特性,我們可以根據用戶的需求進行線下預查詢,提前把所需要的數據導入 Tachyon,從而避免用戶冷查詢。
圖6: Tachyon在百度大數據平臺的部署
在使用Tachyon過程中,我們也遇到了一些問題:在剛開始部署Tachyon的時候, 我們發現數據完全不能被緩存,第一次與后續的查詢耗時是一樣的。如圖7的源代碼所示:只有整個數據Block被讀取后,這個Block才會被緩存住;否則 緩存的操作會被取消。比如一個Block是256MB,如果你讀了其中的255MB,這個Block還是不會被緩存,因為它只需讀取整個block中的部 分數據。在百度內部,我們很多數據是用行列式存儲的,比如ORC與Parquet文件,每次查詢只會讀其中的某幾列, 因此不會讀取完整的Block, 以致block緩存失敗。為了解決這個問題,我們對Tachyon進行了修改,如果數據Block不是太大的話,冷查詢時即使用戶請求的只是其中幾列,我 們也會把整個Block都讀進來,保證整個Block能被緩存住,然后再次查詢的話就可以直接從Tachyon讀取了。在使用了修改的版本 后,Tachyon達到了我們期待的效果,大部分查詢可以在10秒內完成。
圖7: Tachyon緩存數據邏輯
Tachyon的一些新功能
我們把Tachyon當作緩存來使用,但是每臺機器的內存有限,內存很快會被用完。 如果我們有50臺機器,每臺分配20GB的內存給Tachyon,那么總共也只有1TB的緩存空間,遠遠不能滿足我們的需要。在Tachyon最新版本有 一個新的功能: Hierarchical Storage,即使用不同的存儲媒介對數據分層次緩存。如圖8所示,它類于CPU的緩存設計:內存的讀寫速度最快所以可以用于第0級緩存,然后SSD可 以用于第1級緩存,最后本地磁盤可以作為底層緩存。這樣的設計可以為我們提供更大的緩存空間,同樣50臺機器,現在我們每臺可貢獻出20TB的緩存空間, 使總緩存空間達到1PB,基本可以滿足我們的儲存需求。與CPU緩存類似,如果Tachyon的block Replacement Policy設計得當,99%的請求可以被第0級緩存(內存)所滿足,從而在絕大部分時間可以做到秒級響應。
圖8: Tachyon Hierarchical Storage
當Tachyon收到讀請求時,它首先檢查數據是否在第0層,如果命中,直接返回數據,否則它會查詢下一層緩存,直到找到被請求的數據為止。數據 找到后會直接返回給用戶,同時也會被Promote到第0層緩存,然后第0層被替換的數據Block會被LRU算法置換到下一層緩存。如此一來,如果用戶 再次請求相同的數據就會直接從第0層快速得到,從而充分發揮緩存的Locality特性。
當Tachyon收到寫請求時,它首先檢查第0層是否有足夠空間,如果有,則直接寫入數據后返回。否則它會查詢下一層緩存,直到找到一層緩存有足 夠空間,然后把上一層的一個Block用LRU算法推到下一層,如此類推,直到把第0層有足夠空間以寫入新的數據,然后再返回。這么做的目的是保證數據被 寫入第0層,如果讀請求馬上發生在寫請求后,數據可以快速被讀取。可是,這樣做的話寫的性能有可能變的很差:比如頭兩層緩存都滿的話,它需要把一個 Block從第1層丟到第2層,再把一個Block從第0層丟到第1層,然后才能寫數據到第0層,再返回給用戶。
對此我們做了個優化, 與其層層類推騰出空間,我們的算法直接把數據寫入有足夠空間的緩存層,然后快速返回給用戶。如果緩存全滿,則把底層的一個Block置換掉,然后把數據寫 入底層緩存后返回。經過實驗,我們發現優化后的做法會把寫延時降低約50%,大大的提高了寫的效率。但是讀的效率又如何呢,由于在TACHYON里,寫是 通過Memory-Mapped File進行的,所以是先寫入內存,再Flush到磁盤,如果讀是馬上發生在寫之后的話,其實會從操作系統的Buffer,也就是內存里讀數據,因此讀的 性能也不會下降。
Hierarchical Storage很好地解決了我們緩存不夠用的問題,下一步我們將繼續對其進行優化。比如,現在它只有LRU一種置換算法,并不能滿足所有的應用場景, 我們將針對不同的場景設計更高效的置換算法,盡量提高緩存命中率。
結語
我個人相信更細的分工會達到更高的效率,Spark作為一個內存計算平臺,如果使用過多的資源去緩存數據,會引發頻繁的垃圾收集,造成系統的不穩 定,或者影響性能。在我們使用Spark的初期,系統不穩定是我們面臨的最大挑戰,而頻繁的垃圾收集正是引起系統不穩定最大的原因。比如當一次垃圾收集耗 時過長時,Spark Worker變的響應非常不及時,很容易被誤認為已經崩潰,導致任務重新執行。Tachyon通過把內存存儲的功能從Spark中分離出來,讓Spark 更專注在計算本身,從而很好的解決了這個問題。隨著內存變的越來越便宜,我們可以預期未來一段時間里,我們的服務器里可使用的內存會不斷增 長,Tachyon會在大數據平臺中發揮越來越重要的作用。現在還是Tachyon發展的初期,在本文完成時Tachyon才準備發布0.6版,還有很多 功能亟需完善,這也是一個好機遇,有興趣的同學們可以多關注Tachyon,到社區里進行技術討論以及功能開發。
劉少山
百度美國硅谷研發中心高級架構師,主要研究方向分布式系統以及大數據計算與存儲平臺。