ceph的数据存储之路(10) -----ceph对象存储的ls命令实现及思考
2019獨角獸企業重金招聘Python工程師標準>>>
更新:2016-10-19———————————————————————————————————————
前面更新的內容可能略有偏頗,原因是忘記了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中存儲再多的文件數量對性能也不會太大的影響。
有人測試過對象存儲和文件系統在同一個文件夾下存儲文件,文件個數達到上億后,文件系統完敗,地址忘記了。。。。
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方式,我也可以講述一下過程。
至于這兩種方式的比較優勢,還需要高人指點下,希望大家不吝賜教。
?
轉載于:https://my.oschina.net/u/2460844/blog/669769
總結
以上是生活随笔為你收集整理的ceph的数据存储之路(10) -----ceph对象存储的ls命令实现及思考的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis key 相关命令
- 下一篇: handler消息机制