ceph 目前是开源社区比较流行的分布式块存储系统,其以良好的架构,稳定性和完善的数据服务功能,获得的了广泛的部署和应用。
目前ceph 最大的问题是其性能相对较差,特别是无法发挥SSD等高速设备的硬件的性能。 Ceph 开源社区一直在优化ceph的性能问题。 目前的结果就是引入了新的object store,这就是最进合并入ceph master的BlueStore.
如上图所示 :
BlueStore整体架构分为四个部分:
BlockDevice 块设备,其对于一个物理块设备(目前也支持用XFS的一个大文件里实现),用来存储实际的数据。其实现在bluestore/BlockDevice.cc 和 bluestore/BlockDevice.h
其主要实现了异步写操作,写操作是通过 操作系统提供的异步io调用。 由于操作系统支持的aio操作只支持directIO,所以对BlockDevice的写操作直接写入磁盘,并且需要按照page对齐。其内部有一个aio_thread 线程,用来检查aio是否完成。其完成后,调用 aio_callback_t aio_callback; 回调函数通知调用方。
目前BlocekDevice的读操作是同步读操作。 有待继续实现异步的读操作。
BlueFs 既然是一个文件系统,就要解决的是元数据的分配管理,其次解决文件空间的分配和管理,以及磁盘空间的分配和管理。
由于BlueFS用来支持RocksDB,所以就不是一个通用的文件系统,它的功能足以支持RocksDB 就可以了。所以它只支持以下功能:
BlueFS中,文件的元数据由 bluefs_fnode_t 保存
struct bluefs_fnode_t { uint64_t ino; //文件的ino uint64_t size; //文件大小 utime_t mtime; // 修改时间 uint8_t prefer_bdev; //优先在该设备上分配空间 vector<bluefs_extent_t> extents; //文件在磁盘上分配的空间 ...... } bluefs_extents_t 代表在磁盘上的分配的extents struct bluefs_extent_t { uint64_t offset; //块设备上的 extent偏移量 uint32_t length; // extent的长度 uint16_t bdev; //对于的块设备 ...... }
目录对应的结构 Dir
struct Dir { map<string,FileRef> file_map; };
对于BlueFS
//BlueFS的元数据cache map<string, Dir*> dir_map; ///< dirname -> Dir ceph::unordered_map<uint64_t,FileRef> file_map; ///< ino -> File
uint64_t size = 1048476 * 128; string fn = get_temp_bdev(size); BlueFS fs; ASSERT_EQ(0, fs.add_block_device(0, fn)); fs.add_block_extent(0, 1048576, size - 1048576); uuid_d fsid; ASSERT_EQ(0, fs.mkfs(fsid)); ASSERT_EQ(0, fs.mount()); { BlueFS::FileWriter *h; ASSERT_EQ(0, fs.mkdir("dir")); ASSERT_EQ(0, fs.open_for_write("dir", "file", &h, false)); bufferlist bl; bl.append("foo"); h->append(bl); bl.append("bar"); h->append(bl); bl.append("baz"); h->append(bl); fs.fsync(h); fs.close_writer(h); } { BlueFS::FileReader *h; ASSERT_EQ(0, fs.open_for_read("dir", "file", &h)); bufferlist bl; BlueFS::FileReaderBuffer buf(4096); ASSERT_EQ(9, fs.read(h, &buf, 0, 1024, &bl, NULL)); ASSERT_EQ(0, strncmp("foobarbaz", bl.c_str(), 9)); delete h; } fs.umount();
上述代码来自test_bluefs.cc的BlueFS的测试代码,展示了 BlueFS文件系统的使用。
如何在BlueFS上实现RocksDB? 对RocksDB, 只要实现 rocksdb::EnvWrapper接口即可。BlueRocksEnv.cc 和 BlueRocksEnv.h 实现了class BlueRocksEnv 来完成此工作。
Bluestore的 所有的元数据都以KV对的形式写入RocksDB中,主要有以下的元数据:
保存对象的元数据信息
const string PREFIX_OBJ = “O”; // object name -> onode_t
需要主要的是,onode 和 enode的信息 都 以PREFIX_OBJ 为前缀,只是同一个对象的onode和 enode的信息的key不同来区分。
保存 overly 信息
const string PREFIX_OVERLAY = “V”; // u64 + offset -> data
保存对象的omap 信息
const string PREFIX_OMAP = “M”; // u64 + keyname -> value
保存 write ahead log 信息
const string PREFIX_WAL = “L”; // id -> wal_transaction_t
保存块设备的空闲extent信息
const string PREFIX_ALLOC = “B”; // u64 offset -> u64 length (freelist)
onode
数据结构onode 保存了BlueStore中一个对象的数据结构,字段和Filestore差不多,这里就不详细介绍。
Enode
数据结构Enode定义了一个共享的extent,也就是这段数据被多个对象共享,一个对象的onode里保存一个enode数据结构,记录该对象被共享的extent.这个shared extent 用来对象基于extent的Copy-on-write 机制
struct Enode : public boost::intrusive::unordered_set_base_hook<> { atomic_t nref; //< reference count uint32_t hash; string key; //< key under PREFIX_OBJ where we are stored EnodeSet *enode_set; //< reference to the containing set bluestore_extent_ref_map_t ref_map; boost::intrusive::unordered_set_member_hook<> map_item;
数据结构bluestore_extent_ref_map_t 定义了shared extent 被哪些对象引用
struct bluestore_extent_ref_map_t { struct record_t { uint32_t length; uint32_t refs; ...... }; ...... map<uint64_t,record_t> ref_map; }
Bluestore的数据写入分为两类:
BlueStore 其实是实现了用户态的一个文件系统。为了实现简单,又使用了RocksDB来实现了BlueStore的 所有的元数据的管理,简化了实现。
优点在于:
目前BlueStore的思路还是比较不错的,但是笔者认为 实现还是比较复杂,笔者简单测试了一下,性能比FileStore 在SSD 上略好一些,但是效果不明显,由于BlueStore代码实现也比较新,还需要有待优化。