ceph本地对象存储omap的实现
本章我们讲述一下ObjectStore中omap的实现。
1. omap的实现
omap的静态类图如下所示。类ObjectMap定义了omap的抽象接口,类DBObjectMap以KeyValueDB本地存储的方式实现了ObjectMap接口。
目前实现KeyValueDB的本地存储分别为: Facebook开源的levelDB存储,对应类LevelDBStore;Google开源的RocksDB存储,对应类RocksDBStore实现;希捷kinetic客户端存储,对应的类KineticStore。默认采用的是LevelDB实现。
如下表7-1所示,LevelDB是一个key-value存储系统,它是一维的flat模式的KV存储。表7-2是对象存储,多个不同对象的多个不同属性的二维存储模式。
二者如何映射呢?由于对象的名字是全局唯一的,属性在对象内也是唯一的,所以在LevelDB层面就可以用Object名字和属性的名字联合作为在LevelDB的key,这是能想到的比较直观的解决方式。
例如,object1的属性(key1,value1)保存在LevelDB如下:
(object1_name + key1, value1)
这个方法的一个缺点: 当一个对象有多个kv值时,Object1的name多次作为key存储,由于Object的name一般较长,这种存储方式浪费空间比较大。于是就提出了一种压缩的存储方法,也就是目前omap的存储方式。
1.1 omap存储
目前omap在LevelDB中存储分两步:
1) 在LevelDB中,保存键值对
key: HOBJECT_TO_SEQ + ghobject_key(oid) value: header
其中HOBJECT_TO_SEQ是固定的前缀标识字符串,函数ghobject_key获取对应的对象唯一的key字符串。关于omap中用到的一些常量字符串,如下所示:
header保存对象在LevelDB中的唯一标识seq,以及支持快照的父对象的信息,同时保存了对象的collection和oid(这里冗余保存,因为其实我们通过key信息也可以获得)。
2) 对象的属性保存以下格式的键值对
综上所述,设置和获取对象的属性,需要两步:先根据对象的oid,构造键(HOBJECT_TO_SEQ + ghobject_key(oid)),获取header;根据对象的header中的seq,拼接在LevelDB中的key值(USER_PREFIX + header_key(header->seq) + XATTR_PREFIX +key),获取value值。
变量state用于保存KeyValueDB的全局状态,目前只有seq信息:
函数write_state()用于每次分配seq后,把state信息写入LevelDB中,保存:
1.2 omap的克隆
omap的克隆在10.2.10版本中有两种实现方法:
int DBObjectMap::clone(const ghobject_t &oid, const ghobject_t &target, const SequencerPosition *spos); int DBObjectMap::legacy_clone(const ghobject_t &oid, const ghobject_t &target, const SequencerPosition *spos);
对于clone()方式,则是真正的进行数据的复制(会对header、以及对象的属性key/value都进行复制),这也是v10.2.10版本的默认方式。但这里我们介绍一下legacy_clone,其实现了no-copy方式的复制:
-
当克隆一个新的对象时,会产生两个新的Header,原来的Header作为这两个新的Header的parent,这时候无论是原来的Object还是cloned Object在查询或者写操作时都会查询parent的情况,并且实现copy-on-write。
-
当读取一个子对象的属性时,如果子对象不存在该属性,需要去父对象获取
可以看出,omap的clone机制也实现了copy-on-write机制。
1.3 部分代码实现分析
1.3.1 SequencerPosition
类SequencerPosition用来验证操作的顺序性,当操作执行时,后面的操作序号必须大于之前的操作序号。
1.3.2 lookup_create_map_header
本函数用于获取对象的header:
1) 首先调用函数_lookup_map_header查找对象的header
2) 调用函数_generate_new_header来设置Header的字段,并调用函数write_state写入变量state
3) 调用函数set_map_header,把新的header设置到LevelDB中
1.3.3 get_xattrs
本函数用于获取对象的属性,实现如下:
1) 首先获取对应的Header头部;
2) 调用db设置具体的数据
1.3.4 set_keys
本函数用于设置对象的属性,实现如下:
1) 获取KeyValueDB::Transaction的一个事务
2) 先获取对象的Header
3) 先调用事务set函数设置属性
4) 调用db提交事务
[参看]