BlueStore 写入流程总结

整体架构概览

BlueStore 的写入流程分为两条路径:正常写(Direct AIO Write)延迟写(Deferred Write),两者在数据落盘时机上有本质区别,但元数据(onode)都通过 RocksDB 持久化。


一、写入总流程(queue_transactions 入口)

queue_transactions()
    │
    ├─ 1. _txc_create()          创建 TransContext(事务上下文)
    ├─ 2. _txc_add_transaction() 解析 ObjectStore::Transaction,调用 _write()
    ├─ 3. _txc_calc_cost()       计算事务代价(用于流控)
    ├─ 4. _txc_write_nodes()     序列化 onode 写入 RocksDB 事务
    ├─ 5. 若有 deferred_txn:    将 deferred 操作序列化写入 RocksDB(PREFIX_DEFERRED)
    ├─ 6. _txc_finalize_kv()     最终化 KV 事务(释放旧 extent 等)
    └─ 7. _txc_state_proc()      启动事务状态机

二、数据写入核心:_do_write 的三步

_do_write(offset, length, bl)
    │
    ├─ Step 1: _choose_write_options()   决定是否压缩、是否 buffered
    ├─ Step 2: _do_write_data()          将数据分片,分配到 WriteContext
    └─ Step 3: _do_alloc_write()         分配磁盘空间,决定 deferred 还是 direct AIO
Step 2:_do_write_data — 数据分片
_do_write_data(offset, length, bl)
    │
    ├─ 若 offset 和 end 在同一个 min_alloc_size 块内:
    │      └─ _do_write_small()   ← 小写(< min_alloc_size,通常 4KB/16KB)
    │
    └─ 否则(跨块):
           ├─ head 部分(不对齐头)→ _do_write_small()
           ├─ middle 部分(对齐中间)→ _do_write_big()
           └─ tail 部分(不对齐尾)→ _do_write_small()
Step 3:_do_alloc_write — 分配空间 + 决定写路径
_do_alloc_write()
    │
    ├─ 压缩处理(可选)
    ├─ alloc->allocate() 从空闲空间分配物理 extent
    ├─ 计算 checksum
    │
    └─ 对每个 write item 决定写路径:
           │
           ├─ 若 length <= prefer_deferred_size(默认 HDD=64KB, SSD=0):
           │      └─ ★ Deferred Write:
           │             _get_deferred_op() 创建 deferred_op
           │             记录 extents + data 到 txc->deferred_txn
           │             (不立即写磁盘!)
           │
           └─ 否则:
                  └─ ★ Direct AIO Write:
                         bdev->aio_write() 立即提交 AIO
                         记录到 txc->ioc(IO Context)

三、事务状态机:_txc_state_proc

这是两条路径分叉的核心:

queue_transactions()

有 Direct AIO 待完成

无 AIO(纯 deferred 或纯元数据写)

AIO 完成回调 _txc_finish_io()

加入 kv_queue

bluestore_sync_submit_transaction=true 时直接提交

kv_sync_thread 提交 RocksDB 事务

RocksDB sync 完成(on_commit 回调触发)

有 deferred_txn

无 deferred_txn(正常写结束)

deferred AIO 完成 _deferred_aio_finish()

清理 deferred KV 记录

_txc_finish(),释放资源,触发 on_applied

STATE_PREPARE

STATE_AIO_WAIT

STATE_IO_DONE

STATE_KV_QUEUED

STATE_KV_SUBMITTED

STATE_KV_DONE

STATE_DEFERRED_QUEUED

STATE_FINISHING

STATE_DEFERRED_CLEANUP


四、正常写(Direct AIO)完整路径

queue_transactions
    │
    ├─ _do_alloc_write → bdev->aio_write() → txc->ioc
    │
    ├─ _txc_state_proc → STATE_PREPARE
    │       ↓ ioc.has_pending_aios() == true
    ├─ STATE_AIO_WAIT → _txc_aio_submit() → 提交 AIO 到块设备
    │       ↓ AIO 完成中断
    ├─ _txc_finish_io() → STATE_IO_DONE
    │       ↓
    ├─ STATE_KV_QUEUED → 加入 kv_queue,唤醒 kv_sync_thread
    │       ↓ kv_sync_thread
    ├─ db->submit_transaction() → STATE_KV_SUBMITTED
    │       ↓ db->submit_transaction_sync()(flush)
    ├─ STATE_KV_DONE → 无 deferred_txn
    │       ↓
    └─ STATE_FINISHING → _txc_finish()
                         触发 on_commit 回调(通知客户端写入完成)

关键点:数据 AIO 和元数据 KV 提交是串行的——必须等 AIO 完成后,才能提交 RocksDB 事务。这保证了崩溃恢复时,RocksDB 中记录的 extent 一定已经写入磁盘。


五、延迟写(Deferred Write)完整路径

queue_transactions
    │
    ├─ _do_alloc_write → 记录到 txc->deferred_txn(不提交 AIO)
    ├─ deferred_txn 序列化写入 RocksDB(PREFIX_DEFERRED = "L")
    │
    ├─ _txc_state_proc → STATE_PREPARE
    │       ↓ ioc.has_pending_aios() == false(无直接 AIO)
    ├─ STATE_IO_DONE → STATE_KV_QUEUED
    │       ↓ kv_sync_thread
    ├─ db->submit_transaction_sync() → STATE_KV_DONE
    │       ↓ 有 deferred_txn
    ├─ STATE_DEFERRED_QUEUED → _deferred_queue()
    │       将 deferred ops 加入 osr->deferred_pending
    │       ↓ deferred_try_submit() / deferred_aggressive
    ├─ _deferred_submit_unlock()
    │       合并 iomap,bdev->aio_write() 提交真正的数据 AIO
    │       ↓ AIO 完成
    ├─ _deferred_aio_finish()
    │       STATE_DEFERRED_CLEANUP
    │       deferred_done_queue.push_back(b)
    │       ↓ kv_sync_thread 下一轮
    ├─ kv_sync_thread 检测到 deferred_stable
    │       从 RocksDB 删除 PREFIX_DEFERRED 记录(清理 WAL)
    │       ↓
    └─ STATE_FINISHING → _txc_finish()

关键点:deferred write 先提交 RocksDB(含 deferred WAL 记录),再异步写数据。崩溃后可通过 _deferred_replay() 重放 RocksDB 中的 deferred 记录来恢复数据。


六、两种写路径的核心对比

维度 正常写(Direct AIO) 延迟写(Deferred Write)
触发条件 数据量 > prefer_deferred_size 数据量 ≤ prefer_deferred_size
默认阈值 SSD: 所有写;HDD: > 64KB HDD: ≤ 64KB
数据写入时机 KV 提交之前(AIO 先完成) KV 提交之后(异步延迟写)
崩溃恢复 无需额外恢复(AIO 已落盘) 通过 RocksDB 中的 deferred WAL 重放
适用场景 大写、顺序写(SSD 友好) 小写、覆盖写(避免读-改-写放大)
写放大 低(直接写新 extent) 低(直接覆盖旧 extent,无需 RMW)
on_commit 时机 AIO 完成 + KV sync 后 KV sync 后(数据可能还未落盘!)

七、后台线程协作关系

提交线程(OSD Worker)
    │ queue_transactions()
    ▼
kv_sync_thread(bstore_kv_sync)
    │ 批量提交 RocksDB 事务
    │ 处理 deferred_stable(清理 WAL)
    ▼
kv_finalize_thread(bstore_kv_final)
    │ 推进 txc 状态机(STATE_KV_SUBMITTED → STATE_KV_DONE)
    │ 触发 deferred_try_submit()
    ▼
deferred_finisher(defered_finisher)
    │ 处理 deferred AIO 完成后的回调
    ▼
finisher(bluestore_finisher)
    └─ 触发 on_applied / on_commit 回调

这种多线程流水线设计使得 BlueStore 能够在保证数据安全的前提下,最大化 IO 吞吐量。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐