KeyValueStore 是 Ceph 支持的另一个存储引擎(第一个是FileStore),它是在 Emporer 版本中Add LevelDB support to ceph cluster backend store Design Summit 上由本人提出并实现了原型系统,在 Firely 版本中实现了与 ObjectStore 的对接。目前已经合并到 Ceph 的 Master 上。
KeyValueStore 相对于 FileStore 是一个轻量级实现,目标是利用其不同 Backend 提供的能力来为 Ceph 的不同应用场景服务。如目前的默认 engine 是 LevelDB,期望来提供高性能的写性能。
主要数据结构
KeyValueStore主要由三部分组成,一个是继承ObjectStore 的KeyValueStore 类,另一个是GenericObjectMap(类似于FileStore 的DBObjectMap),最后一个是继承GenericObjectMap 的StripObjectMap。GenericObjectMap 是主要用来访问后端Engine 的实现,它的作用有点类似VFS,而Engine 就是各种不同的FileSystem,它抽象出一些基本的方法(read/write)和一些高级接口(rename/clone)等等,首先最初开始设计GenericObjectMap的时候是打算直接利用已经存在的FileStore 的DBObjectMap,但是在一定的调查后发现DBObjectMap 缺少一定的扩展性,很难在不破坏现有接口的前提下来实现,因此最后与Sage 商定直接实现新的ObjectMap。那么什么是ObjectMap,正如在上篇FileStore 文章中所述,ObjectMap 是利用K/V 接口实现的一个多层次Map,目的是让OSD 最重要的Object 具备一个独立和高效查找的KV 空间,同时这个空间还能使用no-copy 的clone。
GenericObjectMap 在提供了一个面向Object 的通用KV 空间后,StripObjectMap 继承了GenericObjectMap 实现了对Object Data 的封装,ObjectStore 有三种类型的数据: Data, attr 和Omap,后两者都是单一的KV 实现,可以直接利用GenericObjectMap 的原生接口实现,但是Data 的接口是类似于Posix 需要具备Parity Write 的能力,因此简单的将一个Object 的Data 作为一个键值对是不合适的,需要做一个Strip 的工作,将一个Object 的Data 根据一定宽度划分成多个键值对,这个工作就是由StripObjectMap 来完成。
最后KeyValueStore 类利用StripObjectMap 来完成了对ObjectStore 的方法实现。
主要 IO 路径
与FileStore 的实现类似,KeyValueStore 也会产生一个消息队列,所有来自上层PG 产生的IO 请求都会先放入这个队列,然后会有多个KeyValueStore 线程作为队列的消费者获取请求进行处理,因为PG 天生的隔离性,目前KeyValueStore 是利用PG 作为一个隔离单元,同一时间只有一个线程处理同一个PG 的请求。KeyValueStore 线程针对每一个请求会产生一个缓冲空间,因为一个请求作为一个事务会包含多个原子操作,为了保证事务的原子性和隔离性,每一个请求在中间阶段并不能写入到持久层,只能产生一些操作序列,而可能的副作用就需要被缓冲空间保存起来作为后续操作的上下文。最后KeyValueStore 线程会提交这个请求来完成这次事务。</font
KeyValueStore.h中相关定义如下
struct Op { utime_t start; uint64_t op; list<Transaction*> tls; Context *ondisk, *onreadable, *onreadable_sync; uint64_t ops, bytes; TrackedOpRef osd_op; }; struct OpWQ : public ThreadPool::WorkQueue<OpSequencer>
消息处理请求控制
unsigned KeyValueStore::_do_transaction(Transaction& transaction, BufferTransaction &t, ThreadPool::TPHandle *handle)