做者簡介:陳闖,花名「戰士雷歐」,白山超級工程師。Linux內核、Nginx模塊、存儲架構資深開發人員,7年以上存儲架構、設計及開發經驗,前後就任於東軟、中科曙光、新浪、美團,擅長獨立進行Haystack、糾刪碼等各類項目研發,愛好不斷下降IO、挑戰冗餘度底線。白山滑板車選手專業十級,會漂移,正積極備戰方莊街道第6屆動感滑板車運動會,家庭夢想是爲愛妻贏得無硅油洗髮水。算法
當今互聯網,數據呈現爆炸式增加,社交網絡、移動通訊、網絡視頻、電子商務等各類應用每每能產生億級甚至十億、百億級的海量小文件。因爲在元數據管理、訪問性能、存儲效率等方面面臨巨大的挑戰,海量小文件問題成爲了業界公認的難題。服務器
業界的一些知名互聯網公司,也對海量小文件提出瞭解決方案,例如:著名的社交網站Facebook,存儲了超過600億張圖片,專門推出了Haystack系統,針對海量小圖片進行定製優化的存儲。網絡
白山雲存儲CWN-X針對小文件問題,也推出獨有的解決方案,咱們稱之爲Haystack_plus。該系統提供高性能數據讀寫、數據快速恢復、按期重組合並等功能。架構
Facebook的Haystack對小文件的解決辦法是合併小文件。將小文件數據依次追加到數據文件中,而且生成索引文件,經過索引來查找小文件在數據文件中的offset和size,對文件進行讀取。post
Haystack的數據文件部分:性能
Haystack的數據文件,將每一個小文件封裝成一個needle,包含文件的key、size、data等數據信息。全部小文件按寫入的前後順序追加到數據文件中。優化
Haystack的索引文件部分:網站
Haystack的索引文件保存每一個needle的key,以及該needle在數據文件中的offset、size等信息。程序啓動時會將索引加載到內存中,在內存中經過查找索引,來定位在數據文件中的偏移量和大小。spa
Facebook的Haystack特色是將文件的完整key都加載到內存中,進行文件定位。機器內存足夠大的狀況下,Facebook完整的8字節key能夠所有加載到內存中。設計
可是現實環境下有兩個主要問題:
存儲服務器內存不會太大,通常爲32G至64G;
小文件對應的key大小難控制,通常選擇文件內容的MD5或SHA1做爲該文件的key。
場景舉例:
一臺存儲服務器有12塊4T磁盤,內存爲32GB左右。
服務器上現需存儲大小約爲4K的頭像、縮略圖等文件,約爲10億個。
文件的key使用MD5,加上offset和size字段,平均一個小文件對應的索引信息佔用28字節。
在這種狀況下,索引佔用內存接近30GB,磁盤僅佔用4TB。內存消耗近100%,磁盤消耗只有8%。
因此索引優化是一個必需要解決的問題。
Haystack_plus的核心也由數據文件和索引文件組成。
與Facebook的Haystack相似,Haystack_plus將多個小文件寫入到一個數據文件中,每一個needle保存key、size、data等信息。
索引是咱們主要優化的方向:
索引文件只保存key的前四字節,而非完整的key;
索引文件中的offset和size字段,經過512字節對齊,節省1個字節;並根據整個Haystack_plus數據文件實際大小計算offset和size使用的字節數。
數據文件中的needle按照key的字母順序存放。
因爲索引文件的key,只保存前四字節,若是小文件key的前四字節相同,不順序存放,就沒法找到key的具體位置。可能出現以下狀況:
例如:用戶讀取的文件key是0x ab cd ef ac ee
,但因爲索引文件中的key只保存前四字節,只能匹配0x ab cd ef ac
這個前綴,此時沒法定位到具體要讀取的offset。
咱們能夠經過needle順序存放,來解決這個問題:
例如:用戶讀取文件的key是0x ab cd ef ac bb
,匹配到0x ab cd ef ac
這個前綴,此時offset指向0x ab cd ef ac aa
這個needle,第一次匹配未命中。
經過存放在needle header中的size,咱們能夠定位0x ab cd ef ac bb
位置,匹配到正確needle,並將數據讀取給用戶。
問題:咱們應用折半查找算法在內存查找key,時間複雜度爲O(log(n)),其中n爲needle數目。索引前綴相同時,須要在數據文件中繼續查找。此時訪問的文件不存在時,容易形成屢次IO查找。
解決方法:在內存中,將存在的文件映射到bloom filter中。此時只須要經過快速搜索,就能夠排除不存在的文件。
時間複雜度爲O(k),k爲一個元素須要的bit位數。當k爲9.6時,誤報率爲1%,若是k再增長4.8,誤報率將下降爲0.1%。
Haystack_plus與Facebook Haystack內存消耗的對比,場景舉例,文件(如:頭像、縮略圖等)大小4K,key爲MD5:
內存消耗對比 | Key | offset | size |
---|---|---|---|
Haystack | 全量key,16字節 | 8字節 | 4字節 |
Haystack_plus | 4字節 | 4字節 | 1字節 |
注:Haystack的needle爲追加寫入,所以offset和size大小固定。Haystack_plus的key使用其前4字節,offset根據Haystack_plus數據文件的地址空間計算字節數,並按512字節對齊;size根據實際文件的大小計算字節數,並按512對齊。
從上圖能夠看出在文件數量爲10億的狀況下,使用Facabook的Haystack消耗的內存超過26G,使用Haystack_plus僅消耗9G多內存,內存使用下降了2/3。
10億個4K小文件,消耗內存超過9G。Key佔用4字節,Offset佔用4字節,還須要再小一些。
根據文件key的前綴,進行分層,相同的前綴爲一層。
經過分層,只保存一份重複的前綴,節省key的字節數。
優化前的offset,偏移範圍爲整個Haystack_plus的數據文件的地址空間。
優化後,只需在數據文件中的層內進行偏移,根據最大的層地址空間能夠計算所需字節數。
從上圖能夠看出,進行分層後,內存消耗從優化前的9G多,下降到4G多,節省了一半的內存消耗。
每臺服務器上,咱們將全部文件分紅多個group,每一個group建立一個Haystack_plus。系統對全部的Haystack_plus進行統一管理。
讀、寫、刪除等操做,都會在系統中定位操做某個Haystack_plus,而後經過索引定位具體的needle,進行操做。
以前已經介紹過,全部needle順序存放,索引作前綴壓縮,並分層。
chunk文件:小文件的實際數據被拆分保存在固定數量的chunk數據文件中,默認爲12個數據塊;
needle list文件:保存每一個needle的信息(如文件名、offset等);
needle index和layer index文件:保存needle list在內存中的索引信息;
global version文件:保存版本信息,建立新version時自動將新版本信息追加到該文件中;
attribute文件:保存系統的屬性信息(如chunk的SHA1等);
original filenames:保存全部文件原始文件名。
A、Haystack_plus數據文件被拆分爲多個chunk組織,chunk1,chunk2,chunk3…… B、分紅多個chunk的好處: 1. 數據損壞時,不影響其它chunk的數據; 2. 數據恢復時,只需恢復損壞的chunk。 C、每一個chunk的SHA1值存放在attribute文件中。
因爲needle在數據文件中按key有序存放,爲不影響其順序,新上傳的文件沒法加入Haystack_plus,而是首先被保存到hash目錄下,再經過按期自動合併方式,將新文件加入到Haystack_plus中。
合併時將從needle_list文件中讀取全部needle信息,將刪除的needle剔除,並加入新上傳的文件,同時從新排序,生成chunk數據文件、索引文件等。
從新合併時將生成一個新版本Haystack_plus。版本名稱是全部用戶的文件名排序的SHA1值的前4字節。
每半個月系統自動進行一次hash目錄檢查,查看是否有新文件,並計算下全部文件名集合的SHA1,查看與當前版本號是否相同,不一樣時說明有新文件上傳,系統將從新合併生成新的數據文件。
同時,系統容許在hash目錄下超過指定的文件數時,再從新建立新版本,從而減小從新合併次數。
版本的控制記錄在global_version文件中,每次建立一個新版本,版本號和對應的crc32將追加到global_version文件(crc32用於查看版本號是否損壞)。
每次生成新版本時,自動通知程序從新載入索引文件、attribute文件等。
用戶的文件將保存成三副本存放,所以Haystack_plus也會存放在3臺不一樣的機器上。
恢復場景一:
當一個Haystack_plus的文件損壞時,會在副本機器上,查找是否有相同版本的Haystack_plus,若是版本相同,說明文件的內容都是一致,此時只需將要恢復的文件從副本機器下載下來,進行替換。
恢復場景二:
若是副本機器沒有相同版本的Haystack_plus,但存在更高版本,那此時能夠將該版本的整個Haystack_plus從副本機器上拷貝下來,進行替換。
恢復場景三:
若是前兩種狀況都不匹配,那就從另外兩臺副本機器上,將全部文件都讀到本地上的hash目錄下,並將未損壞的chunk中保存的文件也提取到hash目錄下,用全部文件從新生成新版本的Haystack_plus。
在使用Haystack_plus後一段時間,咱們發現小文件的總體性能有顯著提升,RPS提高一倍多,機器的IO使用率減小了將近一倍。同時,由於優化了最小存儲單元,碎片下降80%。
使用該系統咱們能夠爲用戶提供更快速地讀寫服務,而且節省了集羣的資源消耗。