Ceph BlueStore:BlueFS架构介绍 +元数据详解

文章目录

  • 概述
    • BlueStore数据存储
    • 为什么需要BlueFS?
    • BlueFS与RocksDB交互
      • RocksDB数据文件
      • RocksDB数据在BlueFS中存储
  • BlueFS持久化元数据
    • 元数据组织结构
    • 元数据类详解
      • bluefs_extent_t(data)
      • bluefs_fnode_t(file)
      • bluefs_super_t(superblock)
      • bluefs_transaction_t(journal)
    • BlueFS journal机制
      • 举例:创建.sst文件事务机制
      • journal回放
  • BlueFS内存元数据
    • 元数据缓存机制
    • 元数据类详解(BlueFS)
    • 内存元数据加载
  • 参考文献

概述

BlueStore数据存储

下图是BlueStore的基本架构,可以看到如下关键信息。

  • 数据
    • 数据直接存储在HDD裸盘上。
  • 元数据
    • 元数据以kv形式存储在RocksDB中。
    • RocksDB的数据通过BlueRocksEnv接口传递给BlueFS。
    • BlueFS将数据存储到SSD裸盘上。
      Ceph BlueStore:BlueFS架构介绍 +元数据详解_第1张图片

为什么需要BlueFS?

RocksDB不支持直接将数据存储到SSD裸盘上,因此需要一个fs的模块。

BlueFS与RocksDB交互

RocksDB数据文件

  • .sst文件:rocksDB中存放数据的file。
  • CURRENT:保存了最新的manifest文件信息。
  • IDENTITY:db的uuid。
  • MANIFEST:保存了rocksDB中LSM结构的变化日志。
  • LOCK:db锁持有信息,同时只允许有一个进程打开db。
  • OPEIONS:db的配置属性。
  • .log:rocksdb数据写时记录的wal数据。

上述数据文件只有CURRENT是修改写,但RocksDB是通过先将数据写到新的临时文件,然后将新文件重命名为CURRENT文件实现修改。因此RocksDB中所有的数据文件都是以追加写的方式完成的。

RocksDB数据在BlueFS中存储

针对rocksDB中的数据文件分类,BlueFS将数据分为wal、db、slow三种类型。

  • wal
    • 使用最快速的存储介质
    • 存储.log文件,用于加速写入。
  • db
    • 使用SSD存储介质
    • 存储.sst、CURRENT、IDENTITY、MANIFEST、LOCK、OPEIONS文件,这些文件通常在compaction时进行读写,需要保证一定的性能
    • 当wal写满后本来写wal的数据会存储在db中。
  • slow
    • 使用低速存储介质
    • 当db写满后,本来在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持久化元数据

元数据组织结构

下图是BlueFS的元数据组织,包括如下关键元数据:

  • superblock:记录文件系统全局信息。
  • journal:BlueFS的元数据不像传统文件系统一样,用特定的数据结构和布局存放,而是通过将所有的操作记录到journal中,然后在加载的时候逐条回放journal中的记录,从而将元数据file加载到内存。
  • file:真实数据的data的元数据。
  • data:真实数据。
    Ceph BlueStore:BlueFS架构介绍 +元数据详解_第2张图片

元数据类详解

bluefs_extent_t(data)

直接看类的实现即可理解。

class bluefs_extent_t {
  uint64_t offset; // BlockDevice的物理地址
  uint32_t length; // 数据长度
  uint8_t bdev; // 属于哪个blockdevice
};

bluefs_fnode_t(file)

直接看类的实现即可理解。

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_super_t(superblock)

记录文件系统的全局信息,也是文件系统加载的入口,其位置固定存放于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的文件
};

Ceph BlueStore:BlueFS架构介绍 +元数据详解_第3张图片

bluefs_transaction_t(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; // 编码后的事务条目,可包含多条记录,每条记录由相关的操作码和操作所涉及的相关数据组成。
};

Ceph BlueStore:BlueFS架构介绍 +元数据详解_第4张图片

BlueFS journal机制

BlueFS的元数据不像传统文件系统一样,用特定的数据结构和布局存放,而是通过将所有的操作记录到journal中,然后在加载的时候逐条回放journal中的记录,从而将元数据加载到内存。

举例:创建.sst文件事务机制

在目录data下新建一个文件1.sst,然后向其中写入数据,将此作为一个事务提交,那么这个事务中最终会按顺序生成三条记录:

  • 生成OP_FILE_UPDATE记录,包含记录类型和新生成的fnode结构;
  • 生成OP_DIR_LINK记录,包含记录类型、目录名data、文件名1.sst以及文件fnode中的ino编号;
  • 生成OP_FILE_UPDATE记录,包含记录类型和新生成的fnode结构;写入操作由于有分配新的空间并且需要更改操作时间,需要更新fnode结构,因此会新生成一条OP_FILE_UPDATE记录。

journal回放

日志回放会根据记录的顺序逐条解析,在上面例子中首先会

  • 将第一条记录中的fnode解析到内存
  • 第二步会在dirmap和filemap中建立文件和目录的映射
  • 第三步会用新的fnode结构替换掉第一步中的fnode。

BlueFS内存元数据

元数据缓存机制

BlueFS的metadata全部加载在内存当中,主要包含superblock、目录和文件的集合、文件跟目录的映射关系以及文件到物理地址的映射关系;当下次文件系统加载时,将日志中的记录逐条回放到内存,从而还原出metadata。

元数据类详解(BlueFS)

元数据如下所示,包括:

  • superblock:见上一章节。
  • dir_map:记录BlueFS中的所有目录的目录名到实际目录结构的映射,目录为扁平结构,不存在隶属关系,例如目录/a/b与目录/a在同一级,/a/b即为目录名
  • file_map:记录目录下的所有文件的文件名到实际文件结构的映射,每个文件结构中包含该文件的fnode结构。
    Ceph BlueStore:BlueFS架构介绍 +元数据详解_第5张图片
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
		......
};

内存元数据加载

  • BlueFS在加载时,会根据日志中记录的内容,在metadata的dir_map和file_map中添加、删除或修改相应的条目;
  • 在BlueFS的使用过程中,也会不断变更此映射结构;
  • BlueFS在定位一个具体的文件时会在内存中经过两次查找
    • 第一次通过dir_map找到文件所在的最底层文件夹,
    • 第二次通过该文件夹下的file_map找到对应的文件。
  • 此外内存中还会记录一个dirty_files的列表,记录发生更改,但还未sync的文件集合(因为BlueFS的更新操作只有在调用sync时才会真正更改)。

内存元数据加载详见Ceph BlueStore:BlueFS元数据恢复replay流程详解 + 元数据encode/decode一文。

参考文献

http://blog.wjin.org/posts/ceph-bluestore-bluefs.html
https://zhuanlan.zhihu.com/p/46362124

你可能感兴趣的:(Ceph,BlueStore,分布式,云计算,linux,大数据,数据库)