目录
- 未完待续
- 前言
- 组成
- 前期准备工作
- 基础概念
- 对象
- Pextent
- extent
- Blob
- Node
- 线程
- 事务
- 磁盘的抽象与分配
- 位图法
- 分层位图
- 上电流程
- 写流程
- 读流程
- 参考资料
未完待续
前言
BlueStore是什么?
Ceph是一个统一的分布式存储系统。BlueStore是Ceph的存储引擎。
它的作用是什么?
存储引擎的作用还能是什么?把Ceph上层交给他的数据安稳的存储在磁盘上,需要的时候再读出来或者删除。这就是他的作用。
组成
如上图,BlueStore由下面接模块构成:
- RocksDB:为BlueStore提供kv存储。
- BlueFs:RocksDB是一个持久化的kv存储系统,但是Rocksdb本身又不能直接操作磁盘读写数据,在这个情况下,BlueFS就承担了这个职责,作为一个文件系统,为RocksDB提供了磁盘上文件的读写能力。
- BlueRocksEnv:在RocksDB与BlueFS间提供一个桥梁。
- Allocator:磁盘的空间管理器。
- BlockDevice:以HDD或者SSD为代表的裸盘
从上图我们可以看到,对于用户数据本身是由BlueStore直接操作裸盘进行读写,但是对于用户数据的元数据,是通过RocksDB进行读写的。
前期准备工作
在开始研究BlueStore的工作流程前,咱们先了解一下必要的知识。
基础概念
对象
咱们尽量从一个实际的例子进行说明。假定我需要存储《流浪地球2》这个电影,电影本身是20个GB。在Ceph里,会把这个20GB的文件切分成2MB(或者4BM,参数,可配)的一个个文件块,我们把这个文件块就叫做对象。而在BlueStore这一层,是感知不到20GB的《浏览地球2》,它只能感知到2MB的对象块。
Pextent
struct bluestore_pextent_t {
// 磁盘上的物理偏移
uint64_t offset = 0;
// 数据段的长度
uint32_t length = 0;
}
Pextent就是代表真实的物理磁盘上的一个区间。offset就是相对于磁盘上起始位置的偏移量,length的单位就是字节。
extent
struct Extent {
// 对象内逻辑偏移,不需要块对齐。
uint32_t logical_offset = 0;
// 逻辑段长度,不需要块对齐。
uint32_t length = 0;
// 当logical_offset是块对齐时,blob_offset始终为0;
// 不是块对齐时,将逻辑段内的数据通过Blob映射到磁盘物理段会产生物理段内的偏移称为blob_offset。
uint32_t blob_offset = 0;
// 多个物理块
BlobRef blob;
}
对于那个2MB的对象,在BlueStore里,也会对他进行逻辑切分。假定把2MB的对象切分成3块,第一块512KB,第二块512KB,第二块1024KB,那么
第一个Extent的logical_offset 是0,length是512x1024
第二个Extent的logical_offset是1x512x1024,length是512x1024
第三个Extent的logical_offset是2x512x1024,length是1024x1024
上面还有blob_offset 的概念,牵扯到块对其,大家暂时忽略。
Blob
在上面的Extent里面还有一个Blob是什么意思?
就上面那个例子,在真实的物理磁盘上,不一定有一块连续的空闲的1MB的空间。所以BlueStore会把物理上不在一块的磁盘空间逻辑上放到一块。
struct Blob {
// reference count
std::atomic_int nref = {0};
mutable vector<bluestore_pextent_t> blob;
}
一个512KB的Blob,内部可能是磁盘上3个并不相连的空间段拼起来的。
Node
// 内存数据结构
struct Onode {
// reference count
std::atomic_int nref;
// onode 对应的PG
Collection *c;
// onode磁盘数据结构
bluestore_onode_t onode;
// 有序的Extent逻辑空间集合,持久化在RocksDB。lexetnt--->blob
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;
......
}
上面的Onode 描述一个对象(就是那个2M的对象)的信息。
上面的ExtentMap,大家猜一下也能知道,假定一个对象被分成了3个Extent,那都是哪3个Extent呢,对象本身得知道,自己的数据逻辑上分成了哪几块么。
线程
事务
磁盘的抽象与分配
位图法
BlueStore面对的一块完完全全的裸盘。那么第一个问题就是,BlueStore怎么知道磁盘上那一块用了,哪一块没有用呢。
首先对磁盘进行分块,假定就按照4KB分块。
其实一个思路就是先把磁盘上的块进行编号,然后对于每个块使用一个比特的0代表已经被使用,1代表没有使用。这样以来我只要使用原始硬盘空间的32768分之1的空间就能描述磁盘里每个区域的使用情况了(410248=32768)
那么一个100GB的磁盘,大概只需要32MB的内存比特流就能描述。
以上,就叫做位图法。
分层位图
以上面的例子来说,假如我需要在1块100GB的磁盘上分配一个4KB的块,最最极端情况下,我就得扫描32MB的数据,才能知道哪个块没有被使用。这个是不可接受的。
大家知道跳表么?
还是那个100GB的空间,我假定L0层就是32MB的内存块。
我在L1层申请64KB的内存块,L1的一个比特对应L0层的512个比特。
假定L0的前512个比特都是0(代表磁盘上前5124KB的一段区域都已经被分配了)那么我L1上第1个比特就是0。
假定L0的前512个比特不都是0(代表磁盘上5124KB的一段区域还没有被完全分配完)那么我L1上第1个比特就是1。
如果磁盘空间更大了,那么L1上面还可以加一个L2。
这样以来,分配空闲磁盘的时间复杂度就可控了。
上电流程
写流程
读流程
参考资料
https://zhuanlan.zhihu.com/p/68067068
https://zhuanlan.zhihu.com/p/92397191