更新:2016-10-19———————————————————————————————————————node
前面更新的內容可能略有偏頗,緣由是忘記了radow gateway這個東西,這對於對象存儲很關鍵。通常的對象存儲系統都不實現文件系統提供open、close、read、write和lseek等接口,對象存儲有簡單的接口,對應rest API風格,這也是本身近期經歷一個rest API項目明白,rest API 類的接口只有簡單的 get、put、delete等操做。這些操做不容許你針對一個對象進行部分獲取,只能get整個object,修改後再上傳到對象存儲中。並且對象存儲的使用場景特色來看,都是用於好比發佈的微信狀態、微博等,網站圖片啊,一旦上傳就不會修改,想要修改只能先刪除,再從新發布。算法
包括以上的緣由等,使得對象存儲不須要使用目錄樹來管理對象(文件系統是須要簡歷目錄樹的),從必定的角度來看,目前開源swift、ceph都沒有使用目錄樹來管理對象,扁平的object管理模式,通常實在bucket或者pool中存放,ceph還涉及到pg層,因此通常對象存儲使用2~3層的模式管理object,這在文件系統可了不起,在文件系統中的一個文件夾下文件數量超過一個值,會讓整個文件夾性能降低的很是厲害,超多的文件inode會讓內存等撐爆,這也是hdfs等不支持大量的小文件緣由,而對象存儲ceph中,在一個pool中存儲再多的文件數量對性能也不會太大的影響。swift
有人測試過對象存儲和文件系統在同一個文件夾下存儲文件,文件個數達到上億後,文件系統完敗,地址忘記了。。。。緩存
ceph對象存儲網關會使用swift或s3 API來訪問對象,這 個網關會本身維護一個索引,這個索引包含了object的一些信息,當用戶使用ls命令時,網關會在本身維護的索引中進行查找,相比去查詢文件系統的目錄樹,提升了不少的性能。ceph網關將本身的索引維護在一個叫作.rgw.buckets.index的pool中(爲和普通的pool區分,這裏叫作index pool),該index pool中對於其餘正常的pool都會生成一個對象.dir.defaukt.xxxxx.x的object中,在該object的omap中會保存 正常pool中object的數據信息,這些信息保存在levelDB中,當ls時,直接從levelDB中獲取,根本不須要遍歷全部的object,一樣swift 會將object維護在container中,因此對象存儲在ls方面很快,可是若是在文件系統中,對於上億個文件的文件夾中使用ls,那你可有得等了。微信
更新:2016-10-17———————————————————————————————————————分佈式
以前本身 想不明白的問題,今天有了寫想法,從新來講說,這位大神問的問題:「你能說說ceph這種對象存儲,ls命令是怎麼實現的?對象存儲能夠保存大量的對象,怎麼才能快速的ls出全部對象的信息?」函數
今天,有人問了我這個問題 https://www.oschina.net/question/252560_2201216,在回答問題前 我仔細的想了一下,爲何ceph對象存儲,ls命令能夠快速列出全部對象的信息?平時咱們感受文件系統ls也很快啊,沒感受有什麼神奇的。因此沒關注過。可是今天在這裏重新說一下性能
爲何會有對象存儲? 一樣都是文件,存在文件系統裏不是同樣的麼?測試
這是由於當文件數目少時,他們差異不大,當存在大量的小文件時,好比淘寶的圖片數據。海量圖片文件。若是保存在文件系統中,你再去ls查看,會慢死。而對象存儲中保存,使用ls會很是快速。網站
ls命令自己是查看文件元數據(文件大小,更新時間,屬性等)的,在文件系統中,元數據和圖片自己的數據在混在一塊兒存儲的,若是想要獲取全部小文件的元數據,熟悉文件系統的朋友都知道,那但是費了勁兒,要處處找元數據所在的位置,遍歷吧,因此時間會很長,慢死。可是在對象存儲中不一樣,元數據和圖片數據分開保存,能夠直接讀取元數據,不用區分元數據和圖片數據,因此對象存儲很快。
今天明白了,仍是本身知道的太少,要多請教的。上面說的不恰當之處,請你們多加指點。
舊:—————————————————————————————————————————————
曾經被一個前輩問過:「你能說說ceph這種對象存儲,ls命令是怎麼實現的?對象存儲能夠保存大量的對象,怎麼才能快速的ls出全部對象的信息?」
這個問題我不知道該怎麼樣來回答。腦子有點空白,這個問題是否是能夠拆成以下兩個問題:
問題1. 分佈式對象存儲和分佈式文件系統存儲相比較,當保存大量的對象文件或者文件時,對象存儲的ls的速度要快,這是爲何?
問題2. 分佈式對象存儲的ls實現與分佈式文件系統的ls實現分別是什麼樣的?
回答:
對於問題1:
我沒有測試過,因此我也不知道誰更快。可是兩者的數據都是在內存中,對象存儲的這部分數據分散在不少的osd節點上,文件系統的這部分數據都保存在mds上。對於對象存儲時能夠將ls分明分紅多個任務發送到不一樣的osd上檢索,最後合併結果。對於文件存儲只能在mds單一節點上完成。這個是速度的關鍵麼?若是有人懂得這個問題,請回答下,很是感謝。
對於問題2:
ceph的對象存儲檢索方面,主要涉及到兩點一個是rbd ls實現,一個是ceph ls實現。
rbd ls 實現比較簡單,能夠直接讀取rbd_directory這個對象就能夠了,該pool下面的全部的rbd都在這個對象文件中保存。
rbd命令從 rbd.cc中的main函數開始,開始解析命令,使用函數get_cmd。
2505:開始解析命令參數。
2508:是不是ls 或者list命令。
2510:在這裏解析爲 OPT_LIST。完成後再回到main函數中
3349:解析參數opt_cmd。
3351:case 解釋參數OPT_LIST。
3353:若是是ls 或者list rbd的信息,這裏直接調用do_list()函數。
0280:執行do_list函數。
0284:調用rbd.list() 獲取全部的rbd名字,放在容器names中。
0199:執行rbd.list()函數。
0202:調用librbd::list()函數,繼續獲取rbd名字的列表。
0430:這裏開始執行librbd::list()函數。
0436:讀取指定的object,object的名字叫作RBD_DIRECTORY,這是個宏定義,解釋出來就是rbd_directory文件。返回的結果保存在bl的buffer中。接下來在對bl中的數據進行解析出rbd name就好,而後將name所有都放在names中。返回結果。
總結rbd ls命令其實沒有真正的去搜索全部的rbd名字,而是隻讀取了一個文件,就能夠解析出全部的rbd名字列表。這個是否是快速的緣由呢?
若是不使用rbd形式,而是直接做爲object存儲呢? 可使用rados客戶端或者網關實現對象文件的上傳動做,須要把對象文件上傳到指定的pool中。最終會根據對象文件的名字保存到一個pg中。由文件名字與pool的名字根據crush算法映射到一個pg中去,pg其實是一個文件夾,一個pg中的全部的object文件都保存在這個文件夾下。
因此 這裏跟蹤下「rados –p testpool ls「 命令,看看如何ls出指定pool下面的對象文件。
命令開始於rados.cc中的main函數。該main函數中最後調用了rados_tool_common()來處理命令,繼續看rados_tool_common中的處理。
1522:直接從這裏開始看,前面都是參數解析和其餘的操做。這裏命令ls準備列舉出全部的object對象。
1524:若是沒有指定pool的名字,是返回錯誤的。因此你想ls出哪一個pool中的object。
1548:獲取全部的object頭部,而後從頭部開始列出,輸出到outstream中。這裏是重點,後面繼續描述。
1550:循環全部的object對象,而後將object對象的信息所有都輸出到outstream中。
來看看在1524行的代碼 io_ctx.nobjects_begin();這個函數是如何返回全部的object鏈。
1533:申明一個用於保存全部object的鏈 的頭部listh。
1534:填充一些信息,用於獲取object。
1535:聲明一個object的迭代器。使用迭代器獲取object。
1536:獲取全部的object信息。而且保存。iter.get_next() -> impl->get_next()
0626:調用get_next函數,準備獲取鏈表。
0636:調用rados_nobjects_list_next()開始獲取。來到該函數中,查看下面的信息
3568:判斷是否是鏈表是空的,這裏空的表明以前沒有獲取過。
3569:這裏調用librados::IoCtxImpl::nlist()函數。設定最大值等參數。
0366:設置最大的鏈表值。
0367:設置nspace的值。
0369:調用objecter->list_nobjects()函數,準備設置獲取後的回調工做。
0371:加鎖,等待完成操做喚醒。
0372:判斷獲取是否完成。
0373:等待獲取完成,等待被喚醒。
0374:解鎖操做。
接下來再看list_nobjects()中的一些處理狀況。
3286:發送請求必須有一個objectoperation的op操做。
3287:設置對於pg的操做。主要的是爲op添加一個操做,CEPH_OSD_OP_PGNLS。
3289:清空list列表的緩存信息。
3290:設置應答回調信息。這裏是很是重要的一個設計。list_context是繼續其餘pg遍歷的處理回調,onfinish是全部pg完成遍歷的處理回調。後續會介紹下C_NList中的finsh函數的處理。
3291:設置object目標的定位信息。這裏不是爲了發送到某個object,而是發送到pg處理便可。
3293:開始設置設置讀取current_pg中的object列表。這時 pool_id肯定,pg_id肯定,能夠根據crush算法知曉current_pg所在osd-set的位置,而後選擇一個osd進行讀取,後續命令封裝的過程就再也不詳細解釋了,拿着CEPH_OSD_OP_PGNLS標記直接去osd的代碼中查看流程。
在replicatedPG.cc 中,函數do_pg_op()中會對CEPH_OSD_OP_PGNLS標記進行解釋。case CEPH_OSD_OP_PGNLS。
0859:繼續調用objects_list_partial進行處理。這個也是最重要的處理。
繼續跟蹤,來到代碼的內部int PGBackend::objects_list_partial()
來到這裏是否是就很是的明瞭呢?
0128:循環獲取object,因爲每次獲取object的數量有限,能夠分屢次完成。
0131:這裏能夠看的出,參數coll就是pg的目錄,這個就是在掃描pg這個目錄下的文件名字,而後看成是object,放在objects的結構中。這個掃描過程很簡單了,繼續向下的代碼FileStore::collection_list_partial()中能夠找到答案。因爲目錄內部的文件都已經創建了index,因此掃描起來也比較快。
還有一點須要說明的是,這只是獲取其中一個pg的object信息,而後還須要處理其餘的pg中object信息,對全部的object信息進行整合,返回給用戶。何時開始處理的其餘pg的object掃描呢?在讀取pg的object時,設定了一個onack 回調函數,該函數具體由 list_nobjects()中申請的C_NList代替。這個C_NList的聲明時使用了C_NList *onack = new C_NList(list_context, onfinish, this); C_NList繼承自context類,因此它具備回調的屬性。在C_NList中看如何回調的。
這個是C_NList設置的回調函數。這裏有兩個分支。參數r是本次處理結果是否正常。
1364:若是處理的結果正常,則須要繼續處理掃描其餘pg中的object信息。
1367:若是出現了錯誤等,則直接返回錯誤消息,再也不處理其餘pg的object信息。
Objecter::_nlist_reply 函數中會從新調用list_nobjects(list_context, final_finish),繼續掃描其餘的pg的object信息。直到完成後,會調用final_finish->complete(0);這個在代碼中很容易看到,既然全部的object都已經掃描完成後,還須要這個回調作什麼?
還記得在librados::IoCtxImpl::nlist()函數。
0373:這裏等待被喚醒,才能返回用戶的請求線程繼續處理。
因此須要在final_finish->complete(0);中實現對cond.wait 的喚醒操做。
這裏的final_finish就是0369這裏聲明的C_SafeCond 回調,該回調中會喚醒0373行的等待,0373行被喚醒後繼續執行,最後返回給使用命令ls的結果。
總結:
1.rbd ls 命令列舉某個pool中的rbd。會直接讀取pool中的rbd_directory對象文件,該文件中保存了該pool中的全部的rbd名字信息。
2.rados ls命令列舉某個pool中的object。先找到pool,而且讀出pool中pg的數量,而後再遍歷每一個pg,讀取每一個pg下面的object。合併結果就是全部的object。這個作法必須一個一個pg的去掃描,並不能作到並行掃描。
根據以上兩點能夠知曉,rbd ls的操做比較簡單直接讀取目標文件便可。rados ls的操做須要串行的掃描pool中的每一個pg中的object。
若是你問我rados ls這樣的命令是怎麼實現的,我能夠講述上面的實現的過程。
若是你問我對於文件系統的ls方式,我也能夠講述一下過程。
至於這兩種方式的比較優點,還須要高人指點下,但願你們不吝賜教。