ceph bluestore源码分析:非对齐写逻辑
文章目錄
- 環(huán)境
- 原理說明
- 總結(jié)
環(huán)境
ceph:12.2.1
場景:ec 2+1
部署cephfs,執(zhí)行如右寫模式:dd if=/dev/zero of=/xxx/cephfs bs=6K count=4 oflag=direct
關(guān)鍵配置:
bluestore_min_alloc_size_hdd = 65536 bluestore分配空間的最小粒度 單位:B
bdev_block_size = 4096 磁盤分配空間的最小粒度 單位:B
原理說明
以上dd寫方式總共直寫四次6K大小的io,每一個io在經(jīng)過PG落到bluestore層次會根據(jù)分片大小2副本+1校驗塊 被拆分為3K大小的 io到每個osd上
-
第一次寫3K io 由于未chuck(bdev_size 4K )對齊,則對3K io補(bǔ)零后和bdev_size對齊,然后寫入bluestore按照bluestore_min_alloc_size_hdd大小分配的blob中
bluestore.cc :_txc_add_transaction–> _write --> _do_write–>_do_write_data --> _do_write_small
直接寫入一個新的blob中
_do_alloc_write txc 0x7fcb6e6443c0 1 blobs
-
第二次寫3K io主要執(zhí)行如下步驟:
a. 會先進(jìn)行clone blob1
b. 使用前1K 數(shù)據(jù)補(bǔ)齊blob1中的lextent1,再重新分配一個blob,寫入補(bǔ)齊后的lextent3
c. 剩下的2K io用0補(bǔ)齊后寫入新的lextent4
d. 移除lextent1對臨時onode的引用,remove臨時onde,進(jìn)而釋放lextent1對blob1的引用,進(jìn)而釋放blob1回收磁盤空間a. 執(zhí)行clone
這里clone是為當(dāng)前extent創(chuàng)建一個單獨的onode,即一個新的對象 空間來存儲,所以這里可以看到offset從0開始分配
這里在執(zhí)行clone的時候根據(jù)bluestore配置參數(shù)bluestore_clone_cow來判斷是否執(zhí)行cow機(jī)制:即當(dāng)前io補(bǔ)齊上一個extent之后,為其重新分配一個blob并寫入。RMW:需要有額外的寫,即先讀出當(dāng)前blob的extent數(shù)據(jù),使用第二次io的前1K補(bǔ)齊改blob,再寫入當(dāng)前blob空間。bluestore為了提升性能,減少小寫次數(shù),這里默認(rèn)使用了cow機(jī)制。
bluestore.cc :_txc_add_transaction–> _clone --> _do_clone
b. 此時blob1即被兩個邏輯段lexten共享,則blob1成為sharedblob
接下來使用1K數(shù)據(jù)補(bǔ)齊lextent1的3K,同時使用0補(bǔ)齊當(dāng)前剩余2K,補(bǔ)齊后的數(shù)據(jù)如下
c. 將補(bǔ)齊后的第一個4K IO寫入到lextent3中,第二次io剩余的數(shù)據(jù)寫入到lextent4中
可以看到這里進(jìn)行了兩次寫,第一次是寫clone之后補(bǔ)齊的數(shù)據(jù),第二次寫剩余用0補(bǔ)齊的數(shù)據(jù)。因為第一次寫是重新分配了blob,所以offset 為0,第二次寫是緊接著第一次的寫,所以這里offset為4096
補(bǔ)齊后的第一次寫 ,執(zhí)行小寫,分配了一個新的blob
bluestore.cc :_txc_add_transaction–> _write --> _do_write–>_do_write_data --> _do_write_small
第二次寫為直接寫入blob中未使用的塊
bluestore.cc :_txc_add_transaction–> _write --> _do_write–>_do_write_data --> _do_write_small
寫完之后的blob和extent引用關(guān)系如下
d. 此時釋放lextent1對blob1的引用如下:
垃圾回收,進(jìn)而釋放lextent1
bluestore.cc :_txc_add_transaction–> _write --> _do_write
_txc_add_transaction–> _write --> _do_write --> _wctx_finish
釋放blob1中l(wèi)exten1對blob1的引用
同時remove臨時onode(clone后的extent2所在的內(nèi)存元數(shù)據(jù)結(jié)構(gòu)),并釋放blob1的空間.最終第二個3k io寫完效果如下
_txc_add_transaction–> _remove --> _do_remove -->
回收磁盤空間 _txc_add_transaction–> _remove --> _do_remove --> _do_truncate --> _wctx_finish
_txc_add_transaction–> _remove --> _do_remove --> _do_truncate --> _wctx_finish
第三次寫 3K io同樣會進(jìn)行一次clone,不過clone完成之后的補(bǔ)齊上一次io的寫lextent4和新的補(bǔ)零寫lextent5之后blob2成為sharedblob,但是由于shareblob仍然再被lextent3引用,所以此時無法remove blob2
第四次寫3k io,同上最后的映射關(guān)系如下
源碼調(diào)用棧如下,本調(diào)用棧只打到了_do_write_small這一層,具體如何向源碼加入函數(shù)調(diào)用棧可以參考ceph bluestore源碼梳理:C++ 獲取線程id:
ceph-osd(BlueStore::_do_write_small(BlueStore::TransContext*,
ceph-osd(BlueStore::_do_write(BlueStore::TransContext*,
ceph-osd(BlueStore::_write(BlueStore::TransContext*,
ceph-osd(BlueStore::_txc_add_transaction(BlueStore::TransContext*,
ceph-osd(BlueStore::queue_transactions(ObjectStore::Sequencer*,
ceph-osd(PrimaryLogPG::queue_transactions(std::vector<ObjectStore::Transaction,
ceph-osd(ECBackend::handle_sub_write(pg_shard_t, boost::intrusive_ptr<OpRequest>,
ceph-osd(ECBackend::_handle_message(boost::intrusive_ptr<OpRequest>)+0x327)
ceph-osd(PGBackend::handle_message(boost::intrusive_ptr<OpRequest>)+0x50)
ceph-osd(PrimaryLogPG::do_request(boost::intrusive_ptr<OpRequest>&,
ceph-osd(OSD::dequeue_op(boost::intrusive_ptr<PG>, boost::intrusive_ptr<OpRequest>,
ceph-osd(PGQueueable::RunVis::operator()(boost::intrusive_ptr<OpRequest> const&)+0x57)
ceph-osd(OSD::ShardedOpWQ::_process(unsigned int, ceph::heartbeat_handle_d*)
ceph-osd(ShardedThreadPool::shardedthreadpool_worker(unsigned int)+0x839)
總結(jié)
綜上,我們可以看到bluestore在非對齊寫4個io時會進(jìn)行3次clone,總共執(zhí)行6次do_write_small方式的小寫。這里是因為clone時使用了cow機(jī)制,然而我們使用RMW時會額外增加一次寫。可以參考_clone函數(shù)中的邏輯。所以按照如上測試方式,每經(jīng)過4次io,bluestore會進(jìn)行9次的小寫。這里bluestore為了性能穩(wěn)定,犧牲磁盤空間來將cow機(jī)制作為默認(rèn)的非對齊寫處理方式。
總結(jié)
以上是生活随笔為你收集整理的ceph bluestore源码分析:非对齐写逻辑的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ceph bluestore源码分析:a
- 下一篇: 关于ceph源码 backtrace