溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

BlueStore事物狀態(tài)機是什么

發(fā)布時間:2021-12-18 15:37:19 來源:億速云 閱讀:156 作者:iii 欄目:云計算

本篇內(nèi)容主要講解“BlueStore事物狀態(tài)機是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“BlueStore事物狀態(tài)機是什么”吧!

前言

BlueStore可以理解為一個支持ACID的本地日志型文件系統(tǒng)。所有的讀寫都是以Transaction進行,又因為支持覆蓋寫,所以寫流程設(shè)計的相對復(fù)雜一些,涉及到一系列的狀態(tài)轉(zhuǎn)換。我們著重分析一下狀態(tài)機、延遲指標(biāo)以及如何保證IO的順序性和并發(fā)性。

狀態(tài)機

queue_transactions

queue_transactions是ObjectStore層的統(tǒng)一入口,KVStore、MemStore、FileStore、BlueStore都相應(yīng)的實現(xiàn)了這個接口。state_t state變量記錄了當(dāng)前時刻事物處于哪個狀態(tài)。在創(chuàng)建TransactionContext的時候會將state初始化為STATE_PREPARE,然后在_txc_add_transaction中會根據(jù)操作碼類型(opcode)進行不同的處理。同時會獲取PG對應(yīng)的OpSequencer(每個PG有一個OpSequencer)用來保證PG上的IO串行執(zhí)行,對于deferred-write會將其數(shù)據(jù)寫入RocksDB(WAL)。

以下階段就進入BlueStore狀態(tài)機了,我們以寫流程為導(dǎo)向分析狀態(tài)機的每個狀態(tài)。

STATE_PREPARE

從state_prepare開始已經(jīng)進入事物的狀態(tài)機了。這個階段會調(diào)用_txc_add_transaction將OSD層面的事物轉(zhuǎn)換為BlueStore層面的事物;然后檢查是否還有未提交的IO,如果還有就將state設(shè)置為STATE_AIO_WAIT并調(diào)用_txc_aio_submit提交IO,然后退出狀態(tài)機,之后aio完成的時候會調(diào)用回調(diào)函數(shù)txc_aio_finish再次進入狀態(tài)機;否則就進入STATE_AIO_WAIT狀態(tài)。
_txc_aio_submit函數(shù)調(diào)用棧:

bdev->aio_submit –> KernelDevice::aio_submit –> io_submit將aio提交到內(nèi)核Libaio隊列。

主要工作:準(zhǔn)備工作,生成大小寫、初始化TransContext、deferred_txn、分配磁盤空間等。

延遲指標(biāo)l_bluestore_state_prepare_lat,從進入狀態(tài)機到prepare階段完成,平均延遲大概0.2ms左右。

STATE_AIO_WAIT

該階段會調(diào)用_txc_finish_io進行SimpleWrite的IO保序等處理,然后將狀態(tài)設(shè)置為STATE_IO_DONE再調(diào)用_txc_state_proc進入下一個狀態(tài)的處理。

主要工作:對IO保序,等待AIO的完成。

延遲指標(biāo)l_bluestore_state_aio_wait_lat,從prepare階段完成開始到AIO完成,平均延遲受限于設(shè)備,SSD 0.03ms左右。

STATE_IO_DONE

完成AIO,并進入STATE_KV_QUEUED階段。會根據(jù)bluestore_sync_submit_transaction做不同處理。該值為布爾值,默認為false。

如果為true,設(shè)置狀態(tài)為STATE_KV_SUBMITTED并且同步提交kv到RocksDB但是沒有sync落盤(submit_transaction),然后applied_kv。

如果為false,則不用做上面的操作,但是以下操作都會做。

最后將事物放在kv_queue里,通過kv_cond通知kv_sync_thread去同步IO和元數(shù)據(jù)。

主要工作:將事物放入kv_queue,然后通知kv_sync_thread,osr的IO保序可能會block。

延遲指標(biāo)l_bluestore_state_io_done_lat,平均延遲在0.004ms,通常很小主要耗在對SimpleWrite的IO保序處理上。

STATE_KV_QUEUED

該階段主要在kv_sync_thread線程中同步IO和元數(shù)據(jù),并且將狀態(tài)設(shè)置為STATE_KV_SUBMITTED。具體會在異步線程章節(jié)分析kv_sync_thread線程。

主要工作:從kv_sync_thread隊列中取出事物。

延遲指標(biāo)l_bluestore_state_kv_queued_lat,從事物進入隊列到取出事物,平均延遲在0.08ms,因為是單線程順序處理的,所以依賴于kv_sync_thread處理事物的速度。

STATE_KV_SUBMITTED

等待kv_sync_thread中kv元數(shù)據(jù)和IO數(shù)據(jù)的Sync完成,然后將狀態(tài)設(shè)置為STATE_KV_DONE并且回調(diào)finisher線程。

主要工作:等待kv元數(shù)據(jù)和IO數(shù)據(jù)的Sync完成,回調(diào)finisher線程。

延遲指標(biāo)l_bluestore_state_kv_committing_lat,從隊列取出事物到完成kv同步,平均延遲1.0ms,有極大的優(yōu)化空間。

STATE_KV_DONE

如果是SimpleWrite,則直接將狀態(tài)設(shè)置為STATE_FINISHING;如果是DeferredWrite,則將狀態(tài)設(shè)置為STATE_DEFERRED_QUEUED并放入deferred_queue。

主要工作:如上。

延遲指標(biāo)l_bluestore_state_kv_done_lat,平均延遲0.0002ms,可以忽略不計。

STATE_DEFERRED_QUEUED

主要工作:將延遲IO放入deferred_queue等待提交。

延遲指標(biāo)l_bluestore_state_deferred_queued_lat,通常不小,沒有數(shù)據(jù)暫不貼出。

STATE_DEFERRED_CLEANUP

主要工作:清理延遲IO在RocksDB上的WAL。

延遲指標(biāo)l_bluestore_state_deferred_cleanup_lat,通常不小,沒有數(shù)據(jù)暫不貼出。

STATE_FINISHING

主要工作:設(shè)置狀態(tài)為STATE_DONE,如果還有DeferredIO也會提交。

延遲指標(biāo)l_bluestore_state_finishing_lat,平均延遲0.001ms。

STATE_DONE

主要工作:標(biāo)識整個IO完成。

延遲指標(biāo)l_bluestore_state_done_lat

延遲分析

BlueStore定義了狀態(tài)機的多個延遲指標(biāo),由PerfCounters采集,函數(shù)為BlueStore::_init_logger()。

可以使用ceph daemon osd.0 perf dump或者ceph daemonperf osd.0來查看對應(yīng)的延遲情況。

除了每個狀態(tài)的延遲,我們通常也會關(guān)注以下兩個延遲指標(biāo):

b.add_time_avg(l_bluestore_kv_lat, "kv_lat",
 "Average kv_thread sync latency", "k_l",
 
b.add_time_avg(l_bluestore_commit_lat, "commit_lat",
 "Average commit latency", "c_l",

BlueStore延遲主要花費在l_bluestore_state_kv_committing_lat也即c_l,大概1ms左右。

BlueStore統(tǒng)計狀態(tài)機每個階段延遲的方法如下:

// 該階段延遲 = 上階段完成到該階段結(jié)束
void log_state_latency(PerfCounters *logger, int state) {
 utime_t lat, now = ceph_clock_now();
 lat = now - last_stamp;
 logger->tinc(state, lat);
 last_stamp = now;
}

在塊存儲的使用場景中,除了用戶并發(fā)IO外,通常用戶也會使用dd等串行IO的命令,此時便受限于讀寫的絕對延遲,擴容加機器、增加線程數(shù)等橫向擴展的優(yōu)化便是無效的,所以我們需要關(guān)注兩方面的延遲:并發(fā)IO延遲串行IO延遲。

并發(fā)IO延遲優(yōu)化:kv_sync_thread、kv_finalize_thread多線程化;自定義WAL;async read。

串行IO延遲優(yōu)化:并行提交元數(shù)據(jù)、數(shù)據(jù);將sync操作與其他狀態(tài)并行處理。

IO保序

保證IO的順序性以及并發(fā)性是分布式存儲必然面臨的一個問題。因為BlueStore使用異步IO,后提交的IO可能比早提交的IO完成的早,所以更要保證IO的順序,防止數(shù)據(jù)發(fā)生錯亂。客戶端可能會對PG中的一個Object連續(xù)提交多次讀寫請求,每次請求對應(yīng)一個Transaction,在OSD層面通過PGLock將并發(fā)的讀寫請求在PG層面串行化,然后按序依次提交到ObjectStore層,ObjectStore層通過PG的OpSequencer保證順序處理讀寫請求

BlueStore寫類型有SimpleWrite、DeferredWrite兩種,所以我們分析一下SimpleWrite、DeferredWrite下的IO保序問題。

SimpleWrite

因為STATE_AIO_WAIT階段使用Libaio,所以需要保證PG對應(yīng)的OpSequencer中的txc按排隊的先后順序依次進入kv_queue被kv_sync_thread處理,也即txc在OpSequencer中的順序和在kv_queue中的順序是一致的。

void BlueStore::_txc_finish_io(TransContext *txc)
{
 // 獲取txc所屬的OpSequencer,并且加鎖,保證互斥訪問osr
 OpSequencer *osr = txc->osr.get();
 std::lock_guard<std::mutex> l(osr->qlock);
 
 // 設(shè)置狀態(tài)機的state為STATE_IO_DONE
 txc->state = TransContext::STATE_IO_DONE;
 
 // 清除txc正在運行的aio
 txc->ioc.running_aios.clear();
 
 // 定位當(dāng)前txc在osr的位置
 OpSequencer::q_list_t::iterator p = osr->q.iterator_to(*txc);
 
 while (p != osr->q.begin()) {
 --p;
 // 如果前面還有未完成IO的txc,那么需要停止當(dāng)前txc操作,等待前面txc完成IO。
 // 目的是:確保之前txc的IO都完成。
 if (p->state < TransContext::STATE_IO_DONE) {
 return;
 }
 
 // 前面的txc已經(jīng)進入大于等于STATE_KV_QUEUED的狀態(tài)了,那么遞增p并退出循環(huán)。
 // 目的是:找到狀態(tài)為STATE_IO_DONE的且在osr中排序最靠前的txc。
 if (p->state > TransContext::STATE_IO_DONE) {
 ++p;
 break;
 }
 }
 
 // 依次處理狀態(tài)為STATE_IO_DONE的tx
 // 將txc放入kv_sync_thread的kv_queue、kv_queue_unsubmitted隊列
 do {
 _txc_state_proc(&*p++);
 } while (p != osr->q.end() && p->state == TransContext::STATE_IO_DONE);
 ......
}

DeferredWrite

DeferredWrite在IO的時候也是通過Libaio提交到內(nèi)核Libaio隊列進行寫數(shù)據(jù),也需要保證IO的順序性。

相應(yīng)的數(shù)據(jù)結(jié)構(gòu)如下:

class BlueStore {
 typedef boost::intrusive::list<
 OpSequencer, boost::intrusive::member_hook<
 OpSequencer, boost::intrusive::list_member_hook<>,
 &OpSequencer::deferred_osr_queue_item>>
 deferred_osr_queue_t;
 
 // osr's with deferred io pending
 deferred_osr_queue_t deferred_queue;
}
 
class OpSequencer {
 DeferredBatch *deferred_running = nullptr;
 DeferredBatch *deferred_pending = nullptr;
}
 
struct DeferredBatch {
 OpSequencer *osr;
 
 // txcs in this batch
 deferred_queue_t txcs;
}

BlueStore內(nèi)部包含一個成員變量deferred_queue;deferred_queue隊列包含需要執(zhí)行DeferredIO的OpSequencer;每個OpSequencer包含deferred_running和deferred_pending兩個DeferredBatch類型的變量;DeferredBatch包含一個txc數(shù)組。

如果PG有寫請求,會在PG對應(yīng)的OpSequencer中的deferred_pending中排隊加入txc,待時機成熟的時候,一次性提交所有txc給Libaio,執(zhí)行完成后才會進行下一次提交,這樣不會導(dǎo)致DeferredIO亂序。

void BlueStore::_deferred_queue(TransContext *txc)
{
 deferred_lock.lock();
 // 排隊osr
 if (!txc->osr->deferred_pending && !txc->osr->deferred_running) {
 deferred_queue.push_back(*txc->osr);
 }
 
 // 追加txc到deferred_pending中
 txc->osr->deferred_pending->txcs.push_back(*txc);
 
 _deferred_submit_unlock(txc->osr.get());
 ......
}
 
void BlueStore::_deferred_submit_unlock(OpSequencer *osr)
{
 ......
 // 切換指針,保證每次操作完成后才會進行下一次提交
 osr->deferred_running = osr->deferred_pending;
 osr->deferred_pending = nullptr;
 ......
 
 while (true) {
 ......
 // 準(zhǔn)備所有txc的寫buffer
 int r = bdev->aio_write(start, bl, &b->ioc, false);
 }
 
 ......
 // 一次性提交所有txc
 bdev->aio_submit(&b->ioc);
}

線程隊列

線程+隊列是實現(xiàn)異步操作的基礎(chǔ)。BlueStore的一次IO經(jīng)過狀態(tài)機要進入多個隊列并被不同的線程處理然后回調(diào),線程+隊列是BlueStore事物狀態(tài)機的重要組成部分。BlueStore中的線程大致有7種。

  • mempool_thread:無隊列,后臺監(jiān)控內(nèi)存的使用情況,超過內(nèi)存使用的限制便會做trim。

  • aio_thread:隊列為Libaio內(nèi)核queue,收割完成的aio事件。

  • discard_thread:隊列為discard_queued,對SSD磁盤上的extent做Trim。

  • kv_sync_thread:隊列為kv_queue、deferred_done_queue、deferred_stable_queue,sync元數(shù)據(jù)和數(shù)據(jù)。

  • kv_finalize_thread:隊列為kv_committing_to_finalize、deferred_stable_to_finalize,執(zhí)行清理功能。

  • deferred_finisher:調(diào)用回調(diào)函數(shù)提交DeferredIO的請求。

  • finishers:多個回調(diào)線程Finisher,通知用戶請求完成。

我們主要分析aio_threadkv_sync_thread、kv_finalize_thread

aio_thread

aio_thread比較簡單,屬于KernelDevice模塊的,主要作用是收割完成的aio事件,并觸發(fā)回調(diào)函數(shù)。

void KernelDevice::_aio_thread() {
 while (!aio_stop) {
 ......
 // 獲取完成的aio
 int r = aio_queue.get_next_completed(cct->_conf->bdev_aio_poll_ms, aio, max);
 // 設(shè)置flush標(biāo)志為true。
 io_since_flush.store(true);
 // 獲取aio的返回值
 long r = aio[i]->get_return_value();
 ......
 // 調(diào)用aio完成的回調(diào)函數(shù)
 if (ioc->priv) {
 if (--ioc->num_running == 0) {
 aio_callback(aio_callback_priv, ioc->priv);
 }
 } 
 }
}

涉及延遲指標(biāo)state_aio_wait_latstate_io_done_lat。

kv_sync_thread

當(dāng)IO完成后,要么將txc放入隊列,要么將dbh放入隊列,雖然對應(yīng)不同隊列,但都是由kv_sync_thread執(zhí)行后續(xù)操作。

對于SimpleWrite,都是寫新的磁盤block(如果是cow,也是寫新的block,只是事務(wù)中k/v操作增加對舊的block的回收操作),所以先由aio_thread寫block,再由kv_sync_thread同步元信息,無論什么時候掛掉,數(shù)據(jù)都不會損壞。

對于DeferredWrite,在事物的prepare階段將需要DeferredWrite的數(shù)據(jù)作為k/v對(也稱為WAL)寫入基于RocksDB封裝的db_transaction中,此時還在內(nèi)存,kv_sync_thread第一次的commit操作中,將wal持久化在了k/v系統(tǒng)中,然后進行后續(xù)的操作,異常的情況,可以通過回放wal,數(shù)據(jù)也不會損壞。

kv_sync_thread主要執(zhí)行的操作為:在Libaio寫完數(shù)據(jù)后,需要通過kv_sync_thread更新元數(shù)據(jù)k/v,主要包含object的Onode、擴展屬性、FreelistManager的磁盤空間信息等等,這些必須按順序操作。

涉及的隊列如下:

  • kv_queue:需要執(zhí)行commit的txc隊列。將kv_queue中的txc存入kv_committing中,并提交給RocksDB,即執(zhí)行操作db->submit_transaction,設(shè)置狀態(tài)為STATE_KV_SUBMITTED,并將kv_committing中的txc放入kv_committing_to_finalize,等待線程kv_finalize_thread執(zhí)行。

  • deferred_done_queue:已經(jīng)完成DeferredIO操作的dbh隊列,還沒有sync磁盤。這個隊列的dbh會有兩種結(jié)果: 1) 如果沒有做flush操作,會將其放入deferred_stable_queue待下次循環(huán)繼續(xù)處理 2) 如果做了flush操作,說明數(shù)據(jù)已經(jīng)落盤,即已經(jīng)是stable的了,直接將其插入deferred_stable_queue隊列。這里stable的意思就是數(shù)據(jù)已經(jīng)sync到磁盤了,前面RocksDB中記錄的wal沒用可以刪除了。

  • deferred_stable_queue:DeferredIO已經(jīng)落盤,等待清理RocksDB中的WAL。依次操作dbh中的txc,將RocksDB中的wal刪除,然后dbh入隊列deferred_stable_to_finalize,等待線程kv_finalize_thread執(zhí)行。

void BlueStore::_kv_sync_thread() {
 while (true) {
 // 交換指針
 kv_committing.swap(kv_queue);
 kv_submitting.swap(kv_queue_unsubmitted);
 deferred_done.swap(deferred_done_queue);
 deferred_stable.swap(deferred_stable_queue)
 
 // 處理 deferred_done_queue
 if (force_flush) {
 // flush/barrier on block device
 bdev->flush();
 // if we flush then deferred done are now deferred stable
 deferred_stable.insert(deferred_stable.end(),
 deferred_done.begin(),
 deferred_done.end());
 deferred_done.clear();
 }
 
 // 處理 kv_queue
 for (auto txc : kv_committing) {
 int r = cct->_conf->bluestore_debug_omit_kv_commit
 ? 0
 : db->submit_transaction(txc->t);
 _txc_applied_kv(txc);
 }
 
 // 處理 deferred_stable_queue
 for (auto b : deferred_stable) {
 for (auto &txc : b->txcs) {
 get_deferred_key(wt.seq, &key);
 synct->rm_single_key(PREFIX_DEFERRED, key);
 }
 }
 
 // submit synct synchronously (block and wait for it to commit)
 // 同步kv,有設(shè)置bluefs_extents、刪除wal兩種操作
 int r = cct->_conf->bluestore_debug_omit_kv_commit
 ? 0
 : db->submit_transaction_sync(synct);
 
 // 放入finalize線程隊列,并通知其處理。
 std::unique_lock<std::mutex> m(kv_finalize_lock);
 kv_committing_to_finalize.swap(kv_committing);
 deferred_stable_to_finalize.swap(deferred_stable);
 kv_finalize_cond.notify_one();
 }
}

涉及延遲指標(biāo)state_kv_queued_lat、state_kv_committing_lat、kv_lat

kv_finalize_thread

清理線程,包含兩個隊列:

  • kv_committing_to_finalize:再次調(diào)用_txc_state_proc進入狀態(tài)機,設(shè)置狀態(tài)為STATE_KV_DONE,并執(zhí)行回調(diào)函數(shù)通知用戶io操作完成。

  • deferred_stable_to_finalize:遍歷deferred_stable中的dbh,調(diào)用_txc_state_proc進入狀態(tài)機,設(shè)置狀態(tài)為STATE_FINISHING,繼續(xù)調(diào)用_txc_finish,設(shè)置狀態(tài)為STATE_DONE,狀態(tài)機結(jié)束,事物完成。

void BlueStore::_kv_finalize_thread() {
 while (true) {
 // 交換指針
 kv_committed.swap(kv_committing_to_finalize);
 deferred_stable.swap(deferred_stable_to_finalize);
 
 // 處理kv_committing_to_finalize隊列
 while (!kv_committed.empty()) {
 TransContext *txc = kv_committed.front();
 _txc_state_proc(txc);
 kv_committed.pop_front();
 }
 
 // 處理deferred_stable_to_finalize
 for (auto b : deferred_stable) {
 auto p = b->txcs.begin();
 while (p != b->txcs.end()) {
 TransContext *txc = &*p;
 p = b->txcs.erase(p); // unlink here because
 _txc_state_proc(txc); // this may destroy txc
 }
 delete b;
 }
 deferred_stable.clear();
 }
}

涉及延遲指標(biāo)state_deferred_cleanup_lat、state_finishing_lat

IO狀態(tài)

主要分為SimpleWrite、DeferredWrite、SimpleWrite+DeferredWrite。

到此,相信大家對“BlueStore事物狀態(tài)機是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI