下图是BlueStore的基本架构,可以看到如下关键信息。
RocksDB不支持直接将数据存储到SSD裸盘上,因此需要一个fs的模块。
上述数据文件只有CURRENT是修改写,但RocksDB是通过先将数据写到新的临时文件,然后将新文件重命名为CURRENT文件实现修改。因此RocksDB中所有的数据文件都是以追加写的方式完成的。
针对rocksDB中的数据文件分类,BlueFS将数据分为wal、db、slow三种类型。
下面是在真实设备中导出的BlueFS中数据,可以看出其存储分布
[root@localhost ceph-node]# ceph-bluestore-tool bluefs-export --path /var/lib/ceph/osd/ceph-0/ --out-dir /data/osd_ceph_0
inferring bluefs devices from bluestore path
slot 1 /var/lib/ceph/osd/ceph-0/block -> /var/lib/ceph/osd/ceph-0/block
db/
db/000021.sst
db/000023.sst
db/CURRENT
db/IDENTITY
db/LOCK
db/MANIFEST-000024
db/OPTIONS-000020
db/OPTIONS-000027
db.slow/
db.wal/
db.wal/000025.log
下图是BlueFS的元数据组织,包括如下关键元数据:
直接看类的实现即可理解。
class bluefs_extent_t {
uint64_t offset; // BlockDevice的物理地址
uint32_t length; // 数据长度
uint8_t bdev; // 属于哪个blockdevice
};
直接看类的实现即可理解。
struct bluefs_fnode_t {
uint64_t ino; // inode编号
uint64_t size; // 文件大小
utime_t mtime; // 修改时间
uint8_t prefer_bdev; // 优先使用哪个block device
mempool::bluefs::vector<bluefs_extent_t> extents; // 文件对应的磁盘空间
uint64_t allocated; // 文件实际占用的空间大小,extents的length之和。应该是小于等于size
};
记录文件系统的全局信息,也是文件系统加载的入口,其位置固定存放于BlueFS的第二个block。
其中的log_fnode为journal的文件。
struct bluefs_super_t {
uuid_d uuid; // uuid
uuid_d osd_uuid; // osd uuid
uint64_t version; // 版本号,当且仅当日志进行压缩的时候递增,可通过版本号判断BlueFS进行日志压缩的次数
uint32_t block_size; // BlueFS中的块大小,即每次读写的最小单位
bluefs_fnode_t log_fnode; // 记录文件系统journal的文件
};
journal实际上是一种特殊的文件,其fnode记录在superblock中,而其他文件的fnode作为日志内容记录在journal文件中。
journal是由一条条的事务transaction组成。
struct bluefs_transaction_t {
typedef enum {
OP_NONE = 0,
OP_INIT, ///< initial (empty) file system marker
// 给文件分配和释放空间
OP_ALLOC_ADD, ///< add extent to available block storage (extent)
OP_ALLOC_RM, ///< remove extent from availabe block storage (extent)
// 创建和删除目录项
OP_DIR_LINK, ///< (re)set a dir entry (dirname, filename, ino)
OP_DIR_UNLINK, ///< remove a dir entry (dirname, filename)
// 创建和删除目录
OP_DIR_CREATE, ///< create a dir (dirname)
OP_DIR_REMOVE, ///< remove a dir (dirname)
// 文件更新
OP_FILE_UPDATE, ///< set/update file metadata (file)
OP_FILE_REMOVE, ///< remove file (ino)
// bluefs日志文件的compaction操作
OP_JUMP, ///< jump the seq # and offset
OP_JUMP_SEQ, ///< jump the seq #
} op_t;
uuid_d uuid; // 表示事务归属的bluefs对应的uuid
uint64_t seq; // 事务的全局唯一序列号
bufferlist op_bl; // 编码后的事务条目,可包含多条记录,每条记录由相关的操作码和操作所涉及的相关数据组成。
};
BlueFS的元数据不像传统文件系统一样,用特定的数据结构和布局存放,而是通过将所有的操作记录到journal中,然后在加载的时候逐条回放journal中的记录,从而将元数据加载到内存。
在目录data下新建一个文件1.sst,然后向其中写入数据,将此作为一个事务提交,那么这个事务中最终会按顺序生成三条记录:
日志回放会根据记录的顺序逐条解析,在上面例子中首先会
BlueFS的metadata全部加载在内存当中,主要包含superblock、目录和文件的集合、文件跟目录的映射关系以及文件到物理地址的映射关系;当下次文件系统加载时,将日志中的记录逐条回放到内存,从而还原出metadata。
元数据如下所示,包括:
class BlueFS {
public:
// 文件系统支持不同种类的块设备
static constexpr unsigned MAX_BDEV = 5;
static constexpr unsigned BDEV_WAL = 0;
static constexpr unsigned BDEV_DB = 1;
static constexpr unsigned BDEV_SLOW = 2;
static constexpr unsigned BDEV_NEWWAL = 3;
static constexpr unsigned BDEV_NEWDB = 4;
enum {
WRITER_UNKNOWN,
WRITER_WAL, // RocksDB的log文件
WRITER_SST, // RocksDB的sst文件
};
// 文件
struct File : public RefCountedObject {
bluefs_fnode_t fnode; // 文件inode
int refs; // 引用计数
uint64_t dirty_seq; // dirty序列号
bool locked;
bool deleted;
boost::intrusive::list_member_hook<> dirty_item;
// 读写计数
std::atomic_int num_readers, num_writers;
std::atomic_int num_reading;
};
// 目录
struct Dir : public RefCountedObject {
mempool::bluefs::map<string,FileRef> file_map; // 目录包含的文件
};
// 文件系统的内存映像
mempool::bluefs::map<string, DirRef> dir_map; // 所有的目录
mempool::bluefs::unordered_map<uint64_t,FileRef> file_map; // 所有的文件
map<uint64_t, dirty_file_list_t> dirty_files; // 脏文件,根据序列号排列
// 文件系统超级块和日志
bluefs_super_t super;
bluefs_transaction_t log_t;
// 结构体FileWriter/FileReader/FileLock,用来对一个文件进行读写和加锁
......
vector<BlockDevice*> bdev; // BlueFS能够使用的所有BlockDevice,包括wal/db/slow
vector<IOContext*> ioc; // bdev对应的IOContext
vector<interval_set<uint64_t> > block_all; // bdev对应的磁盘空间
vector<Allocator*> alloc; // bdev对应的allocator
......
};
内存元数据加载详见Ceph BlueStore:BlueFS元数据恢复replay流程详解 + 元数据encode/decode一文。
http://blog.wjin.org/posts/ceph-bluestore-bluefs.html
https://zhuanlan.zhihu.com/p/46362124