目錄樹維護了整個文件系統的元信息,全部對文件系統中文件的增刪查改操做都首先須要通過目錄樹的操做才能進行。百度開源的分佈式文件系統BFS(開源地址:https://github.com/baidu/bfs)利用leveldb,實現了目錄樹的管理,使得目錄樹的實現很是簡潔,同時對目錄樹的操做十分高效。本文將爲你解析其具體設計及實現思路。git
目錄樹爲一個樹型結構,而leveldb是一個kv存儲引擎,所以,若是想用經過leveldb實現目錄樹,則須要把樹型結構映射成kv的扁平化結構。github
單獨就每一個文件或者目錄來說,其信息只須要一條kv記錄便可保存,而這條kv中的value須要保存該目錄或文件的屬性信息,可變更的空間不大。因此,從樹型結構到kv記錄的映射 ,關鍵在於key的選取。緩存
在BFS的目錄樹中,定義了一個int64_t
的整型數字做爲EntryID
,每一個文件或目錄擁有一個EntryID
,而且全局惟一,根目錄的EntryID
規定爲1。假設有以下目錄結構:微信
/home/dirx/ /filex diry/ /filey /tmp/ filez
按照建立順序,咱們依次給/home
分配的EntryID
爲2,/tmp
的EntryID
爲3,/home/dirx
的EntryId
爲4,/home/diry
的EntryID
爲5,/home/tmp/filez
的EntryID
爲6,/home/dirx/filex
的EntryID
爲7,/home/diry/filey
的EntryID
爲8。架構
注意到每一個目錄擁有一個本身的EntryID
的同時,又確定擁有一個父目錄,其父目錄又確定擁有一個EntryID
,能夠利用這一點,經過父目錄的EntryID
和子目錄中的文件名,來肯定一條記錄的key:對於每一個文件或者目錄,咱們規定:每條kv記錄的key爲 "父目錄的EntryID
+自身文件名",同時在value中存儲本身的EntryID
這樣編碼後,上述目錄樹即可以表示爲以下kv記錄:分佈式
1home -> 2 + xxx 1tmp -> 3 + xxx 2dirx -> 4 + xxx 2diry -> 5 + xxx 3filez -> 6 + xxx 4filex -> 7 + xxx 5filey -> 8 + xxx
其中,xxx
表示該目錄或文件的其它信息,如大小,建立時間,實際數據存放位置等。編碼
到此,目錄樹這個樹型結構,便已經平展成爲一條條的kv記錄,對目錄樹的操做,便轉化成了對某幾條kv記錄的操做:設計
/home/work/
目錄,則首先在/
目錄中查找home
目錄,因爲/
的EntryID
爲1,因此第一次查找時,key爲1home
,而後讀出其value,解析後發現/home
的EntryID
爲2,則將此EntryID
記下,繼續往下走,發現work
即爲所須要建立的文件,則爲其申請一個EntryID
(假設爲9),此時,寫入一條記錄,按照上面的規則,其key爲2work
,value爲work
建立的時間等信息,以及work
的EntryID
(9)/home/work
目錄刪除,只須要將key爲2work
的這條記錄刪除便可/home/dirx/filex
文件中的內容,則首先讀取1home
這條key所對應的value,解析發現value中記錄的EntryID
爲2,而後再去讀取2dirx
這條key所對應的value,解析發現value中記錄的EntryID
爲4,而後再去讀取4filex
這條key所對應的value,從裏面解析出/home/dirx/filex
的實際數據存放位置,進行文件內容的讀取EntryID
,所以,只需進行一次掃描便可。好比ls /
,則只需掃描leveldb中,以1\0x0
爲前綴的key便可,當遇到2時中止,所得結果即爲/
目錄下的全部內容/home/diry/filey
文件移動到home/dirx
目錄中,按照以前的規則,/home/diry/filey
在leveldb中存儲的key爲5filey
,/home/dirx
的EntryID
爲4,把5filey
這條記錄中的內存讀取出來,以4filey
爲key,再次存儲到leveldbk ,而後將5filey
這條記錄刪除,即完成了Rename操做這樣,一個目錄樹所須要的基本操做便已經支持,因爲leveldb引擎自己寫入速度較快,而且在讀取時,內部自己已經有cache來緩存住較熱的kv數據,而且緩存大小可配置,因此一個很是簡潔高效的目錄樹便實現了~code
想要了解BFS實現中的其它細節 ,請關注https://github.com/baidu/bfs,或者關注微信公衆號『百度網頁搜索基礎架構團隊』獲取更多技術信息內存