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; }
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; }
|
关键逻辑:
- 如果object-map未启用: 返回
true(保守策略)
- 如果object-map无效: 返回
true(保守策略)
- 如果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(); if (read_snap_id == image_ctx->snap_id && image_ctx->object_map != nullptr && !image_ctx->object_map->object_may_exist(this->m_object_no)) { image_ctx->asio_engine->post([this]() { read_parent(); }); return; } image_locker.unlock();
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; } else { m_object_may_exist = image_ctx->object_map->object_may_exist( this->m_object_no); } }
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; }
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为空时,意味着:
- 未启用object-map功能:
RBD_FEATURE_OBJECT_MAP未设置
- object-map对象不存在: RADOS中不存在
rbd_object_map.<image_id>对象
- 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写入请求(需要创建对象) ✓
|
结论
object-map为空时:
object_may_exist()返回true(保守策略)
- 仍然会发起RADOS请求
- 不会跳过任何I/O操作
object-map有效时:
- 如果对象标记为不存在,可能跳过RADOS请求
- 读取:直接读取父镜像
- 写入:no-op操作可能直接完成
优化前提:
- object-map必须存在且有效
- 必须启用
RBD_FEATURE_OBJECT_MAP功能
- object-map不能标记为无效
设计理念:
- 保守策略: 当不确定时,假设对象存在,避免跳过实际存在的对象
- 性能优化: 当确定对象不存在时,跳过不必要的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年
正在加载留言…