1. 程式人生 > >對海量小檔案儲存優化的一些理解和TFS介紹

對海量小檔案儲存優化的一些理解和TFS介紹

在研究圖片伺服器問題時,瞭解到現在很多大公司基本上都是用分散式檔案系統來儲存海量小檔案,比如Facebook有haystack,淘寶有TFS,京東有JFS。最近在研究TFS,結合之前學習的linux下的inode相關知識,瞭解到在ext檔案系統中,對一個檔案的讀需要先從磁碟中讀取到檔案對應的Inode,然後根據inode獲取到對應的block的位置資訊,進行第二次磁碟讀取目標檔案的內容,這樣就需要兩次磁碟IO。在系統中會有inode快取,這樣可保證一些檔案讀取只需要一次磁碟IO。

在儲存向圖片這樣的小檔案時,一般不超過1M,一些可能之後幾kb,這樣就會是海量檔案,總佔用容量不大。linux在格式化磁碟時,會預設每1kB或2KB就分配一個inode,inode table 和 資料區分開儲存,一個磁碟的大小為1GB,inode按128B算,inode佔的總大小為128MB,這樣在比較大的硬碟中,inode數會更多,佔用的空間也會更大。inode可以放到cache中,加快檔案讀,當檔案的數量少時,需要加入到記憶體中的inode數量少,可以全部快取到記憶體中,減少一次磁碟IO,可以提升讀取檔案的效能,但是當是海量的小檔案時,對應的inode數就比較多,幾乎無法全部載入到記憶體中,那麼檔案的讀可能是隨機的,不在一個block中,那麼會有很多檔案inode無法在快取中命中,需要兩次磁碟IO,讀檔案效能會比較低。

先在應對海量小檔案的優化策略,一般都會包括把小檔案進行合併儲存,比如Facebook開源的haystack和淘寶的TFS都採用了這樣的優化策略,通過合併檔案減少inode的數量,也就是原資料的大小減小,達到基本上可以全部載入到記憶體中,可以實現一次磁碟IO就可以讀取到檔案,大大提升讀取檔案的效能,解決了海量小檔案的讀取效能問題。

下邊是TFS的官方文件,我就直接搬到這裡了:

簡介

TFS(Taobao !FileSystem)是一個高可擴充套件、高可用、高效能、面向網際網路服務的分散式檔案系統,主要針對海量的非結構化資料,它構築在普通的Linux機器叢集上,可為外部提供高可靠和高併發的儲存訪問。TFS為淘寶提供海量小檔案儲存,通常檔案大小不超過1M,滿足了淘寶對小檔案儲存的需求,被廣泛地應用在淘寶各項應用中。它採用了HA架構和平滑擴容,保證了整個檔案系統的可用性和擴充套件性。同時扁平化的資料組織結構,可將檔名對映到檔案的實體地址,簡化了檔案的訪問流程,一定程度上為TFS提供了良好的讀寫效能。

TFS的總體結構

一個TFS叢集由兩個!NameServer節點(一主一備)和多個!DataServer節點組成。這些服務程式都是作為一個使用者級的程式執行在普通Linux機器上的。

在TFS中,將大量的小檔案(實際資料檔案)合併成為一個大檔案,這個大檔案稱為塊(Block), 每個Block擁有在叢集內唯一的編號(Block Id), Block Id在!NameServer在建立Block的時候分配, !NameServer維護block與!DataServer的關係。Block中的實際資料都儲存在!DataServer上。而一臺!DataServer伺服器一般會有多個獨立!DataServer程序存在,每個程序負責管理一個掛載點,這個掛載點一般是一個獨立磁碟上的檔案目錄,以降低單個磁碟損壞帶來的影響。

!NameServer主要功能是: 管理維護Block和!DataServer相關資訊,包括!DataServer加入,退出, 心跳資訊, block和!DataServer的對應關係建立,解除。正常情況下,一個塊會在!DataServer上存在, 主!NameServer負責Block的建立,刪除,複製,均衡,整理, !NameServer不負責實際資料的讀寫,實際資料的讀寫由!DataServer完成。

!DataServer主要功能是: 負責實際資料的儲存和讀寫。

同時為了考慮容災,!NameServer採用了HA結構,即兩臺機器互為熱備,同時執行,一臺為主,一臺為備,主機繫結到對外vip,提供服務;當主機器宕機後,迅速將vip繫結至備份!NameServer,將其切換為主機,對外提供服務。圖中的HeartAgent就完成了此功能。



TFS的塊大小可以通過配置項來決定,通常使用的塊大小為64M。TFS的設計目標是海量小檔案的儲存,所以每個塊中會儲存許多不同的小檔案。!DataServer程序會給Block中的每個檔案分配一個ID(File ID,該ID在每個Block中唯一),並將每個檔案在Block中的資訊存放在和Block對應的Index檔案中。這個Index檔案一般都會全部load在記憶體,除非出現!DataServer伺服器記憶體和叢集中所存放檔案平均大小不匹配的情況。

另外,還可以部署一個對等的TFS叢集,作為當前叢集的輔叢集。輔叢集不提供來自應用的寫入,只接受來自主叢集的寫入。當前主叢集的每個資料變更操作都會重放至輔叢集。輔叢集也可以提供對外的讀,並且在主叢集出現故障的時候,可以接管主叢集的工作。

平滑擴容

原有TFS叢集執行一定時間後,叢集容量不足,此時需要對TFS叢集擴容。由於DataServer與NameServer之間使用心跳機制通訊,如果系統擴容,只需要將相應數量的新!DataServer伺服器部署好應用程式後啟動即可。這些!DataServer伺服器會向!NameServer進行心跳彙報。!NameServer會根據!DataServer容量的比率和!DataServer的負載決定新資料寫往哪臺!DataServer的伺服器。根據寫入策略,容量較小,負載較輕的伺服器新資料寫入的概率會比較高。同時,在叢集負載比較輕的時候,!NameServer會對!DataServer上的Block進行均衡,使所有!DataServer的容量儘早達到均衡。

進行均衡計劃時,首先計算每臺機器應擁有的blocks平均數量,然後將機器劃分為兩堆,一堆是超過平均數量的,作為移動源;一類是低於平均數量的,作為移動目的。

移動目的的選擇:首先一個block的移動的源和目的,應該保持在同一網段內,也就是要與另外的block不同網段;另外,在作為目的的一定機器內,優先選擇同機器的源到目的之間移動,也就是同臺!DataServer伺服器中的不同!DataServer程序。
當有伺服器故障或者下線退出時(單個叢集內的不同網段機器不能同時退出),不影響TFS的服務。此時!NameServer會檢測到備份數減少的Block,對這些Block重新進行資料複製。

在建立複製計劃時,一次要複製多個block, 每個block的複製源和目的都要儘可能的不同,並且保證每個block在不同的子網段內。因此採用輪換選擇(roundrobin)演算法,並結合加權平均。

由於DataServer之間的通訊是主要發生在資料寫入轉發的時候和資料複製的時候,叢集擴容基本沒有影響。假設一個Block為64M,數量級為1PB。那麼NameServer上會有 1 * 1024 * 1024 * 1024 / 64 = 16.7M個block。假設每個Block的元資料大小為0.1K,則佔用記憶體不到2G。

儲存機制

在TFS中,將大量的小檔案(實際使用者檔案)合併成為一個大檔案,這個大檔案稱為塊(Block)。TFS以Block的方式組織檔案的儲存。每一個Block在整個叢集內擁有唯一的編號,這個編號是由NameServer進行分配的,而DataServer上實際儲存了該Block。在!NameServer節點中儲存了所有的Block的資訊,一個Block儲存於多個!DataServer中以保證資料的冗餘。對於資料讀寫請求,均先由!NameServer選擇合適的!DataServer節點返回給客戶端,再在對應的!DataServer節點上進行資料操作。!NameServer需要維護Block資訊列表,以及Block與!DataServer之間的對映關係,其儲存的元資料結構如下:



在!DataServer節點上,在掛載目錄上會有很多物理塊,物理塊以檔案的形式存在磁碟上,並在!DataServer部署前預先分配,以保證後續的訪問速度和減少碎片產生。為了滿足這個特性,!DataServer現一般在EXT4檔案系統上執行。物理塊分為主塊和擴充套件塊,一般主塊的大小會遠大於擴充套件塊,使用擴充套件塊是為了滿足檔案更新操作時檔案大小的變化。每個Block在檔案系統上以“主塊+擴充套件塊”的方式儲存。每一個Block可能對應於多個物理塊,其中包括一個主塊,多個擴充套件塊。
在DataServer端,每個Block可能會有多個實際的物理檔案組成:一個主Physical Block檔案,N個擴充套件Physical Block檔案和一個與該Block對應的索引檔案。Block中的每個小檔案會用一個block內唯一的fileid來標識。!DataServer會在啟動的時候把自身所擁有的Block和對應的Index載入進來。

容錯機制

  1. 叢集容錯

TFS可以配置主輔叢集,一般主輔叢集會存放在兩個不同的機房。主叢集提供所有功能,輔叢集只提供讀。主叢集會把所有操作重放到輔叢集。這樣既提供了負載均衡,又可以在主叢集機房出現異常的情況不會中斷服務或者丟失資料。

  1. !NameServer容錯

Namserver主要管理了!DataServer和Block之間的關係。如每個!DataServer擁有哪些Block,每個Block存放在哪些!DataServer上等。同時,!NameServer採用了HA結構,一主一備,主NameServer上的操作會重放至備NameServer。如果主NameServer出現問題,可以實時切換到備NameServer。
另外!NameServer和!DataServer之間也會有定時的heartbeat,!DataServer會把自己擁有的Block傳送給!NameServer。!NameServer會根據這些資訊重建!DataServer和Block的關係。

  1. !DataServer容錯

TFS採用Block儲存多份的方式來實現!DataServer的容錯。每一個Block會在TFS中存在多份,一般為3份,並且分佈在不同網段的不同!DataServer上。對於每一個寫入請求,必須在所有的Block寫入成功才算成功。當出現磁碟損壞!DataServer宕機的時候,TFS啟動複製流程,把備份數未達到最小備份數的Block儘快複製到其他DataServer上去。 TFS對每一個檔案會記錄校驗crc,當客戶端發現crc和檔案內容不匹配時,會自動切換到一個好的block上讀取。此後客戶端將會實現自動修復單個檔案損壞的情況。

併發機制

對於同一個檔案來說,多個使用者可以併發讀。
現有TFS並不支援併發寫一個檔案。一個檔案只會有一個使用者在寫。這在TFS的設計裡面對應著是一個block同時只能有一個寫或者更新操作。

TFS檔名的結構

TFS的檔名由塊號和檔案號通過某種對應關係組成,最大長度為18位元組。檔名固定以T開始,第二位元組為該叢集的編號(可以在配置項中指定,取值範圍 1~9)。餘下的位元組由Block ID和File ID通過一定的編碼方式得到。檔名由客戶端程式進行編碼和解碼,它對映方式如下圖:



TFS客戶程式在讀檔案的時候通過將檔名轉換為BlockID和FileID資訊,然後可以在!NameServer取得該塊所在!DataServer資訊(如果客戶端有該Block與!DataServere的快取,則直接從快取中取),然後與!DataServer進行讀取操作。

TFS效能資料

  1. 軟體環境描述

【測試機軟體情況描述】
(1) Red Hat Enterprise Linux AS release 4 (Nahant Update 8) 
(2) gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-11) 
(3) 部署了TFS客戶端程式 
【伺服器軟體情況描述】
(1) Red Hat Enterprise Linux Server release 5.4 (Tikanga) 
(2) gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-9) 
(3) 部署了2臺!DataServer程式。
【伺服器軟體情況描述】
(1) Red Hat Enterprise Linux Server release 5.4 (Tikanga) 
(2) gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46) 
(3) 部署了2臺!NameServer(HA)程式。

  1. 硬體環境描述

【測試機硬體情況描述】
(1) 一枚八核Intel(R) Xeon(R) CPU E5520 @ 2.27GHz 
(2) 記憶體總數8299424 kB 
【伺服器硬體情況描述】cpu/memory等
(1) 一枚八核Intel(R) Xeon(R) CPU E5520 @ 2.27GHz 
(2) 記憶體總數8165616 kB

  1. 隨機讀取1K~50K大小的檔案效能




Read的TPS隨著執行緒數的增加而增加,增長逐漸趨緩,到90執行緒的時候達到第一個高峰,此時再增加讀執行緒,則TPS不再穩定增長。

  1. 隨機寫入1K~50K大小的檔案




Write的TPS線上程數60左右達到高峰,此時再增加寫入執行緒,TPS不再穩定增長。

  1. 在不同執行緒寫壓力下的讀檔案效能







可以看出隨著寫壓力的增加,讀檔案的TPS會大幅下滑。當寫壓力達到一定程度時讀檔案TPS趨緩。

同時,對平均大小為20K的檔案進行了測試,測試中讀:寫:更新:刪除操作的比率為100:18:1:1時,在!DataServer伺服器磁碟util訪問達到80%以上時,響應時間如下: 

TYPE SUCCCOUNT FAILCOUNT AVG(us) MIN(us) MAX(us)
read 100000 0 20886 925 1170418
write 18000 0 17192 2495 1660686
update 1000 0 48489 5755 1205119
delete 1000 0 14221 382 591651

TYPE:操作型別
SUCCCOUNT:成功個數
FAILCOUNT:失敗個數
AVG:平均響應時間
MIN:最短響應時間
MAX: 最大響應時間