bluestore的写入流程
正常写(Direct AIO Write)和延迟写(Deferred Write),两者在数据落盘时机上有本质区别,但元数据(onode)都通过 RocksDB 持久化。
·
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
这是两条路径分叉的核心:
四、正常写(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 吞吐量。
更多推荐


所有评论(0)