ceph修改

为避免混乱,我做了两点取舍(都是为了"准确"):

  1. 不再用 FileStore 的 Journal 叙事(BlueStore 下不成立)。

  2. 对 BlueStore 内部写入,我按 Ceph 的真实分层表达:

    • 数据面(data):裸块设备(block device)
    • 元数据面(metadata):RocksDB(跑在 BlueFS 上)+ WAL
    • 一致性语义 :数据写入 + 元数据原子提交(映射切换)
      同时说明"为什么你会看到 WAL/日志字样"。

RBD 读写数据流(BlueStore 架构,完整版)

0. 总览:从"块"到"对象"再到"OSD 落盘"

RBD 是"块设备"语义,但 Ceph 后端是"对象存储"(RADOS)。因此任一 RBD I/O 都经历三次抽象转换:

  1. 应用/文件系统视角:写 LBA(块设备逻辑地址)
  2. *RBD 视角:切分为 rbd_data. 对象上的 offset/len(对象寻址)**
  3. OSD 视角:把对象写落到裸块设备 extents,并更新 RocksDB 元数据映射(BlueStore)

阶段一:客户端节点内部(从应用到 RBD 块设备)

1. 应用程序层

  • 应用调用 read()/write(),数据在用户态 buffer(字节流)。

2. VFS + 本地文件系统(ext4/xfs)

  • write() 进入内核,VFS 找到挂载点对应的文件系统。
  • 文件系统完成 inode/extent 等元数据处理,并把"文件偏移"变成"底层块设备的逻辑块地址(LBA)"。
  • 数据通常先进入 Page Cache(脏页)。非同步写常在这里返回(除非 fsync/O_DIRECT 等)。

3. 通用块层(Generic Block Layer)

  • I/O 合并、调度,形成 struct bio

    • 起始扇区(LBA)
    • 长度
    • page 指针

4. 进入 RBD 的两条客户端路径(你提到的 Path A / Path B)

路径 A:物理机 / 容器(内核 RBD,krbd)
  • bio 进入 /dev/rbdX 的内核块设备驱动(krbd)。
  • krbd 把块请求映射为一个或多个对象操作(后续见阶段二)。
  • 网络通信由内核 ceph 子系统(libceph)完成。
路径 B:虚拟机 / QEMU(用户态 librbd)
  • QEMU 使用 librbd 作为后端(例如 rbd: 协议)。
  • I/O 在用户态直接进入 librbd,对象映射与网络由 librados/librbd 完成。
  • 跳过内核的 krbd,但"块→对象→OSD"的逻辑完全相同。

你可以这样记:
krbd = 内核块设备实现librbd = 用户态块设备实现

两者最终都变成"对 rbd_data.* 对象的读写"。


阶段二:数据切分与位置计算(块 → 对象)

1. 对象切分(Striping / Object Mapping)

RBD 镜像按照固定参数切分为对象:

  • object_size:默认常见 4MB(可配置)
  • stripe_unit / stripe_count:决定条带分布方式
    很多常见场景 stripe_count=1,使映射更接近"按 object_size 切块"

对一个块写请求(镜像内偏移 offset,长度 len):

  • object_index = offset / object_size
  • obj_offset = offset % object_size
  • 对象名:rbd_data.<image_id>.<object_index(16进制补齐)>

若一次写跨越 object 边界,则拆成多个对象写(每个对象内是一段连续 offset/len)。

这一层就是你之前读到的 Striper::file_to_extents() 思想:
把连续逻辑范围切成"每对象最多一个连续 extent"的对象请求集合


阶段三:对象定位与网络发送(Objecter + CRUSH)

1. Objecter 计算 PG

  • 对象名 hash 后映射到 PG:

    • pgid = hash(object_id) % pg_num(概念化表达,实际还有更多参数与哈希规则)

PG 是数据放置与复制的基本单位。

2. CRUSH 计算 OSD 列表

  • Objecter 持有 OSDMap(集群拓扑与状态)。

  • 使用 CRUSH 算法,将 PG 映射为一组 OSD(有序):

    • Primary OSD(协调者)
    • Replica OSDs(副本)

3. 发送请求

  • 客户端(krbd+libceph 或 librbd/librados)直接连接 Primary OSD 发送 MOSDOp,不经过 MON 代理。

阶段四:OSD 内部处理与复制(Primary/Replica 协调)

1. Primary OSD 接收与排队

  • OSD 收到对象写请求,交由对应 PG 处理。

  • PG/ReplicatedBackend 决定:

    • 写入顺序
    • 复制策略
    • 何时 ACK(与 min_size/size、配置、故障状态有关)

2. 并发复制

  • Primary 将同一对象操作转发给所有 Replica OSD。
  • Replica OSD 执行本地写入并返回 ACK 给 Primary。

3. 客户端 ACK

  • Primary 收到满足策略要求的 ACK 后,向客户端返回成功。
  • 注意:ACK 的严格语义与配置相关(例如是否要求落到稳定存储再 ack),但在 BlueStore 下"稳定存储"的判定不再是"Journal 写成功"。

阶段五:OSD 后端持久化(BlueStore:数据面 + 元数据面)

这是你原文中"Journal/XFS"最需要纠正的地方。BlueStore 下的正确结构是:

1. BlueStore 的三块组成

(1) 数据面:裸块设备(block device)
  • 对象数据不存文件系统,不存在 rbd_data.* 的"对象文件"。
  • BlueStore 通过 Allocator 分配物理 extents(offset+len),将数据写入这些 extents。
(2) 元数据面:RocksDB(跑在 BlueFS 上)
  • 对象的元数据(非常关键)存 RocksDB:

    • object → extents 映射(extent map)
    • 对象 size、attrs、omap(键值)
    • checksum 记录
    • deferred/free 等状态
  • RocksDB 需要一个"文件接口",但 BlueStore 不用 XFS,因此引入 BlueFS

(3) BlueFS:为 RocksDB/WAL 提供"轻量文件系统"
  • BlueFS 是 BlueStore 内建的轻量 FS,专门承载:

    • RocksDB 的 SST 文件
    • RocksDB WAL
    • 以及少量 BlueStore 自身元数据文件
  • BlueFS 本质上仍然是"在裸块设备上管理少量文件/extent",目标是高性能、低开销。


2. BlueStore 写入到底怎么"安全落盘"?

你需要记住一句话(这是 BlueStore 时代的核心):

数据先写到最终 extents,随后 RocksDB 原子提交新的映射;
一致性靠"映射切换",而不是 FileStore 的 Journal 回放。

把它展开成步骤:

  1. Allocator 分配 extents(通常为新版本数据分配新空间;覆盖写也常是"写新空间+改映射")

  2. 数据写入块设备 extents(可能压缩/加密,并生成 checksum)

  3. RocksDB 原子提交元数据(extent_map/size/checksum/omap 等)

  4. 崩溃恢复时:

    • 若 DB 提交未完成:新数据不可见,旧映射仍生效
    • 若 DB 提交完成:新映射生效,数据可按映射读取并校验

3. "为什么你会看到 WAL/日志字样"?(纠正你原文里的"Journal"残影)

在 BlueStore 下你可能会听到"WAL/日志",但它不是 FileStore Journal:

  • RocksDB WAL:保证 DB 更新原子性/可重放(主要是元数据)
  • BlueFS 也有自己的元数据一致性机制(为 DB 文件提供持久与一致)

这类"日志"承担的是:

数据库/元数据层的一致性

而不是:
对象数据先写 Journal 再 flush 到后端盘


读路径补全(对称理解)

客户端侧

  • read() → VFS/FS → bio → krbd/librbd
  • 做块→对象映射,形成对 rbd_data.* 的对象读

OSD/BlueStore 侧

  1. RocksDB 查 extent_map(逻辑 offset → 物理 extents)
  2. 从块设备读取对应 extents
  3. 校验 checksum(必要时触发修复/重读副本)
  4. 拼接返回对象数据 → 客户端再拼成块数据返回给应用

关键差异总结(你写报告时建议放一段)

1) krbd vs librbd(客户端差异)

  • krbd:内核块设备 + 内核 libceph 发包
  • librbd:用户态库 + librados 发包(QEMU 常用)
    但二者的对象映射/CRUSH/复制语义一致。

2) FileStore vs BlueStore(后端差异)

  • FileStore:Journal + XFS 对象文件
  • BlueStore:裸块设备数据 extents + RocksDB(BlueFS) 元数据
    BlueStore 没有"先写 Journal 再 flush 到 XFS"的流程。

你第二段文字里需要特别纠正/保留的点(已在整合版中正确处理)

✅ 保留并正确化的点

  • Path A/Path B(krbd vs QEMU/librbd)------保留且强化为"客户端两实现"
  • BlueFS 用于承载 RocksDB ------保留且明确职责
  • "对齐大块 IO 直接写数据区"这一思路 ------保留为"数据面直接写 extents + 元数据更新"
  • "小 IO 与 WAL/DB 关系"------改写为"元数据事务依赖 RocksDB WAL",避免误导为"数据先写 WAL"

❌ 彻底删除的旧点

  • "OSD 先写 Journal(含数据本身)"
  • "Journal flush 到后端文件系统"
  • "对象最终以文件形式存在 XFS 目录"

可直接附带的简化流程图(文本版)

写路径

复制代码
App write()
→ FS/PageCache → bio
→ krbd 或 librbd
→ (block→object mapping)
→ Objecter: PG + CRUSH
→ send to Primary OSD
→ Primary forward to Replicas
→ each OSD: BlueStore allocate extents → write data → RocksDB commit mapping
→ replicas ACK → primary ACK client

读路径

复制代码
App read()
→ FS/PageCache miss → bio
→ krbd 或 librbd
→ (block→object mapping)
→ Objecter: PG + CRUSH
→ Primary OSD
→ BlueStore: RocksDB lookup mapping → read extents → checksum → assemble
→ return to client → assemble → app buffer

如果你希望我把这份"整合版"进一步升级成更像教材的版本,我建议下一步做两件事(我也可以直接给你产出):

  1. 在"阶段五 BlueStore"里加入 block.db / block.wal / block 三设备部署模式(非常常见的考试点/面试点)
  2. 增加"ACK 语义"一段:解释 applied / committed / on_applied_sync 在 OSD/BlueStore 中大概对应什么阶段(你之前贴的 bluestore.cc 正好能对上)

你希望我优先补哪一个?

相关推荐
代码游侠2 小时前
复习——网络测试工具
linux·开发语言·网络·笔记·学习·测试工具
qq_406176142 小时前
JavaScript的同步与异步
前端·网络·tcp/ip·ajax·okhttp
colus_SEU2 小时前
【计算机网络笔记】第三章 传输层
网络·笔记·计算机网络
不染尘.2 小时前
应用层之HTTP
服务器·网络·网络协议·计算机网络·http
插件开发3 小时前
win10电脑手机蓝牙连接共享手机网络
网络·智能手机·电脑
照海19Gin3 小时前
静态路由 + 浮动路由实战:小型网络互通配置指南
网络·智能路由器
糕......3 小时前
Java IO流:数据传输的艺术与机制
java·开发语言·网络·学习
米羊1213 小时前
身份威胁检测与响应(上)
网络·安全
wwwlyj1233213 小时前
劳特巴赫 cotex-m trace指南
网络