HDFS大目錄文件刪除方案的實踐思考

前言


前面幾篇文章筆者講述了2篇關於文件目錄刪除的相關文章,也提到了一些相對應的解決方案和思路。不過筆者本文想再談談對於這個問題的一些思考,主要關注在HDFS下大目錄的刪除性能影響方面。不敢說是談論的是HDFS大目錄刪除的最佳實踐方案,但是在某些點上,在實際環境中還是有一定的可應用性的。本文部分內容會引入筆者前段時間寫的兩篇相關文章:聊聊HDFS刪除Snapshot行爲導致的NameNode crash文件系統大目錄下的操作性能效率提升

HDFS的大目錄刪除行爲


曾經執行過HDFS大目錄rm行爲的同學,應該能切身體會到這個操作給集羣帶來的重大影響,尤其當這個大目錄擁有數量龐大的子文件數以及足夠深的深度目錄樹。

因爲這個刪除操作本身是要池NN FSN鎖的,所以這個過程執行的越漫長,對其它正常的RPC請求的處理影響就越大。

那麼爲什麼大目錄刪除會有這麼大的影響呢,總結下來有以下相關三點:

  • 單目錄下子entry過多(子目錄或子文件),造成delete動作的性能開銷。因HDFS目錄內用ArrayList做目錄孩子文件的信息保存的。當子文件過多的時候,就觸發了ArrayList remove操作的性能影響。這點筆者在文件系統大目錄下的操作性能效率提升中有相關詳細細節的討論。
  • 目錄深度過深且目錄下文件總數量規模龐大,需要進行逐一層級的遞歸目錄樹的遍歷。在這點上,在後面小節中我們再來討論這個問題。
  • HDFS快照加劇了目錄刪除的影響。這裏的前提是如果集羣啓用了HDFS Snapshot的情況下,因爲Snapshot會保留在Snapshot創建時間點後被刪除的那些文件目錄數據,達到數據保護的效果。那麼這麼被“延時”刪除的數據會在Snapshot被清理的那一刻, 進行一次性的刪除處理。這就好比是一個更大目錄刪除的疊加效果。

HDFS大目錄刪除實現方案思考


針對上節提到的三個難題,每一個說實話都不好解決,社區有一些解決方案和思路但是並沒有被合入release的分支裏面。

第一個問題,單一目錄子文件過多觸發ArrayList性能問題這點,社區HDFS-7174 JIRA有解決方案,原理是通過拆ArrayList的方式達到優化的效果,筆者也測試了這個JIRA的patch,效果還是相當的明顯的,詳見文章文件系統大目錄下的操作性能效率提升

第二個難點問題,目錄樹深且待刪除目錄總文件數龐大。

這裏其實有個變相的做法可以繞開這個問題,如果我們不想在HDFS內部刪除邏輯這塊做優化的情況下,通過改變外部的刪除調用時間會是十分簡單高效的做法。簡單來說,就是讓用戶不要一下子刪除那麼目錄文件走。取而代之的方式是分小時,分時段一批批的去刪除目標的文件目錄。因爲HDFS Trash機制在後面清理trash目錄的時候就會按照時間小時目錄依次去清理刪除目錄,每個小時目錄其實刪除的量就會小很多,對NN造成的影響也就會小。當然,此方案在HDFS Snapshot目錄的case下是起不到作用的。

至於這個難點的另外一個目錄樹深的問題,這裏可以brainstorm一下,比如用線程遞歸的方式去掃描目錄文件數據。這裏還不能是純線程異步的方式,因爲這裏會涉及到HDFS quota的更新問題,可能可行的方案是多線程啓動遞歸執行,然後在刪除目錄的call方法裏等待線程執行結束。不過社區在這塊操作上只是做了removeINode和removeBlock動作的拆分,僅到此爲止。removeBlock這步完全是可以分批進行的,所以社區在這裏是通過batch的方式分批進行刪除。通過改小這個刪除block的batch size是可以減輕一些刪除操作鎖帶來的影響的,相關JIRA:HDFS-13831: Make block increment deletion number configurable

第三個難點,HDFS Snapshot模式下怎麼減輕刪除目錄的影響。HDFS Snapshot會hold住實際已經刪除了的文件目錄數據。如果我們調整自己的刪除目錄操作時間,也是不起作用的。那麼這個時候有什麼好的辦法呢?答案很簡單,同樣更改Snapshot的創建時間點,我們將每次的一個大Snapshot變爲若干小的Snapshot。這樣的話,其實每個小Snapshot所刪除的文件目錄樹就會小很多。

通過建更多小Snapshot的方式,筆者在內部集羣上也進行測試過,優化效果只能說一般般。HDFS小Snapshot確實刪除的量會變小,但是其中有個getChilrenList的方式有比較大的時間cost。這個getChilrenList的調用主要是通過獲取Snapshot的diff來得到那個Snapshot時間點的文件列表數據,然後進行目錄的遞歸刪除。

但是getChilrenList在快照很多的情況下需要進行多次Snapshot diff的合併動作,然後再逆序apply到當前的文件目錄狀態,以此獲得Snapshot時間點的狀態。

以下面的例子爲例,

Diffs: s0­>s1­>s2­>s3­>s4­>s5­>s6­>s7­>s8­>s9­

上面總共10個Snapshot按照時間順序依次創建而來。假設我們此時準備刪除創建實際最早的s0開始,然後我們需要去獲取s0時刻Snapshot目錄的子文件目錄的列表情況,它需要將s0相對s1的diff,比如叫diff01,和後面依次diff12…diff89,和最後的diff9-latest狀態的這些diff進行合併。這個過程是會佔據一定時間的。

不過社區有解決方案解決上述getChilrenList的操作性能問題,通過SkipList維護多level的Snapshot diff list來達到加速ChilrenList獲取的速度。此方案的主要drawback是需要用額外的內存來保存這些diff數據。這裏需要我們在具體使用的時候做一定的衡量,此方案相關的JIRA: HDFS-11225: NameNode crashed because deleteSnapshot held FSNamesystem lock too long

SkipList的結構類似如下:

  • level 2: s08------------------------------->s9
  • level 1: S02------->s35-------->s68-------->s9
  • level 0: s0->s1->s2->s3->s4->s5->s6->s7->s8->s9

在上面s0-s9的Snapshot模式下,如果從中間Snapshot開始刪,會達到優化的效果嗎?實際來說的話,優化效果也比較一般,假設我們從s3刪除開始,所說它所需要做diff combine的次數少了,但是它的diff diff34還是需要和diff23進行一次merge作爲s2的diff的。其實我們可以看到它的combine diff的總開銷其實並沒有減少多少。

以上就是本文筆者關於HDFS大目錄刪除方案的一些想法思考方案,大家感興趣的可以參考實踐實踐。

引用


[1].https://issues.apache.org/jira/browse/HDFS-11225
[2].https://issues.apache.org/jira/browse/HDFS-13831
[3].https://blog.csdn.net/Androidlushangderen/article/details/105454638
[4].https://blog.csdn.net/Androidlushangderen/article/details/105256532

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章