RBD对象映射优化机制分析

RBD对象映射优化机制分析

问题

问题: 当RBD客户端的object-map为空时,是否不会向RADOS发起请求?

答案: 不会跳过。当object-map为空或无效时,RBD采用保守策略,仍然会发起RADOS请求。只有当object-map存在且明确标记对象为不存在时,才会跳过RADOS请求。


核心机制

1. object_may_exist() 方法

位置: librbd/ObjectMap.cc:154-177

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
template <typename I>
bool ObjectMap<I>::object_may_exist(uint64_t object_no) const
{
ceph_assert(ceph_mutex_is_locked(m_image_ctx.image_lock));

// 如果对象映射被禁用或无效,回退到默认逻辑(保守策略)
if (!m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP,
m_image_ctx.image_lock)) {
return true; // 保守策略:假设对象可能存在
}

bool flags_set;
int r = m_image_ctx.test_flags(m_image_ctx.snap_id,
RBD_FLAG_OBJECT_MAP_INVALID,
m_image_ctx.image_lock, &flags_set);
if (r < 0 || flags_set) {
return true; // 如果object-map无效,返回true(保守策略)
}

uint8_t state = (*this)[object_no];
bool exists = (state != OBJECT_NONEXISTENT);
ldout(m_image_ctx.cct, 20) << "object_no=" << object_no << " r=" << exists
<< dendl;
return exists;
}

关键逻辑:

  1. 如果object-map未启用: 返回true(保守策略)
  2. 如果object-map无效: 返回true(保守策略)
  3. 如果object-map有效: 根据对象状态返回
    • OBJECT_NONEXISTENT → 返回false
    • 其他状态 → 返回true

2. 读取请求优化

位置: librbd/io/ObjectRequest.cc:223-256

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
template <typename I>
void ObjectReadRequest<I>::read_object() {
I *image_ctx = this->m_ictx;

std::shared_lock image_locker{image_ctx->image_lock};
auto read_snap_id = this->m_io_context->get_read_snap();

// 关键优化点:如果object-map标记对象不存在,直接读取父镜像
if (read_snap_id == image_ctx->snap_id &&
image_ctx->object_map != nullptr &&
!image_ctx->object_map->object_may_exist(this->m_object_no)) {
// 跳过RADOS读取,直接尝试从父镜像读取
image_ctx->asio_engine->post([this]() { read_parent(); });
return;
}
image_locker.unlock();

// 如果object-map为空或标记对象存在,正常发起RADOS读取请求
neorados::ReadOp read_op;
// ... 构建读取操作
image_ctx->rados_api.execute(
{data_object_name(this->m_ictx, this->m_object_no)},
*this->m_io_context, std::move(read_op), nullptr,
// ...
);
}

优化逻辑:

  • 条件1: read_snap_id == image_ctx->snap_id (读取当前快照)
  • 条件2: image_ctx->object_map != nullptr (object-map存在)
  • 条件3: !object_may_exist() (对象不存在)

当所有条件满足时: 跳过RADOS读取请求,直接尝试从父镜像读取

当object-map为空时: object_may_exist()返回true不会跳过RADOS请求


3. 写入请求优化

位置: librbd/io/ObjectRequest.cc:412-437

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
template <typename I>
void AbstractObjectWriteRequest<I>::send() {
I *image_ctx = this->m_ictx;

{
std::shared_lock image_lock{image_ctx->image_lock};
if (image_ctx->object_map == nullptr) {
m_object_may_exist = true; // object-map为空,假设对象存在
} else {
m_object_may_exist = image_ctx->object_map->object_may_exist(
this->m_object_no);
}
}

// 优化:如果对象不存在且操作是no-op,直接完成
if (!m_object_may_exist && is_no_op_for_nonexistent_object()) {
ldout(image_ctx->cct, 20) << "skipping no-op on nonexistent object"
<< dendl;
this->async_finish(0);
return; // 跳过RADOS写入请求
}

pre_write_object_map_update();
}

优化逻辑:

  • 如果object-map为空: m_object_may_exist = true不会跳过写入请求
  • 如果object-map存在且对象不存在:
    • 对于no-op操作(如全零写入、discard等),可能跳过写入
    • 对于实际写入操作,仍然会发起RADOS请求(需要创建对象)

对象映射状态

对象状态定义

1
2
3
4
5
6
enum {
OBJECT_NONEXISTENT = 0, // 对象不存在
OBJECT_EXISTS = 1, // 对象存在
OBJECT_PENDING = 2, // 对象待定(正在创建)
OBJECT_EXISTS_CLEAN = 3 // 对象存在且干净(未修改)
};

object-map为空的情况

当object-map为空时,意味着:

  1. 未启用object-map功能: RBD_FEATURE_OBJECT_MAP未设置
  2. object-map对象不存在: RADOS中不存在rbd_object_map.<image_id>对象
  3. object-map无效: RBD_FLAG_OBJECT_MAP_INVALID标志被设置

在这些情况下,object_may_exist()都会返回true,采用保守策略。


优化效果

1. 读取优化

场景: 读取稀疏镜像(大部分对象不存在)

优化前:

1
读取对象 → RADOS读取请求 → 返回ENOENT → 读取父镜像

优化后 (object-map有效且标记对象不存在):

1
读取对象 → 检查object-map → 跳过RADOS请求 → 直接读取父镜像

性能提升: 减少不必要的RADOS请求,降低延迟

2. 写入优化

场景: 对不存在对象执行no-op操作(如全零写入、discard)

优化前:

1
写入对象 → RADOS写入请求 → 创建对象 → 执行操作

优化后 (object-map有效且标记对象不存在):

1
写入对象 → 检查object-map → 判断为no-op → 直接完成(跳过RADOS请求)

性能提升: 避免创建不必要的对象


代码流程总结

读取请求流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ObjectReadRequest::read_object()

├─→ 检查object-map
│ │
│ ├─→ object-map为空/无效
│ │ └─→ object_may_exist() = true
│ │ └─→ 发起RADOS读取请求 ✓
│ │
│ └─→ object-map有效
│ │
│ ├─→ object_may_exist() = true (对象存在)
│ │ └─→ 发起RADOS读取请求 ✓
│ │
│ └─→ object_may_exist() = false (对象不存在)
│ └─→ 跳过RADOS请求,直接读取父镜像 ✗

写入请求流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
AbstractObjectWriteRequest::send()

├─→ 检查object-map
│ │
│ ├─→ object-map为空
│ │ └─→ m_object_may_exist = true
│ │ └─→ 发起RADOS写入请求 ✓
│ │
│ └─→ object-map有效
│ │
│ ├─→ object_may_exist() = true
│ │ └─→ 发起RADOS写入请求 ✓
│ │
│ └─→ object_may_exist() = false
│ │
│ ├─→ is_no_op_for_nonexistent_object() = true
│ │ └─→ 跳过RADOS请求,直接完成 ✗
│ │
│ └─→ is_no_op_for_nonexistent_object() = false
│ └─→ 发起RADOS写入请求(需要创建对象) ✓

结论

  1. object-map为空时:

    • object_may_exist()返回true(保守策略)
    • 仍然会发起RADOS请求
    • 不会跳过任何I/O操作
  2. object-map有效时:

    • 如果对象标记为不存在,可能跳过RADOS请求
    • 读取:直接读取父镜像
    • 写入:no-op操作可能直接完成
  3. 优化前提:

    • object-map必须存在且有效
    • 必须启用RBD_FEATURE_OBJECT_MAP功能
    • object-map不能标记为无效
  4. 设计理念:

    • 保守策略: 当不确定时,假设对象存在,避免跳过实际存在的对象
    • 性能优化: 当确定对象不存在时,跳过不必要的RADOS请求

相关代码位置

  • ObjectMap::object_may_exist(): librbd/ObjectMap.cc:154-177
  • 读取优化: librbd/io/ObjectRequest.cc:223-256
  • 写入优化: librbd/io/ObjectRequest.cc:412-437
  • ObjectMap定义: librbd/ObjectMap.h

文档版本: 1.0
最后更新: 2024年

文章互动

阅读 --

留言

0 条留言

正在加载留言…