BlueStore软件架构流程分析

BlueStore软件架构流程分析

目录

  1. BlueStore概述
  2. 整体架构
  3. 核心组件
  4. 数据模型
  5. 写入流程
  6. 读取流程
  7. 事务处理
  8. 空间管理
  9. 缓存机制
  10. 压缩和校验

BlueStore概述

1.1 什么是BlueStore

BlueStore 是Ceph的默认对象存储后端,直接在块设备上存储对象数据,避免了传统文件系统的开销。它是为高性能SSD设计的存储后端。

1.2 核心特点

  • 直接管理块设备:绕过文件系统,直接在块设备上操作
  • 元数据存储在RocksDB:使用RocksDB存储所有元数据
  • BlueFS文件系统:轻量级文件系统,用于存储RocksDB的WAL和SST文件
  • 写时分配(COW):支持克隆和快照
  • 内联压缩:支持数据压缩
  • 校验和:支持数据完整性校验
  • 多设备支持:支持WAL、DB、慢速设备分离

1.3 与传统文件系统的区别

特性 传统文件系统(FileStore) BlueStore
数据存储 通过文件系统 直接在块设备
元数据 文件系统元数据 RocksDB
性能 受文件系统限制 更高性能
开销 双重写入(数据+元数据) 单次写入
适用场景 通用场景 SSD优化

整体架构

2.1 架构层次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
┌─────────────────────────────────────────────────────────┐
│ ObjectStore接口 │
│ (OSD层调用的存储接口) │
└───────────────────────┬───────────────────────────────────┘

┌───────────────────────▼───────────────────────────────────┐
│ BlueStore核心 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Collection │ │ Onode │ │ Buffer │ │
│ │ (集合) │ │ (对象节点) │ │ (缓冲区) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Extent │ │ Blob │ │ SharedBlob │ │
│ │ (逻辑范围) │ │ (数据块) │ │ (共享块) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└───────────────────────┬───────────────────────────────────┘

┌───────────────┼───────────────┐
│ │ │
┌───────▼──────┐ ┌─────▼──────┐ ┌─────▼──────┐
│ Allocator │ │ Freelist │ │ BlueFS │
│ (分配器) │ │ Manager │ │ (文件系统) │
│ │ │ (空闲管理) │ │ │
└───────┬──────┘ └─────┬──────┘ └─────┬──────┘
│ │ │
┌───────▼───────────────▼───────────────▼──────┐
│ RocksDB (元数据) │
│ (通过BlueFS存储WAL和SST) │
└───────────────────────┬───────────────────────┘

┌───────────────────────▼───────────────────────┐
│ 块设备 (BlockDevice) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ WAL │ │ DB │ │ Slow │ │
│ │ (快速) │ │ (快速) │ │ (慢速) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└───────────────────────────────────────────────┘

2.2 设备布局

BlueStore支持三种设备类型:

  1. WAL设备:存储RocksDB的WAL(Write-Ahead Log)文件

    • 要求:低延迟、高IOPS
    • 通常:NVMe SSD
  2. DB设备:存储RocksDB的SST文件

    • 要求:中等延迟、高IOPS
    • 通常:SSD
  3. 慢速设备(Slow):存储对象数据

    • 要求:大容量
    • 通常:HDD或大容量SSD

设备布局示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
块设备布局:
┌─────────────────────────────────────────┐
│ Label (设备标签) │
│ - OSD UUID │
│ - 设备大小 │
│ - 创建时间 │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ BlueFS (如果使用BlueFS) │
│ - WAL文件 │
│ - SST文件 │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ BlueStore数据区 │
│ - 对象数据 │
│ - 空闲空间由Allocator管理 │
└─────────────────────────────────────────┘

核心组件

3.1 BlueStore类

位置BlueStore.h / BlueStore.cc

职责

  • 实现ObjectStore接口
  • 管理所有存储操作(read、write、transaction等)
  • 协调各个子组件

关键成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class BlueStore : public ObjectStore {
// 设备管理
BlockDevice *bdev; // 主数据设备
BlockDevice *bdev_slow; // 慢速设备
BlockDevice *bdev_wal; // WAL设备
BlockDevice *bdev_db; // DB设备

// 元数据存储
KeyValueDB *db; // RocksDB实例

// 文件系统
BlueFS *bluefs; // BlueFS文件系统

// 空间管理
Allocator *alloc; // 空间分配器
FreelistManager *fm; // 空闲列表管理器

// 缓存
OnodeCache *onode_cache; // Onode缓存
BufferCache *buffer_cache; // 缓冲区缓存

// 集合管理
ceph::unordered_map<coll_t, CollectionRef> coll_map;
};

3.2 Collection(集合)

位置BlueStore.h 内部结构

职责

  • 管理一个PG(Placement Group)的对象集合
  • 维护Onode缓存
  • 管理事务上下文

关键成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct Collection {
coll_t cid; // 集合ID
bluestore_cnode_t cnode; // 集合节点元数据

// Onode管理
OnodeCacheShard *onode_cache; // Onode缓存分片
ceph::unordered_map<ghobject_t, OnodeRef> onode_map;

// 共享Blob管理
SharedBlobSet shared_blob_set;

// 统计信息
PerfCounters *logger;
};

3.3 Onode(对象节点)

位置BlueStore.h 内部结构

职责

  • 表示一个对象的元数据
  • 管理对象的逻辑到物理映射(ExtentMap)
  • 管理对象的缓冲区(BufferSpace)

关键成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Onode {
ghobject_t oid; // 对象ID
bluestore_onode_t onode; // 持久化元数据

// 逻辑到物理映射
ExtentMap extent_map; // Extent映射表

// 缓冲区管理
BufferSpace buffer_space; // 缓冲区空间

// OMAP管理
OMapSpace omap_space; // OMAP空间

// 引用计数
std::atomic_int nref;
};

Onode元数据结构bluestore_onode_t):

1
2
3
4
5
6
7
8
9
struct bluestore_onode_t {
uint64_t nid; // 节点ID
uint64_t size; // 对象大小
utime_t mtime; // 修改时间
utime_t atime; // 访问时间
uint32_t expected_object_size; // 期望对象大小
uint32_t alloc_hint_flags; // 分配提示标志
// ... 其他元数据
};

3.4 Extent(逻辑范围)

位置BlueStore.h 内部结构

职责

  • 表示对象的一个逻辑范围
  • 映射到物理Blob

关键成员

1
2
3
4
5
6
struct Extent {
uint32_t logical_offset; // 逻辑偏移
uint32_t length; // 长度
BlobRef blob; // 指向Blob
uint32_t blob_offset; // 在Blob中的偏移
};

3.5 Blob(数据块)

位置BlueStore.h 内部结构

职责

  • 表示物理存储的数据块
  • 管理物理extent(pextent)
  • 支持压缩和校验

关键成员

1
2
3
4
5
6
7
8
9
10
struct Blob {
uint64_t id; // Blob ID
bluestore_blob_t blob; // 持久化Blob元数据

// 物理extent列表
PExtentVector extents; // 物理extent向量

// 共享Blob引用
SharedBlobRef shared_blob; // 共享Blob引用
};

Blob元数据结构bluestore_blob_t):

1
2
3
4
5
6
7
8
9
10
11
struct bluestore_blob_t {
uint64_t id; // Blob ID
uint32_t logical_length; // 逻辑长度
uint32_t compressed_length; // 压缩后长度
uint8_t compression; // 压缩算法
uint8_t csum_type; // 校验和类型
uint8_t csum_chunk_order; // 校验和块大小
uint32_t flags; // 标志位
PExtentVector extents; // 物理extent列表
bluestore_extent_ref_map_t ref_map; // 引用映射
};

3.6 SharedBlob(共享Blob)

位置BlueStore.h 内部结构

职责

  • 管理共享Blob的引用计数
  • 支持写时复制(COW)
  • 管理共享Blob的缓冲区缓存

关键成员

1
2
3
4
5
6
7
struct SharedBlob {
std::atomic_int nref; // 引用计数
bluestore_shared_blob_t *persistent; // 持久化元数据

// 引用映射
bluestore_extent_ref_map_t ref_map; // 引用映射表
};

3.7 Buffer(缓冲区)

位置BlueStore.h 内部结构

职责

  • 缓存对象数据
  • 管理写入状态
  • 支持读写缓存

状态

  • STATE_CLEAN:干净状态(已写入磁盘)
  • STATE_WRITING:正在写入
  • STATE_DIRTY:脏数据(待写入)

3.8 BlueFS(文件系统)

位置BlueFS.h / BlueFS.cc

职责

  • 轻量级文件系统
  • 管理RocksDB的WAL和SST文件
  • 直接在块设备上操作

关键特性

  • 日志结构文件系统
  • 支持三种设备(WAL、DB、Slow)
  • 文件分配和回收

3.9 Allocator(分配器)

位置Allocator.h / 各种实现

职责

  • 管理空闲空间
  • 分配物理extent
  • 支持多种分配算法

分配器类型

  • StupidAllocator:简单分配器
  • BitmapAllocator:位图分配器
  • AvlAllocator:AVL树分配器
  • BtreeAllocator:B树分配器
  • Btree2Allocator:B树2分配器
  • HybridAllocator:混合分配器

3.10 FreelistManager(空闲列表管理器)

位置FreelistManager.h / 实现

职责

  • 持久化空闲空间信息到RocksDB
  • 管理空闲空间的分配和释放
  • 支持多种实现(Bitmap、Btree等)

数据模型

4.1 逻辑到物理映射

1
2
3
4
5
6
7
8
9
10
11
12
13
对象 (Object)

├─► Onode (对象元数据)
│ │
│ └─► ExtentMap (逻辑范围映射)
│ │
│ ├─► Extent 1: [0, 1MB) → Blob A
│ ├─► Extent 2: [1MB, 2MB) → Blob B
│ └─► Extent 3: [2MB, 3MB) → Blob A (共享)

└─► BufferSpace (缓冲区空间)

└─► Buffer缓存 (内存中的数据)

4.2 物理存储结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Blob (数据块)

├─► 物理Extent列表
│ ├─► PExtent 1: [offset=100MB, length=1MB]
│ ├─► PExtent 2: [offset=200MB, length=1MB]
│ └─► PExtent 3: [offset=500MB, length=1MB]

├─► 压缩信息
│ ├─► 压缩算法: snappy
│ ├─► 原始长度: 2MB
│ └─► 压缩长度: 1.5MB

└─► 校验和信息
├─► 校验和类型: crc32c
└─► 校验和块: 每64KB一个校验和

4.3 RocksDB键空间

BlueStore使用以下键前缀:

1
2
3
4
5
6
7
8
PREFIX_SUPER = "S"        // 超级块信息
PREFIX_STAT = "T" // 统计信息
PREFIX_COLL = "C" // Collection元数据
PREFIX_OBJ = "O" // Onode元数据
PREFIX_OMAP = "M" // OMAP键值对
PREFIX_DEFERRED = "L" // 延迟事务
PREFIX_ALLOC = "B" // 空闲空间(FreelistManager)
PREFIX_SHARED_BLOB = "X" // 共享Blob元数据

4.4 对象键编码

对象键的编码格式:

1
2
3
[shard_id + 0x80] + [pool_id + 2^63] + [hash(bit_reversed)] + 
[namespace(escaped)] + [key(escaped)] + ['<'|'='|'>'] +
[object_name(escaped)] + [snap] + [generation] + ['o']

写入流程

5.1 写入流程概览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
客户端写入请求


BlueStore::write()

├─► 获取或创建Collection
├─► 获取或创建Onode
└─► 创建事务上下文 (TransContext)


Writer::do_write()

├─► 处理缓冲区 (BufferSpace)
├─► 分配空间 (Allocator)
├─► 创建Blob
├─► 压缩数据 (可选)
├─► 计算校验和
└─► 调度I/O


BlueStore::_txc_add_transaction()

├─► 准备事务 (prepare)
├─► 提交I/O (aio_submit)
├─► 等待I/O完成 (aio_wait)
└─► 提交元数据 (kv_commit)


事务完成

5.2 详细写入步骤

步骤1:接收写入请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int BlueStore::write(
coll_t cid, // 集合ID
const ghobject_t& oid, // 对象ID
uint64_t offset, // 偏移
size_t len, // 长度
const bufferlist& bl, // 数据
uint32_t fadvise_flags) // 建议标志
{
// 1. 获取Collection
CollectionRef c = _get_collection(cid);

// 2. 获取或创建Onode
OnodeRef o = c->get_onode(oid, true);

// 3. 创建事务上下文
TransContext *txc = _get_trans_context(c);

// 4. 执行写入
Writer w(this, txc, wctx, o);
w.do_write(offset, bl);

// 5. 提交事务
_txc_add_transaction(txc);
return 0;
}

步骤2:Writer处理写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void Writer::do_write(uint32_t location, bufferlist& data)
{
// 1. 处理数据分割和压缩
blob_vec blobs;
_split_data(location, data, blobs);

// 2. 对齐到磁盘块
_align_to_disk_block(location, ref_end, blobs);

// 3. 处理现有Blob的修改
_do_put_blobs(location, data_end, ref_end, blobs, after_punch_it);

// 4. 分配新空间(如果需要)
_defer_or_allocate(need_size);

// 5. 创建新Blob
_do_put_new_blobs(location, ref_end, bd_it, bd_end);
}

步骤3:空间分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void Writer::_defer_or_allocate(uint32_t need_size)
{
// 1. 检查是否需要延迟写入
if (should_defer) {
// 延迟写入:先写入缓冲区,稍后分配空间
do_deferred = true;
return;
}

// 2. 从Allocator分配空间
PExtentVector extents;
int64_t allocated = alloc->allocate(
need_size, // 需要的大小
block_size, // 块大小
max_alloc_size, // 最大分配大小
hint, // 分配提示
&extents); // 输出的extent列表

// 3. 记录分配的extent
allocated.insert(allocated.end(), extents.begin(), extents.end());
}

步骤4:Blob创建和数据放置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
BlobRef Writer::_blob_create_with_data(
uint32_t in_blob_offset,
bufferlist& disk_data)
{
// 1. 创建Blob
BlobRef b = new Blob();
b->id = generate_blob_id();

// 2. 分配物理extent
PExtentVector extents;
_get_disk_space(disk_data.length(), extents);
b->extents = extents;

// 3. 设置Blob元数据
b->blob.logical_length = disk_data.length();
b->blob.compressed_length = compressed ? compressed_size : 0;
b->blob.compression = compression_algorithm;

// 4. 计算校验和
if (csum_type) {
calculate_checksums(b, disk_data);
}

return b;
}

步骤5:调度I/O

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void Writer::_schedule_io(
const PExtentVector& disk_extents,
bufferlist data)
{
// 1. 将数据分割到extent
for (auto& e : disk_extents) {
uint64_t disk_offset = e.offset;
uint32_t length = e.length;

// 2. 创建AIO请求
IOContext *ioc = new IOContext();
ioc->aio_write(disk_offset, data);

// 3. 添加到事务上下文
txc->aio_write_queue.push_back(ioc);
}
}

步骤6:事务提交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void BlueStore::_txc_add_transaction(TransContext *txc)
{
// 1. 准备阶段
_txc_prepare(txc);
├─► 更新Onode元数据
├─► 更新ExtentMap
├─► 更新Blob元数据
└─► 准备RocksDB事务

// 2. 提交I/O
_txc_submit_aio(txc);
└─► 提交所有AIO请求到块设备

// 3. 等待I/O完成
_txc_aio_wait(txc);
└─► 等待所有AIO完成

// 4. 提交元数据
_txc_commit_kv(txc);
└─► 提交RocksDB事务

// 5. 完成阶段
_txc_finish(txc);
├─► 更新缓存
├─► 释放资源
└─► 更新统计信息
}

5.3 小写优化

对于小写(小于bluestore_small_io_size),BlueStore使用特殊优化:

  1. 内联写入:小数据直接写入Onode的OMAP
  2. 延迟分配:先写入缓冲区,稍后批量分配空间
  3. 合并写入:多个小写合并为一个大写

5.4 大写处理

对于大写(大于bluestore_big_io_size),BlueStore:

  1. 直接分配:立即分配空间
  2. 直接写入:直接写入块设备,不经过缓冲区
  3. 支持压缩:如果启用压缩,会压缩数据

读取流程

6.1 读取流程概览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
客户端读取请求


BlueStore::read()

├─► 获取Collection
├─► 获取Onode
└─► 从BufferSpace读取

├─► 检查缓冲区缓存
│ ├─► 命中:直接返回
│ └─► 未命中:继续

├─► 查找ExtentMap
│ └─► 确定需要读取的Blob

├─► 从Blob读取数据
│ ├─► 查找物理extent
│ ├─► 调度AIO读取
│ ├─► 等待I/O完成
│ ├─► 验证校验和
│ └─► 解压缩(如果需要)

└─► 更新缓存
└─► 将数据加入BufferSpace

6.2 详细读取步骤

步骤1:接收读取请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int BlueStore::read(
coll_t cid, // 集合ID
const ghobject_t& oid, // 对象ID
uint64_t offset, // 偏移
size_t len, // 长度
bufferlist& bl, // 输出缓冲区
uint32_t op_flags) // 操作标志
{
// 1. 获取Collection
CollectionRef c = _get_collection(cid);

// 2. 获取Onode
OnodeRef o = c->get_onode(oid, false);
if (!o) {
return -ENOENT;
}

// 3. 从BufferSpace读取
ready_regions_t ready;
interval_set<uint32_t> missing;
o->buffer_space.read(
cache, // 缓存
offset, // 偏移
len, // 长度
ready, // 已就绪区域
missing); // 缺失区域

// 4. 从磁盘读取缺失区域
if (!missing.empty()) {
_read_from_disk(o, missing, bl);
}

return 0;
}

步骤2:从BufferSpace读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void BufferSpace::read(
BufferCacheShard* cache,
uint32_t offset,
uint32_t length,
ready_regions_t& res,
interval_set<uint32_t>& res_intervals)
{
// 1. 查找覆盖范围的Buffer
auto it = _data_lower_bound(offset);
uint32_t pos = offset;
uint32_t end = offset + length;

while (pos < end && it != buffer_map.end()) {
Buffer& b = *it;

// 2. 检查Buffer是否覆盖当前位置
if (b.offset <= pos && pos < b.offset + b.length) {
// Buffer覆盖,添加到结果
uint32_t b_off = pos - b.offset;
uint32_t b_len = min(b.length - b_off, end - pos);
res[pos] = b.data.substr(b_off, b_len);
res_intervals.insert(pos, b_len);
pos += b_len;
} else if (pos < b.offset) {
// 有间隙,需要从磁盘读取
res_intervals.insert(pos, b.offset - pos);
pos = b.offset;
} else {
++it;
}
}

// 3. 处理末尾间隙
if (pos < end) {
res_intervals.insert(pos, end - pos);
}
}

步骤3:从磁盘读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void BlueStore::_read_from_disk(
OnodeRef o,
interval_set<uint32_t>& missing,
bufferlist& bl)
{
// 1. 遍历缺失区域
for (auto& p : missing) {
uint32_t offset = p.first;
uint32_t length = p.second;

// 2. 查找ExtentMap
auto extents = o->extent_map.get_containing_extents(offset, length);

// 3. 对每个Extent读取
for (auto& ext : extents) {
BlobRef blob = ext.blob;
uint32_t blob_offset = ext.blob_offset + (offset - ext.logical_offset);

// 4. 从Blob读取
_read_blob(blob, blob_offset, length, bl);
}
}
}

步骤4:从Blob读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
void BlueStore::_read_blob(
BlobRef blob,
uint32_t blob_offset,
uint32_t length,
bufferlist& bl)
{
// 1. 查找物理extent
PExtentVector extents = blob->get_extents_containing(blob_offset, length);

// 2. 调度AIO读取
vector<IOContext*> ios;
for (auto& e : extents) {
uint64_t disk_offset = e.offset + (blob_offset - e.logical_offset);
IOContext *ioc = new IOContext();
ioc->aio_read(disk_offset, length);
ios.push_back(ioc);
}

// 3. 提交AIO
bdev->aio_submit(ios);

// 4. 等待完成
for (auto ioc : ios) {
ioc->wait();

// 5. 验证校验和
if (blob->blob.csum_type) {
verify_checksum(blob, ioc->bl);
}

// 6. 解压缩(如果需要)
if (blob->blob.compression) {
decompress(blob, ioc->bl);
}

bl.append(ioc->bl);
}

// 7. 更新缓存
o->buffer_space.did_read(cache, blob_offset, std::move(bl));
}

6.3 读取优化

  1. 预读(Read-ahead):预测性读取相邻数据
  2. 缓存命中:优先从BufferSpace读取
  3. 并行读取:多个extent并行读取
  4. 校验和验证:读取时验证数据完整性

事务处理

7.1 事务模型

BlueStore使用两阶段提交:

  1. 准备阶段(Prepare)

    • 更新内存中的元数据
    • 准备RocksDB事务
    • 调度AIO操作
  2. 提交阶段(Commit)

    • 等待AIO完成
    • 提交RocksDB事务
    • 更新缓存状态

7.2 TransContext(事务上下文)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct TransContext {
CollectionRef collection; // 所属集合
ObjectStore::Transaction t; // 事务对象

// I/O队列
list<IOContext*> aio_write_queue; // 写入队列
list<IOContext*> aio_read_queue; // 读取队列

// 统计信息
uint64_t bytes_written;
uint64_t bytes_read;

// 状态
enum {
STATE_PREPARE, // 准备中
STATE_AIO_WAIT, // 等待AIO
STATE_IO_DONE, // I/O完成
STATE_KV_QUEUED, // KV已排队
STATE_KV_COMMITTING, // KV提交中
STATE_KV_DONE, // KV完成
STATE_FINISHING, // 完成中
STATE_DONE // 完成
} state;
};

7.3 事务提交流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
_txc_add_transaction(txc)


_txc_prepare(txc)
├─► 更新Onode元数据
├─► 更新ExtentMap
├─► 更新Blob元数据
├─► 更新FreelistManager
└─► 准备RocksDB事务


_txc_submit_aio(txc)
└─► 提交所有AIO到块设备


_txc_aio_wait(txc)
└─► 等待所有AIO完成


_txc_commit_kv(txc)
└─► 提交RocksDB事务


_txc_finish(txc)
├─► 更新缓存状态
├─► 释放资源
└─► 更新统计信息

空间管理

8.1 分配流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
需要分配空间


Allocator::allocate()

├─► 查找空闲extent
│ ├─► StupidAllocator: 线性搜索
│ ├─► BitmapAllocator: 位图查找
│ ├─► AvlAllocator: AVL树查找
│ └─► BtreeAllocator: B树查找

├─► 选择最佳extent
│ └─► 考虑hint、连续性等

└─► 返回分配的extent列表


FreelistManager::allocate()
└─► 更新RocksDB中的空闲列表

8.2 释放流程

1
2
3
4
5
6
7
8
9
10
11
12
13
需要释放空间


Allocator::release()

├─► 将extent标记为空闲
│ └─► 更新分配器内部结构

└─► 合并相邻空闲extent(可选)


FreelistManager::release()
└─► 更新RocksDB中的空闲列表

8.3 碎片整理

BlueStore支持在线碎片整理:

  1. 识别碎片:扫描Blob,识别碎片化区域
  2. 迁移数据:将数据迁移到连续区域
  3. 更新映射:更新ExtentMap
  4. 释放旧空间:释放原来的extent

缓存机制

8.1 Onode缓存

目的:缓存对象的元数据(Onode)

实现

  • 使用LRU策略
  • 分片缓存(减少锁竞争)
  • 支持固定(pinned)Onode

缓存结构

1
2
3
4
5
struct OnodeCacheShard {
ceph::mutex lock; // 锁
LRUOnodeCache cache; // LRU缓存
map<ghobject_t, OnodeRef> pinned; // 固定的Onode
};

8.2 Buffer缓存

目的:缓存对象数据

实现

  • 使用PriorityCache
  • 支持多级缓存
  • 支持写回缓存

缓存状态

  • CLEAN:数据与磁盘一致
  • WRITING:正在写入
  • DIRTY:数据已修改,待写入

8.3 缓存策略

  1. 读缓存:读取的数据加入缓存
  2. 写缓存:小写先写入缓存
  3. 预读:预测性读取
  4. 淘汰:LRU淘汰策略

压缩和校验

9.1 压缩

支持的算法

  • snappy:快速压缩
  • zlib:标准压缩
  • lz4:快速压缩
  • zstd:高性能压缩

压缩流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
写入数据


尝试压缩

├─► 压缩率检查
│ └─► 如果压缩率 < threshold,使用压缩

├─► 压缩数据
│ └─► 使用选择的压缩算法

└─► 存储压缩数据
├─► 设置compressed_length
└─► 设置compression算法

压缩配置

  • bluestore_compression_algorithm:压缩算法
  • bluestore_compression_min_blob_size:最小压缩大小
  • bluestore_compression_max_blob_size:最大压缩大小
  • bluestore_compression_required_ratio:压缩率要求

9.2 校验和

支持的算法

  • crc32c:CRC32校验
  • xxhash32:XXHash校验
  • xxhash64:XXHash64校验

校验和流程

1
2
3
4
5
6
7
8
9
10
写入数据


计算校验和

├─► 按块计算(每64KB一个)
│ └─► 块大小由csum_chunk_order决定

└─► 存储校验和
└─► 存储在Blob元数据中

读取验证

1
2
3
4
5
6
7
8
9
读取数据


验证校验和

├─► 读取数据块
├─► 计算校验和
├─► 与存储的校验和比较
└─► 如果不匹配,返回EIO错误

总结

核心优势

  1. 高性能:直接操作块设备,避免文件系统开销
  2. 灵活性:支持多种分配算法和压缩算法
  3. 可靠性:支持校验和、事务、写时复制
  4. 可扩展性:支持多设备、大容量

关键设计决策

  1. 元数据与数据分离:元数据存储在RocksDB,数据直接存储在块设备
  2. 写时分配:写入时分配空间,支持延迟分配
  3. 多级缓存:Onode缓存和Buffer缓存
  4. 异步I/O:使用AIO提高并发性能

适用场景

  • SSD存储:针对SSD优化
  • 高性能要求:需要低延迟、高IOPS
  • 大容量存储:支持PB级存储
  • 云存储:适合云环境部署

参考资料

  1. Ceph源码:cephMain/src/os/bluestore/
  2. BlueStore文档:Ceph官方文档
  3. RocksDB文档:https://rocksdb.org/

文章互动

阅读 --

留言

0 条留言

正在加载留言…