儲存架構|Haystack太強了!存2600億圖片

陶然陶然發表於2022-02-24

   小檔案儲存

  小檔案儲存,老生常談的問題。先聊聊小檔案儲存重點關注的是什麼?

  以前我們提過,對於磁碟來說,小 io 吃 iops,大塊 io 吃吞吐。

  劃重點:小檔案的重點是 io 次數。

  為什麼每次提到海量小檔案的時候,總說傳統的檔案系統不合適呢?

  因為它的後設資料操作太惹人眼球了。假設有 1K 的資料,後設資料如果搞個 1K ,這個開銷就太大了,空間大一倍,效能下降一倍。所以,只要是針對小檔案的儲存最佳化,基本上都會在後設資料上下點功夫。

   Haystack 的背景

  Haystack 是 Facebook 為了解決他們圖片儲存而專門設計的一套儲存架構,2012 年發表論文《 Finding a needle in Haystack: Facebook’s photo storage 》。

  文中提到當時( 2012 年 )他們已經有 2600 億張圖片,超過 20 PB 的資料,使用者每週上傳 10 億張,大約 60 TB 的資料。

  從這個資料量來看,確實談得上海量的檔案。算出來的圖片平均大小 64K 左右吧,不大,就是以前普通圖片的大小。

  64K 不知道怎麼算的?

  用 60 TB 除一個 10 億就知道了。

   Haystack 的特點

  接下來聊聊 Haystack 的設計到底有什麼神奇特點呢?可以歸納下面四點:

  Write Once

  Read Often

  Nerver Modify

  Rarely Deleted

  大白話就是,只寫一次,從不更新,不定期會讀,極少刪除。這個 Haystack 特點是適配 Facebook 的圖片場景的。

  注意,是先有 facebook 的業務場景特點,然後才把 Haystack 設計成這樣的。因果關係不要搞反了哦。

   海量的檔案的挑戰在哪裡?

  每一次檔案儲存會涉及到後設資料和資料兩部分的操作。當數量是海量的時候,無論是對儲存容量和後設資料的量都會帶來巨大的影響。

  儲存容量這個自不用提,這是使用者的資料,它是你必須要儲存的,通常這裡考慮的是儲存效率,考慮用更少的介質、更高的可靠性,來儲存更多的資料,通常這裡的選型是副本和糾刪碼。

  後設資料就有意思了,因為這個是內部的設計導致的冗餘資料(為了索引使用者資料而產生的資料),後設資料的設計則會影響到使用者的體驗,特別是海量的場景。

  童鞋思考個問題:海量、小檔案 的前提下,為什麼後設資料會帶來挑戰?挑戰主要是哪些方面?

   1 儲存成本有挑戰

  劃重點:任何的評估不能脫離場景。

  舉個簡單的例子,假如每個檔案 1K ,每個檔案對應後設資料也 1K ,這開銷大不大?

  太大了嘛。一倍的浪費。在海量的背景下,使用者儲存 1P 的資料,就要儲存 1P 的後設資料,浪費在後設資料的成本無法容忍。

  那後設資料設計成 1K 的是錯誤的嗎?

  不一定。

  比如說,如果是每個檔案 1G,對應每個後設資料 1K 呢,這個開銷大不大?

  不大,因為 1K/1G 才是 0.00009% ,也就是說,使用者儲存 1P 的資料,後設資料消耗為 0.092 TB ,這成本幾乎可以忽略。

  所以,前提很重要,設計好壞並不是絕對的,都是相對而言的,任何架構都要適配自己的場景。

   2 儲存效能有挑戰

  接著上面的例子,每個檔案 1K ,每個檔案對應後設資料也 1K ,這效能開銷大不大?

  太大了嘛。效能是一倍的損耗。每個檔案 1K ,本該一次磁碟 IO 就能解決,但是另外還要加一次後設資料操作的磁碟 IO 。也就是說磁碟極限如果 1 萬的 iops ,使用者只能獲取到 5000 的 iops 效能。內部損耗一半。

  那如果是每個檔案 1G,對應每個後設資料 1K 呢,這個開銷大不大?

  不大嘛,假設每筆 io 是 4K 的定長大小。1G 的資料寫 262144 次。只是多加一次後設資料 IO ,無關緊要。

   3 Hasystack 的突圍方向

  劃重點:小檔案的場景,後設資料的成本消耗和效能消耗會顯得更突出。再加上海量的前提下,這個是必須要解決的挑戰。

  那 Haystack 應該怎麼做呢?兩個方面:

  重新設計後設資料結構,而不是使用檔案系統的結構,要精簡後設資料的大小;

  削減後設資料的 io 的次數,甚至從 io 路徑上徹底消除後設資料它;

  你如果理解了上面的栗子,對於這兩個最佳化方向的匯出應該也是水到渠成的。

   Haystack 的目標

  高吞吐,低延遲

  高可靠,具備故障容錯能力;

  架構簡單,底成本

   Haystack 的架構設計

   1 整體架構

  Haystack 的架構非常簡單,擷取論文中的圖片:

  圖中表明瞭三個核心元件:

  Haystack Directory

  Haystack Cache

  Haystack Store

  Store 就是一個單機的儲存引擎,上層告訴它寫哪,它就寫哪。管理的單位是一個個大塊檔案。Haystack 裡面叫做 Physical Volume ,其實就是一個個大檔案而已啦。

  劃重點:Haystack 也是基於檔案系統之上的。

  Physical Volume 有一個閾值,比如寫滿 100 GB就不寫了。可以把它理解成一個大日誌檔案,資料的寫入方式也是 log 日誌的方式,append 寫入。

  Directory 是最上層的一個抽象,上面提到 Store 管理的是 Physical Volume ,上報到 Directory 元件,Directory 把這些底層的 Physical Volume 按照副本關係組織起來形成 Logical Volume 。Logical Volume 就是提供給使用者寫入資料用的。

  舉個簡單的例子,如果是三副本的 Haystack 系統,那麼一個 Logical Volume 由 3 個 Physical Volume 組成副本映象。

  Cache 這個就不用說了,就是一個單純的快取元件。

   2 資料怎麼組織

  奇伢用幾個問題的形式來闡述資料的組織。

  問題一:Physical Volume 是什麼?

  其實就是大檔案,Haystack Store 是基於檔案系統之上的。Physical Volume 就實現形式來講就是檔案,可以是 ext4 的檔案,也可以是 xfs 的檔案。只不過這個檔案有名字( Physical Volume ID ),也是一個閾值,比如 100 GB 。

  問題二:Logical Volume 是什麼?

  抽象出來的結構。由多個 Physical Volume 組成。它的個數由副本數決定,比如一個 3 副本 Logical Volume 由 3 個 Physical Volume 組成。

  問題三:Physical Volume 內部又是有什麼構成呢?

  一個叫做 Needle 的東西。

  Needle 其實就是使用者資料加一些頭部,加一些尾部構成的一個整體結構。Physical Volume 就是由這一個個 Needle 組成的。

  問題四:Needle 的頭尾有啥用?

  主要幾個方面:

  用來構建後設資料索引用的,裡面有 key,size 等關鍵資料;

  用來校驗資料是否損壞,裡面有 magic,crc 等;

  用來標識資料是否刪除,裡面有 Flags 標記位;

  這些頭尾資料就是 Haystack 給每個使用者物件重新設計的後設資料了,相比檔案系統的後設資料,這個太精簡了。

  在記憶體中的記憶體表,甚至只需要一個 16 個位元組就夠了,8 位元組的 key ,4 位元組的 offset,4 位元組的 size 。這個比核心檔案系統動輒幾百位元組甚至幾 K 位元組要好太多了。

  問題四:後設資料現在多大了?

  後設資料分為磁碟後設資料(持久化了的)和記憶體後設資料。

  磁碟後設資料可以看上面的 Needle 結構體,具體實現在 32 位元組左右。記憶體後設資料可以控制在 16 個位元組。

   3 讀、寫、刪

  資料寫入的流程:

  Web 接入點先去 Haystack Directory 選一個 Logical Volume ;

  把資料發往 Haystack Store ,寫到對應的三個 Physical Volume 即可(注意,append 寫入哦);

  資料讀取的流程:

  Web 接入點先去 Haystack Directory 拿到指定物件的後設資料;

  然後請求發給 Haystack Store ,讀取資料(這裡就不提 Haystack Cache 或者 CDN 的邏輯了,過於簡單);

  資料刪除的流程:

  Web 接入點先去 Haystack Directory 拿到指定物件的後設資料;

  然後把刪除請求發給 Haystack Store ,就地更新 Needle 的標記位,標記成刪除;

  劃重點:Haystack 的刪除是就地更新,而不是 append 寫入。這裡跟純粹的 log 檔案不大一樣。但由於刪除是極少的,所以就算不是 append 寫入,也不影響大局。

   4 空間回收

  Haystack 也和 LSM,Bitcask 等設計類似,刪除是刪除,回收是回收,這是兩個步驟。

  空間回收就是 Compact ,太簡單了,論文甚至都沒稀的提它,寥寥數語說了兩句,原文描述如下:

  A Store machine compacts a volume file by copying needles into a new file while skipping any duplicate or deleted entries.

  實現很簡單,和以前提過的 Compact 並無二樣。邏輯就是遍歷 Volume 檔案,把重複的和標記刪除了的 Needle 跳過,有效的 Needle 讀出來寫到新的地方,即可。

   優秀的開源

  1 野心不小的 SeaweedFS

  它也是個類 Haystack 的專案,但不止於此,它還借鑑了 Facebook 很多的其他設計。對外提供了 FUSE、S3、Hadoop 等介面,甚至實現了 Kubernetes CSI Driver,內部實現了糾刪碼儲存,資料自動均衡等功能。

  專案地址:

  專案在持續更新,從這些方面來看,它的野心不小。想在超大體量物件儲存,大資料領域,雲原生等領域分一杯羹,值得學習。

   2 嗶哩嗶哩的 bfs

  B 站開源的 bfs ,純 Golang 專案,很不錯,盡得 Haystack 設計的精髓。並且對於 IO 過程有自己的思考最佳化,值得學習。

  專案地址:。

  不過 bfs 基本上在 Github 上算是封版了,幾年沒更新過。

   不一樣的思考

  回想一下這個架構,思考一下它做到了它立的 flag 嗎?

   1 它的目標:高吞吐,低延遲,怎麼實現的呢?

  對於寫請求,全都化為 append 請求,極力的保持磁碟的順序效能。並且得益於 Needle 的設計,Haystack 把資料和後設資料放在一起,一次性落盤,相當於省去了後設資料的 IO 寫開銷。

  當然,這種設計也必然有代價,由此帶來的代價就是載入時間變長。

  對於讀請求,透過後設資料的精簡,讓記憶體 hold 住所有的後設資料,去除了後設資料的 IO 開銷,這樣讀操作也就只剩使用者資料的 IO 。

  注意:Haystack 刪除不是 append 哦,而是覆蓋寫,但之前已經說過了,Haystack 的適用場景就是“極少刪除” 。

   2 高可靠,故障容錯怎麼實現的呢?

  這個很簡單,透過副本冗餘來做的。Volume 的組織邏輯放在 Directory 元件中,一份資料儲存多份,並且分散在不同的位置。當其中一份故障,則只需要複製其他副本即可。

   3 畢竟 2012 年的論文,Haystack 的實踐過時了嗎?

  論文中提到,Facebook 當時的實踐是用 2U 的刀鋒伺服器,48G 記憶體,搭配 12 * 1TB 的 SATA 盤。

  如果按照一個檔案 64 KB 算,一個 needle 記憶體後設資料 16 位元組(這個很極限了),只需要 3 G 的記憶體,單機 48 GB 的實體記憶體應對這整機的後設資料確實綽綽有餘。

  但現在很多伺服器已經升級到 64 盤,單盤 16 TB,滿載的話需要 256 G 的記憶體裝後設資料。這個記憶體配比就不大合適了,如果後設資料再稍微大點,就更不行了。

  但話說回來,並不是每個人都用 64 盤 16 T 的高密伺服器,所以並不能一概而論,還是要看自己的需求場景。

  就算過去 10 年,我覺得它還能秀。

   總結

  Haystack 最核心的最佳化是?重新設計後設資料的結構,使得記憶體後設資料只有十幾個位元組,極大的減輕了負擔,且設計的 Needle 結構可以完整恢復記憶體後設資料;

  得益於後設資料的精簡,Haystack 就能把單機全量後設資料放在記憶體;

  讀的時候,後設資料在記憶體,只有使用者資料的 IO 消耗,極大的提高了效能;

  寫的時候,得益於 Needle 的設計,後設資料更新操作不單獨刷,而是和使用者資料在一起刷,相當於省掉了後設資料 IO 的開銷;

  和 Bitcask 類似,為了提高記憶體載入速度,也有索引(Index 檔案)的實現;

  Haystack 並不過時,可以結合自己的場景,煥發生機;

來自 “ 奇伢雲端儲存 ”, 原文作者:奇伢;原文連結:https://mp.weixin.qq.com/s/jKvxuNQAUzRg4Cq4yTdN6w,如有侵權,請聯絡管理員刪除。

相關文章