Facebook圖片存儲(chǔ)架構(gòu)技術(shù)全解析
原創(chuàng)【51CTO獨(dú)家特稿】照片應(yīng)用程序是Facebook最流行的功能。直至目前為止,F(xiàn)acebook的用戶已經(jīng)上傳了超過(guò)150萬(wàn)幅照片,這使得Facebook成為最大的照片共享網(wǎng)站。對(duì)于每一個(gè)上傳的照片, Facebook生成并保存成4種不同大小的圖像,即總共有60億的圖片占1.5PB的存儲(chǔ)容量。目前的增長(zhǎng)速度是每星期220萬(wàn)個(gè)新照片,即每周消耗25TB的額外存儲(chǔ)空間。在高峰期,平均每秒會(huì)上傳550,000幅圖像。這些數(shù)字給Facebook的照片存儲(chǔ)基礎(chǔ)架構(gòu)帶來(lái)了嚴(yán)重的挑戰(zhàn)。
NFS照片基礎(chǔ)架構(gòu)
舊的照片基礎(chǔ)架構(gòu)包含幾個(gè)層次:
◆上傳層接收用戶上傳的照片,測(cè)量原始圖像的大小并將其保存到NFS存儲(chǔ)層。
◆照片服務(wù)層接收HTTP照片請(qǐng)求,并向用戶提供保存于NFS存儲(chǔ)層的照片。
◆NFS存儲(chǔ)層建立于商業(yè)存儲(chǔ)設(shè)備之上。
由于每個(gè)圖像存儲(chǔ)在自己的文件內(nèi),所以根據(jù)命名空間目錄和文件inode(內(nèi)節(jié)點(diǎn)),在存儲(chǔ)層產(chǎn)生了大量的元數(shù)據(jù)。這些元數(shù)據(jù)量遠(yuǎn)遠(yuǎn)超過(guò)了NFS存儲(chǔ)層的緩存能力,導(dǎo)致了上傳和讀取每張照片時(shí)成倍的I/O操作。整個(gè)照片服務(wù)的基礎(chǔ)架構(gòu)由于NFS存儲(chǔ)層的大量元數(shù)據(jù)負(fù)荷而成為了一個(gè)瓶頸,這就是Facebook嚴(yán)重依賴CDNs來(lái)提供照片服務(wù)的原因之一。以下兩個(gè)附加的優(yōu)化部署,用來(lái)在一定程度上減輕這個(gè)問(wèn)題:
Cachr :一個(gè)緩存服務(wù)層,用來(lái)緩存Facebook中較小的“個(gè)人資料”圖像。
NFS文件句柄緩存——部署在照片服務(wù)層,消除了一些NFS存儲(chǔ)級(jí)元數(shù)據(jù)負(fù)荷
Haystack照片基礎(chǔ)架構(gòu)
新的照片基礎(chǔ)架構(gòu)將照片服務(wù)層和存儲(chǔ)層合并為一個(gè)物理層。它實(shí)現(xiàn)了一個(gè)基于HTTP的照片服務(wù)器,把照片存儲(chǔ)在名為Haystack的通用對(duì)象中。對(duì)于新層次的主要要求是消除任何照片讀取操作的不必要的元數(shù)據(jù)開(kāi)銷,使每個(gè)讀取I/O操作只是讀取實(shí)際照片數(shù)據(jù)(而不是文件系統(tǒng)元數(shù)據(jù))。Haystack可劃分為以下一些功能層-
◆HTTP服務(wù)器
◆照片存儲(chǔ)
◆Haystack對(duì)象存儲(chǔ)
◆文件系統(tǒng)
◆存儲(chǔ)設(shè)備
以下各節(jié)中,我們會(huì)自底向上密切關(guān)注每一個(gè)功能層。
存儲(chǔ)設(shè)備
Haystack部署于日常存儲(chǔ)片之上。一個(gè)2U存儲(chǔ)片的典型硬件配置的是-
◆2 x 4核CPUs
◆16GB – 32GB內(nèi)存
◆具有256MB – 512MB NVRAM緩存的硬件RAID控制器
◆12+ 1TB SATA驅(qū)動(dòng)器
每個(gè)存儲(chǔ)片提供大約10TB的可用空間,配置為一個(gè)RAID-6分區(qū),由硬件RAID控制器進(jìn)行管理。RAID 6提供了足夠的冗余性和出色的讀取性能,可以降低存儲(chǔ)成本。RAID控制器NVRAM回寫高速緩存可以部分緩解低劣的寫性能。由于讀取大多是隨機(jī)的,所以NVRAM緩存完全保留給寫操作。磁盤高速緩存被禁用,以保證在系統(tǒng)崩潰或電源斷電時(shí)數(shù)據(jù)的一致性。#p#
文件系統(tǒng)
Haystack對(duì)象存儲(chǔ)實(shí)現(xiàn)于一個(gè)文件之上,該文件存儲(chǔ)在一個(gè)單一文件系統(tǒng)上,該文件系統(tǒng)建立于10TB volume(卷)大小的空間之上。
照片讀取請(qǐng)求導(dǎo)致read()系統(tǒng)調(diào)用請(qǐng)求讀取文件中不同偏移量的信息,但為了執(zhí)行讀取操作,文件系統(tǒng)必須首先在實(shí)際物理卷上找到數(shù)據(jù)。在文件系統(tǒng)中,每個(gè)文件的是由一個(gè)名為inode的結(jié)構(gòu)所描述,該結(jié)構(gòu)包含一個(gè)塊映射,可以把邏輯文件偏移量映射到物理卷中的物理塊偏離量。對(duì)于大文件,根據(jù)所使用的文件系統(tǒng)類型的不同,塊映射可能會(huì)相當(dāng)龐大。
基于塊的文件系統(tǒng)為每個(gè)邏輯塊維護(hù)其映射信息,對(duì)于大文件,這些映射信息將不會(huì)像通常那樣存入緩存的inode,而是儲(chǔ)存在間接地址塊,讀取文件數(shù)據(jù)時(shí)需要進(jìn)行轉(zhuǎn)換。間接轉(zhuǎn)化可能存在好幾個(gè)層次,因此,根據(jù)間接地址塊是否被緩存,單一的讀取可能會(huì)導(dǎo)致若干個(gè)I/O操作。
基于范圍的文件系統(tǒng)只為連續(xù)的塊(區(qū)域)維護(hù)映射信息。對(duì)于一個(gè)連續(xù)大文件的塊映射只由一個(gè)區(qū)域組成,此區(qū)域的大小正好可以裝入inode之中。但是,如果該文件是嚴(yán)重地分散和不連續(xù)的,其區(qū)塊在卷中不連續(xù),那么其塊映射可以隨之增長(zhǎng)。有了基于范圍的文件系統(tǒng),就可以通過(guò)積極分配一大塊空間來(lái)減少碎片。
目前,所選擇的文件系統(tǒng)是的XFS,基于范圍的文件系統(tǒng)提供有效文件預(yù)分配。
Haystack對(duì)象存儲(chǔ)
Haystack是一個(gè)簡(jiǎn)單日志結(jié)構(gòu)(只追加)的對(duì)象存儲(chǔ),包含描述存儲(chǔ)對(duì)象的指針。一個(gè)Haystack包括兩個(gè)文件——實(shí)際的包含指針的Haystack存儲(chǔ)文件,以及一個(gè)索引文件。下圖顯示了Haystack存儲(chǔ)文件的結(jié)構(gòu)布局:
第一個(gè)8KB的Haystack存儲(chǔ)由超級(jí)塊所占用。緊接著超級(jí)塊的是指針,每個(gè)指針由頁(yè)眉、數(shù)據(jù)、和頁(yè)腳組成。
一個(gè)指針是由其﹤Offset(偏移量), Key, Alternate Key(替換鍵),Cookie﹥?cè)M唯一確定,其中偏移量是指在Haystack存儲(chǔ)中的指針偏移量。Haystack對(duì)于關(guān)鍵字的值沒(méi)有任何限制,有的指針可以有多個(gè)關(guān)鍵字。下圖顯示的是索引文件的結(jié)構(gòu)布局—
在Haystack存儲(chǔ)文件中,每個(gè)指針有一個(gè)相應(yīng)的索引紀(jì)錄,而且指針?biāo)饕o(jì)錄的順序必須與Haystack存儲(chǔ)文件中相關(guān)的指針順序相匹配。索引文件提供查找Haystack存儲(chǔ)文件中某一特定指針?biāo)璧淖钚≡獢?shù)據(jù)。為了快速查找,把索引記錄載入并組織到一個(gè)數(shù)據(jù)結(jié)構(gòu)中,這是Haystack應(yīng)用程序(在我們的情況下是照片存儲(chǔ))的職責(zé)。索引文件不是至關(guān)重要的,因?yàn)樗梢愿鶕?jù)所需從Haystack存儲(chǔ)文件中重建。索引的主要目的是可以快速加載指針元數(shù)據(jù)到內(nèi)存中,而無(wú)須遍歷龐大的Haystack存儲(chǔ)文件,這是因?yàn)樗饕拇笮⊥ǔ_€不到存儲(chǔ)文件的1%。
Haystack寫操作
Haystack寫操作同步添加新的指針到Haystack存儲(chǔ)文件中。當(dāng)指針成功添加到龐大的Haystack存儲(chǔ)文件中之后,相應(yīng)的索引記錄也被寫入索引文件。由于索引文件不是至關(guān)重要的,為了達(dá)到更快的性能,該索引記錄是異步寫。
索引文件還會(huì)定期被刷新到下面的存儲(chǔ)設(shè)備,以便限制由硬件故障所引起的恢復(fù)操作的程度。在系統(tǒng)崩潰或突然斷電的情況下,Haystack恢復(fù)程序丟棄所有存儲(chǔ)中的不完整的指針,同時(shí)截?cái)郒aystack存儲(chǔ)文件直到最后一個(gè)有效的指針,然后,在Haystack存儲(chǔ)文件最后為所有跟蹤的孤立指針寫入丟失的索引記錄。
Haystack不允許覆蓋已存在的指針偏移量,因此,如果某個(gè)指針的數(shù)據(jù)需要修改,其修改后的新版本必須使用相同的﹤Key, Alternate Key, Cookie﹥?cè)M。然后應(yīng)用程序就可以認(rèn)為,在那些有著多個(gè)關(guān)鍵字的指針中,具有最大偏移量的指針就是最新添加的指針。
Haystack讀操作
傳遞給Haystack讀操作的參數(shù)包括指針偏移量、關(guān)鍵字、替換鍵、Cookie和數(shù)據(jù)大小。然后Haystack添加頁(yè)眉和頁(yè)腳的大小到數(shù)據(jù)大小中,并從文件中讀取整個(gè)指針。只有當(dāng)關(guān)鍵字、替換鍵和Cookie符合參數(shù)類型,所傳遞的數(shù)據(jù)通過(guò)校驗(yàn),并且指針沒(méi)有被之前的操作刪除時(shí),讀操作才能成功(見(jiàn)下文)。
Haystack刪除操作
刪除操作很簡(jiǎn)單——通過(guò)設(shè)置指針的標(biāo)記域中的一個(gè)“deleted(已刪除)”標(biāo)記位,標(biāo)記Haystack存儲(chǔ)中的指針為已刪除。然而,相關(guān)的索引記錄并不進(jìn)行任何方式的修改,因此一個(gè)應(yīng)用程序可能會(huì)結(jié)束于引用某個(gè)已刪除的指針。對(duì)于這樣的指針的讀操作會(huì)注意到“deleted”標(biāo)記,然后終止操作,提示操作錯(cuò)誤,給出錯(cuò)誤信息。已刪除的指針的空間不會(huì)以任何方式回收?;厥找褎h除指針的空間的唯一方法是壓縮c(見(jiàn)下文) 。
照片存儲(chǔ)服務(wù)器
照片存儲(chǔ)服務(wù)器負(fù)責(zé)接收HTTP請(qǐng)求,并轉(zhuǎn)化成相應(yīng)的Haystack存儲(chǔ)操作。為了盡量減少讀取照片所需的I/ O操作次數(shù),服務(wù)器在內(nèi)存中保存一個(gè)Haystack存儲(chǔ)文件中所有照片偏移量的索引。啟動(dòng)時(shí),服務(wù)器讀取Haystack索引文件并生成一個(gè)內(nèi)存中的索引。由于每個(gè)節(jié)點(diǎn)數(shù)以億計(jì)的照片(并且該數(shù)字只會(huì)隨著更大容量的驅(qū)動(dòng)器而增加),我們必須確保該索引能夠裝入可用的內(nèi)存中。這是通過(guò)在內(nèi)存中保留最少數(shù)量的元數(shù)據(jù)來(lái)實(shí)現(xiàn),只保留查找照片所需的信息。
當(dāng)用戶上傳一個(gè)照片,該照片就被分配一個(gè)唯一的64位編號(hào)。然后將照片轉(zhuǎn)化為4個(gè)不同大小的圖片。每個(gè)圖片具有相同的隨機(jī)Cookie和64位關(guān)鍵字,合理的圖像大?。ù螅?,小,縮略圖)是儲(chǔ)存在替換鍵中。然后上傳服務(wù)器調(diào)用照片存儲(chǔ)服務(wù)器,把所有4個(gè)圖像存儲(chǔ)在Haystack中。
內(nèi)存中的索引為每張照片保存以下信息:
Haystack使用開(kāi)源Google稀疏散列數(shù)據(jù)結(jié)構(gòu)來(lái)減小內(nèi)存中的索引,因?yàn)槭褂盟?,每條記錄只占2位。
照片存儲(chǔ)寫/修改操作
寫操作寫入照片到Haystack,并更新內(nèi)存索引。如果該索引中已經(jīng)包含了具有相同關(guān)鍵字的記錄,那么這就是一個(gè)修改現(xiàn)有照片的操作,那么只修改索引記錄偏移量,以反映新圖像在Haystack存儲(chǔ)文件中的位置。照片存儲(chǔ)總是假設(shè)存在重復(fù)的照片(具有相同關(guān)鍵字的照片),只有存儲(chǔ)在最大偏移量位置的照片是有效的。
照片存儲(chǔ)讀操作
傳遞到讀操作的參數(shù)包括Haystack id 和照片關(guān)鍵字、大小和COOKIE 。服務(wù)器根據(jù)照片關(guān)鍵字,執(zhí)行一個(gè)在內(nèi)存索引上的查找操作,然后得到含有所需照片的指針偏移量。如果發(fā)現(xiàn)調(diào)用的是Haystack讀操作來(lái)讀取照片,那么如上所述,Haystack刪除操作并不更新Haystack索引文件記錄。因此,一個(gè)新的內(nèi)存索引可能會(huì)包含之前刪除的照片的舊記錄。讀取之前刪除的照片將會(huì)導(dǎo)致操作失敗,并且內(nèi)存中的索引會(huì)自動(dòng)更新,設(shè)置已經(jīng)刪除圖像的偏移量為0。
照片存儲(chǔ)刪除操作
在調(diào)用Haystack刪除操作之后,內(nèi)存中的索引被更新,設(shè)置特定圖像的偏移量為0來(lái)表示該圖像已經(jīng)被刪除。
壓縮
壓縮是一個(gè)聯(lián)機(jī)操作,可以回收已被刪除的指針和重復(fù)指針(具有相同關(guān)鍵字的指針)所占用的空間。它通過(guò)復(fù)制指針創(chuàng)建一個(gè)新的Haystack,跳過(guò)所有重復(fù)和已刪除的指針。每次這樣做,就會(huì)交換文件和內(nèi)存中文件的結(jié)構(gòu)。
HTTP服務(wù)器
我們使用的HTTP框架是由開(kāi)源lib event圖書館所提供的簡(jiǎn)單的evhttp服務(wù)器。我們使用多線程,同一時(shí)間內(nèi),每個(gè)線程能夠處理一個(gè)HTTP請(qǐng)求。因?yàn)槲覀兊墓ぷ髁孔钪饕怯蒊/O操作產(chǎn)生,因此HTTP服務(wù)器的性能并不是至關(guān)重要的。
【編輯推薦】