淘寶圖片服務的學習

biaodianfu發表於2013-07-26

一、淘寶網的困境

對於淘寶網這樣的大型電子商務網站,對於圖片服務的要求特別的高。而且對於賣家來說,圖片遠勝於文字描述,因此賣家也格外看重圖片的顯示質量、訪問速度等問題。根據淘寶網的流量分析,整個淘寶網流量中,圖片的訪問流量會佔到90%以上,而主站的網頁則佔到不到10%。同時大量的圖片需要根據不同的應用位置,生成不同大小規格的縮圖。考慮到多種不同的應用場景以及改版的可能性,一張原圖有可能需要生成20多個不同尺寸規格的縮圖。

淘寶整體圖片儲存系統容量1800TB(1.8PB),已經佔用空間990TB(約1PB)。儲存的圖片檔案數量達到286億多個,這些圖片檔案包括根據原圖生成的縮圖。平均圖片大小是17.45K;8K以下圖片佔圖片數總量的61%,佔儲存容量的11%。對於如此大規模的小檔案儲存與讀取需要頻繁的尋道和換道,在大量高併發訪問量的情況下,非常容易造成讀取延遲。

2007年之前淘寶採用NetApp公司的檔案儲存系統。至2006年, NetApp公司最高階的產品也不能滿足淘寶儲存的要求。首先是商用的儲存系統沒有對小檔案儲存和讀取的環境進行有針對性的優化;其次,檔案數量大,網路儲存裝置無法支撐;另外,整個系統所連線的伺服器也越來越多,網路連線數已經到達了網路儲存裝置的極限。此外,商用儲存系統擴容成本高,10T的儲存容量需要幾百萬,而且存在單點故障,容災和安全性無法得到很好的保證。

二、淘寶網自主開發的目的

  1. 商用軟體很難滿足大規模系統的應用需求,無論儲存還是CDN還是負載均衡,因為在廠商實驗室端,很難實現如此大的資料規模測試。
  2. 研發過程中,將開源和自主開發相結合,會有更好的可控性,系統出問題了,完全可以從底層解決問題,系統擴充套件性也更高。
  3. 在一定規模效應基礎上,研發的投入都是值得的。當規模超過交叉點後自主研發才能收到較好的經濟效果。實際上淘寶網的規模已經遠遠超過了交叉點。
  4. 自主研發的系統可在軟體和硬體多個層次不斷的優化。

三、淘寶TFS的介紹

1、 TFS 1.0版本

從2006年開始,淘寶網決定自己開發一套針對海量小檔案儲存難題的檔案系統,用於解決自身圖片儲存的難題。到2007年6月,TFS(淘寶檔案系統,Taobao File System)正式上線運營。在生產環境中應用的叢集規模達到了200臺PC Server(146G*6 SAS 15K Raid5),檔案數量達到上億級別;系統部署儲存容量: 140 TB;實際使用儲存容量: 50 TB;單臺支援隨機IOPS 200+,流量3MBps。

圖為淘寶叢集檔案系統TFS 1.0第一版的邏輯架構:叢集由一對Name Server和多臺Data Server構成,Name Server的兩臺伺服器互為雙機,就是叢集檔案系統中管理節點的概念。

  • 每個Data Server執行在一臺普通的Linux主機上
  • 以block檔案的形式存放資料檔案(一般64M一個block)
  • block存多份保證資料安全
  • 利用ext3檔案系統存放資料檔案
  • 磁碟raid5做資料冗餘
  • 檔名內建後設資料資訊,使用者自己儲存TFS檔名與實際檔案的對照關係–使得後設資料量特別小。

TFS最大的特點就是將一部分後設資料隱藏到圖片的儲存檔名上,大大簡化了後設資料,消除了管理節點對整體系統效能的制約,這一理念和目前業界流行的“物件儲存”較為類似。傳統的叢集系統裡面後設資料只有1份,通常由管理節點來管理,因而很容易成為瓶頸。而對於淘寶網的使用者來說,圖片檔案究竟用什麼名字來儲存實際上使用者並不關心,因此TFS在設計規劃上考慮在圖片的儲存檔名上暗藏了一些後設資料資訊,例如圖片的大小、時間、訪問頻次等等資訊,包括所在的邏輯塊號。而在後設資料上,實際上儲存的資訊很少,因此後設資料結構非常簡單。僅僅只需要一個fileID,能夠準確定位檔案在什麼地方。由於大量的檔案資訊都隱藏在檔名中,整個系統完全拋棄了傳統的目錄樹結構,因為目錄樹開銷最大。拿掉後,整個叢集的高可擴充套件性極大提高。

2、 TFS 1.3版本

到2009年6月,TFS 1.3版本上線,叢集規模大大擴充套件,部署到淘寶的圖片生產系統上,整個系統已經從原有200臺PC伺服器擴增至440臺PC Server(300G*12 SAS 15K RPM) + 30臺PC Server (600G*12 SAS 15K RPM)。支援檔案數量也擴容至百億級別;系統部署儲存容量:1800TB(1.8PB);當前實際儲存容量:995TB;單臺Data Server支援隨機IOPS 900+,流量15MB+;目前Name Server執行的實體記憶體是217MB(伺服器使用千兆網路卡)。

圖為TFS1.3版本的邏輯結構圖,在TFS1.3版本中,淘寶網的軟體工作組重點改善了心跳和同步的效能,最新版本的心跳和同步在幾秒鐘之內就可完成切換,同時進行了一些新的優化:包括後設資料存記憶體上,清理磁碟空間,效能上也做了優化,包括:

  • 完全扁平化的資料組織結構,拋棄了傳統檔案系統的目錄結構。
  • 在塊裝置基礎上建立自有的檔案系統,減少EXT3等檔案系統資料碎片帶來的效能損耗
  • 單程式管理單塊磁碟的方式,摒除RAID5機制
  • 帶有HA機制的中央控制節點,在安全穩定和效能複雜度之間取得平衡。
  • 儘量縮減後設資料大小,將後設資料全部載入入記憶體,提升訪問速度。
  • 跨機架和IDC的負載均衡和冗餘安全策略。
  • 完全平滑擴容。

TFS主要的效能引數不是IO吞吐量,而是單臺PCServer提供隨機讀寫IOPS。由於硬體型號不同,很難給出一個參考值來說明效能。但基本上可以達到單塊磁碟隨機IOPS理論最大值的60%左右,整機的輸出隨盤數增加而線性增加。

3、 TFS 2.0版本

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

 一個TFS叢集由兩個!NameServer節點(一主一備)和多個!DataServer節點組成。這些服務程式都是作為一個使用者級的程式執行在普通Linux機器上的。在TFS中,將大量的小檔案(實際資料檔案)合併成為一個大檔案,這個大檔案稱為塊(Block), 每個Block擁有在叢集內唯一的編號(Block Id), Block Id在!NameServer在建立Block的時候分配, !NameServer維護block與!DataServer的關係。Block中的實際資料都儲存在!DataServer上。而一臺!DataServer伺服器一般會有多個獨立!DataServer程式存在,每個程式負責管理一個掛載點,這個掛載點一般是一個獨立磁碟上的檔案目錄,以降低單個磁碟損壞帶來的影響。正常情況下,一個塊會在!DataServer上存在,主!NameServer負責Block的建立,刪除,複製,均衡,整理, !NameServer不負責實際資料的讀寫,實際資料的讀寫由!DataServer完成。

  • !NameServer主要功能是: 管理維護Block和!DataServer相關資訊,包括!DataServer加入,退出, 心跳資訊, block和!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載入進來。

容錯機制

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

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

!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叢集檔案儲存系統,在TFS前端,還部署著200多臺圖片檔案伺服器,用Apatch實現,用於生成縮圖的運算。

根據淘寶網的縮圖生成規則,縮圖都是實時生成的。這樣做的好處有兩點:一是為了避免後端圖片伺服器上儲存的圖片數量過多,大大節約後臺儲存空間的需求,淘寶網計算,採用實時生成縮圖的模式比提前全部生成好縮圖的模式節約90%的儲存空間,也就是說,儲存空間只需要後一種模式的10%;二是,縮圖可根據需要實時生成出來,更為靈活。

淘寶網圖片儲存與處理系統全域性拓撲,圖片伺服器前端還有一級和二級快取伺服器,儘量讓圖片在快取中命中,最大程度的避免圖片熱點,實際上後端到達TFS的流量已經非常離散和平均。

圖片檔案伺服器的前端則是一級快取和二級快取,前面還有全域性負載均衡的設定,解決圖片的訪問熱點問題。圖片的訪問熱點一定存在,重要的是,讓圖片儘量在快取中命中。目前淘寶網在各個運營商的中心點設有二級快取,整體系統中心店設有一級快取,加上全域性負載均衡,傳遞到後端TFS的流量就已經非常均衡和分散了,對前端的響應效能也大大提高。

根據淘寶的快取策略,大部分圖片都儘量在快取中命中,如果快取中無法命中,則會在本地伺服器上查詢是否存有原圖,並根據原圖生成縮圖,如果都沒有命中,則會考慮去後臺TFS叢集檔案儲存系統上調取,因此,最終反饋到TFS叢集檔案儲存系統上的流量已經被大大優化了。

淘寶網將圖片處理與快取編寫成基於Nginx的模組(Nginx-tfs),淘寶認為Nginx是目前效能最高的HTTP伺服器(使用者空間),程式碼清晰,模組化非常好。淘寶使用GraphicsMagick進行圖片處理,採用了面向小物件的快取檔案系統,前端有LVS+Haproxy將原圖和其所有縮圖請求都排程到同一臺Image Server。

檔案定位上,記憶體用hash演算法做索引,最多一次讀盤。寫盤方式則採用Append方式寫,並採用了淘汰策略FIFO,主要考慮降低硬碟的寫操作,沒有必要進一步提高Cache命中率,因為Image Server和TFS在同一個資料中心,讀盤效率還是非常高的。

相關文章