Ceph数据流分析

Ceph数据流分析

OSD类

OSD和OSDService是核心类,他们直接在顶层负责一个OSD节点的工作,从客户端的得到的消息,就是先到达OSD类中,通过OSD类的处理,在调用PrimaryLogPG类进行处理。该类中,在读写流程中的主要工作是消息(Message)封装为 RequestOp,检查epoch (版本)是否需要更新,并获取PG句柄,并做PG相关的检查,最后将请求加入队列。

PrimaryLogPG类

该类继承自PG类,PGBackend::Listener(该类是一个抽象类)类PG类处理相关状态的维护,以及实现PG层面的功能,核心功能是用boost库的statechart状态机来做PG状态转换。它实现了PG内的数据读写等功能。

PGBackend类

该类主要功能是将请求数据通过事务的形式同步到一个PG的其它从OSD上(注意:主OSD的操作PrimaryLogPG来完成)。他有两个子类,分别是 ReplicatedBackend(副本)和ECBackend(EC),对应着PG的的两种类型的实现。

OSD读写函数调用流程

OSD::ms_fast_dispatch
----> OSD::dispatch_session_waiting
    ----> OSD::enqueue_op

OSD::dequeue_op
---->  PrimaryLogPG::do_request
    ----> PrimaryLogPG::do_op
        ----> PrimaryLogPG::find_object_context
            ---->PrimaryLogPG::get_object_context
        ----> PrimaryLogPG::execute_ctx
            ----> PrimaryLogPG::prepare_transaction
            ----> PrimaryLogPG::OpContext::start_async_reads
            ----> PrimaryLogPG::complete_read_ctx
            ----> PrimaryLogPG::issue_repop
            	----> ReplicatedBackend::submit_transaction
            		----> ReplicatedBackend::issue_op
            			----> OSDService::send_message_osd_cluster 
            		----> ReplicatedBackend::queue_transactions
            			----> BlueStore::queue_transactions 

img

个人理解:该图中ReplicatePG已被替换为PrimaryLogPG,FileStore已被Bluestore替换
场景:client需要向cluster集群中写入数据
1:client使用crush算法确认需要交流的主OSD
2:client和主OSD建立联系,发送数据
3:主OSD这边server监听,收到请求后通过ms_fast_dispatch进行处理
4:主OSD会用PrimaryLogPG进行处理,假设此时使用的是副本形式
5:主OSD会用ReplicatedBackend与从OSDs进行连接,将写请求发送到从OSDs(issue_op)
6:主OSD使用bluestore进行数据存储

img

Allocator类

实现对裸设备的管理,直接将数据保存到设备上。用来委派具体哪个实际存储块用来存储当前的object数据;目前采用bitmap的方式来实现allocator,同时采用层级索引来存储多种状态,这种方式对内存的消耗相对较小,平均1TB磁盘需要大概35M左右的ram空间。
img

新版本BitMap分配器以Tree-Like的方式组织数据结构,整体分为L0、L1、L2三层。每一层都包含了完整的磁盘空间映射,只不过是slot以及children的粒度不同,这样可以加快查找。

新版本分配器分配空间的大体策略如下:
循环从L2中找到可以分配空间的slot以及children位置。
在L2的slot以及children位置的基础上循环找到L1中可以分配空间的slot以及children位置。
在L1的slot以及children位置的基础上循环找到L0中可以分配空间的slot以及children位置。
在1-3步骤中保存分配空间的结果以及设置每层对应位置分配的标志位。

新版本分配器整体架构设计有以下几点优势:
Allocator避免在内存中使用指针和树形结构,使用vector连续的内存空间。
Allocator充分利用64位机器CPU缓存的特性,最大程序的提高性能。
Allocator操作的单元是64 bit,而不是在单个bit上操作。
Allocator使用3级树状结构,可以更快的查找空闲空间。
Allocator在初始化时L0、L1、L2三级BitMap就占用了固定的内存大小。
Allocator可以支持并发的分配空闲,锁定L2的children(bit)即可,暂未实现。
总体来说新版本BitMap分配器的优势在于使用连续的内存空间从而尽可能更多的命中CPU Cache以提高分配器性能。

分配流程图

img
最终分配的结果存储在interval_vector_t结构体里面,实际上就是extent_vector,因为分配的磁盘空间不一定是完全连续的,所以会有多个extent,而在往extent_vector 插入extent的时候会合并相邻的extent为一个extent。如果max_alloc_size设置了,且单个连续的分配大小超过了max_alloc_size,那么extent的length最大为max_alloc_size ,同时这次分配结果也会拆分会多个extent。

BlueRocksEnv类

这是RocksDB与BlueFS交互的接口;RocksDB提供了文件操作的接口EnvWrapper,用户可以通过继承实现该接口来自定义底层的读写操作,BlueRocksEnv就是继承自EnvWrapper实现对BlueFS的读写。
class BlueRocksEnv : public rocksdb::EnvWrapper
可以看出来BlueRocksEnv本质上是对rocksdb进行操作

RocksDB类

rocksdb是facebook基于leveldb开发的一款kv数据库,BlueStore将元数据全部存放至RocksDB中,这些元数据包括存储预写式日志、数据对象元数据、Ceph的omap数据信息、以及分配器的元数据 。
class BlueRocksEnv : public rocksdb::EnvWrapper

BlueFS

BlueFS是BlueStore针对RocksDB开发的轻量级文件系统,用于存放RocksDB产生的.sst和.log等文件。

BlueStore读写函数调用流程

从上面的分析,我们已经了解了osd是怎么调用到bluestore接口的,下面我们将从bluestore继续向下研究。

BlueStore::queue_transactions
	----> BlueStore::_txc_add_transaction
		----> BlueStore::_write
			----> BlueStore::_do_write
				----> BlueStore::_do_write_data
					----> BlueStore::_do_write_small
					  BlueStore::_do_write_big
				----> BlueStore::_do_alloc_write
					----> bluestore_blob_t::allocated
					----> BlockDevice::aio_write
					

BlueStore源码分析之对象IO_Python_04

BlueStore相关解释

Collection:PG在内存的数据结构。
bluestore_cnode_t:PG在磁盘的数据结构。
Onode:对象在内存的数据结构。
bluestore_onode_t:对象在磁盘的数据结构。
Extent:一段对象逻辑空间(lextent)。
extent_map_t:一个对象包含多段逻辑空间。
bluestore_pextent_t:一段连续磁盘物理空间。
bluestore_blob_t:一片不一定连续的磁盘物理空间,包含多段pextent。
Blob:包含一个bluestore_blob_t、引用计数、共享blob等信息。

https://blog.51cto.com/wendashuai/2500499

BlueStore源码分析之对象IO_onode

onode

struct Onode {  // 对象在内存中的数据结构
    bluestore_onode_t onode;  /// 对象在磁盘中的数据结构
    
    ExtentMap extent_map;
    ......
    }
    
// 磁盘数据结构 onode: per-object metadata
struct bluestore_onode_t {
    // 逻辑ID,单个BlueStore内部唯一。
    uint64_t nid = 0;

    // 对象大小
    uint64_t size = 0;

    // 对象扩展属性
    map<mempool::bluestore_cache_other::string, bufferptr> attrs;
    ......
}

ExtentMap

/// a sharded extent map, mapping offsets to lextents to blobs
struct ExtentMap {
    Onode *onode;
    extent_map_t extent_map;        ///< map of Extents to Blobs 一个对象包含多段逻辑空间
    blob_map_t spanning_blob_map;   ///< blobs that span shards
    ......
    }
    
typedef boost::intrusive::set<Extent> extent_map_t;

// LExtent
struct Extent : public ExtentBase { // 一段对象逻辑空间(lextent)
    MEMPOOL_CLASS_HELPERS();

    uint32_t logical_offset = 0;      ///< logical offset
    uint32_t blob_offset = 0;         ///< blob offset
    uint32_t length = 0;              ///< length
    BlobRef  blob;                    ///< the blob with our data
    ......
	}

Blob

typedef boost::intrusive_ptr<Blob> BlobRef;
  
/// in-memory blob metadata and associated cached buffers (if any)
struct Blob {
    MEMPOOL_CLASS_HELPERS();
    mutable bluestore_blob_t blob;  ///< decoded blob metadata
    ......
    }
    
/// blob: a piece of data on disk
struct bluestore_blob_t {
  PExtentVector extents;              ///< raw data position on device
  uint32_t logical_length = 0;        ///< original length of data stored in the blob
  uint32_t compressed_length = 0;     ///< compressed length if any
  ......
  }

pextent

typedef mempool::bluestore_cache_other::vector<bluestore_pextent_t> PExtentVector;

/// pextent: physical extent
struct bluestore_pextent_t : public bluestore_interval_t<uint64_t, uint32_t>
{
  bluestore_pextent_t() {}
  bluestore_pextent_t(uint64_t o, uint64_t l) : bluestore_interval_t(o, l) {}
  bluestore_pextent_t(const bluestore_interval_t &ext) :
    bluestore_interval_t(ext.offset, ext.length) {}

  DENC(bluestore_pextent_t, v, p) {
    denc_lba(v.offset, p);
    denc_varint_lowz(v.length, p);
  }

  void dump(ceph::Formatter *f) const;
  static void generate_test_instances(std::list<bluestore_pextent_t*>& ls);
};
WRITE_CLASS_DENC(bluestore_pextent_t)
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Ceph中,stripe是一种将数据分片存储的概念。当进行文件读取操作时,需要通过一系列的计算来确定数据所在的具体位置。本文以CephFS的文件读取流程为例进行分析。 首先,在文件读取过程中,Ceph会将文件划分为若干个条带(stripe),每个条带由多个对象分片(stripe unit)组成。条带可以看作是逻辑上连续的一维地址空间。 接下来,通过file_to_extent函数将一维坐标转化为三维坐标(objectset,stripeno,stripepos),来确定具体的位置。其中,objectset表示所在的对象集,stripeno表示条带号,stripepos表示条带内的偏移位置。 具体的计算过程如下:假设需要读取的数据的偏移量为offset,每个对象分片的大小为su(stripe unit),每个条带中包含的对象分片数为stripe_count。 首先,计算块号blockno = offset / su,表示数据所在的分片号。 然后,计算条带号stripeno = blockno / stripe_count,表示数据所在的条带号。 接着,计算条带内偏移stripepos = blockno % stripe_count,表示数据在条带内的偏移位置。 接下来,计算对象集号objectsetno = stripeno / stripes_per_object,表示数据所在的对象集号。 最后,计算对象号objectno = objectsetno * stripe_count + stripepos,表示数据所在的对象号。 通过以上计算,可以确定数据在Ceph中的具体位置,从而完成文件读取操作。 需要注意的是,以上分析是基于Ceph版本10.2.2(jewel)进行的,尽管版本跨度较大,但是该部分代码在12.2.10(luminous)版本中仍然比较稳定,基本的框架没有发生变化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值