FileStore中的omap

| 分类 ceph-internal  | 标签 ceph 

前言

对于ceph的FileStore实现而言,ObjectMap是非常重要的。因为除了将数据写入本地文件系统(XFS or EXT4 or btrfs,Whatever)还有属性信息,有少量的属性记录在本地文件系统中,以扩展属性xattr的方式存储。还有一些属性信息记录在key-value DB中。而key-value DB部分即ObjectMap,简称omap。

FileStore雇佣的key-value DB有三个版本,分别是google的LevelDB,以及facebook在LevelDB基础上的改进版 RocksDB,还有KineticStore。早期的ceph使用LevelDB,原因是Ceph项目的早期,RocksDB还没有横空出世。Ceph后期的版本从LevelDB switch to RocksDB。

代码导读

FileStore的omap中存放的都是对象的属性信息,以key-value的形式存在,那么对于不同的属性,如何定义对象的键值key呢?

最直接的想法就是(object id + xattr key),两者结合一起,形成对象的键值key,但是这种方法有一个问题,object id可能会很长,尤其是当单个对象存在很多属性的时候,object id不得不在key值中出现多次,这必然会造成存储空间的浪费。

Ceph的FileStore分成了2步,第一步根据object id生成一个比较短的seq,然后seq + xattr key形成对象的某个属性的键值。

omap不是通过计算从object id 获取seq的,他是首先根据object id,存放一个Header类型的数据结构到LevelDB,其中Header中的一个成员变量为seq。

key : HOBJECT_TO_SEQ +ghobject_key(oid)

value : header

Header的定义如下:

274   struct _Header {
275     uint64_t seq;
276     uint64_t parent;
277     uint64_t num_children;
278 
279     coll_t c;
280     ghobject_t oid;
281 
282     SequencerPosition spos;
     }

对象的属性是如下的key-value pair

key: USER_PREFIX + header_key(header->seq) + XATTR_PREFIX +key value: value(即该key对应value值)

综合上面的讨论,如果要获取某个对象的oid的某个属性的值,需要分成两步走

1 找到Header,从header中取出seq的值

2 根据seq的值生成该属性对应的新的最终的键值,从LevelDB中取出value

这部分的代码逻辑在:

/*获取对象oid的某个或者某几个属性的值*/
int DBObjectMap::get_xattrs(const ghobject_t &oid,
                const set<string> &to_get,
                map<string, bufferlist> *out)
{
  MapHeaderLock hl(this, oid);
  /*第一步根据osd找到header*/
  Header header = lookup_map_header(hl, oid);
  if (!header)
    return -ENOENT;
  
  /*根据找到的header中的seq,生成属性的键值,去levelDB中寻找该key对应的value*/
  return db->get(xattr_prefix(header), to_get, out);
}

int DBObjectMap::set_xattrs(const ghobject_t &oid,
                const map<string, bufferlist> &to_set,
                const SequencerPosition *spos)
{
  KeyValueDB::Transaction t = db->get_transaction();
  MapHeaderLock hl(this, oid);
  /*寻找oid对应的header,如若没有,则新建一个header*/
  Header header = lookup_create_map_header(hl, oid, t);
  if (!header)
    return -EINVAL;
  if (check_spos(oid, header, spos))
    return 0;
  /*根据header中的seq,得到真正的键值,然后设置一个或者多个属性*/
  t->set(xattr_prefix(header), to_set);                                                                                                                
  return db->submit_transaction(t);
}

const string DBObjectMap::USER_PREFIX = "_USER_";
const string DBObjectMap::XATTR_PREFIX = "_AXATTR_";
string DBObjectMap::header_key(uint64_t seq)     
{
  char buf[100];
  snprintf(buf, sizeof(buf), "%.*" PRId64, (int)(2*sizeof(seq)), seq);
  return string(buf);
}

string DBObjectMap::xattr_prefix(Header header)
{
  return USER_PREFIX + header_key(header->seq) + XATTR_PREFIX;         
}

介绍了seq,看样子它最作用很重要,它的值是怎么生成的呢?

LevelDB中存放着一个特殊的全局意义的key-value

key: SYS_PREFIX + GLOBAL_STATE_KEY value : state

其中state的定义如下:

  struct State {
    __u8 v;
    uint64_t seq;                                                                                                                                      
    State() : v(0), seq(1) {}
    explicit State(uint64_t seq) : v(0), seq(seq) {}

    void encode(bufferlist &bl) const {
      ENCODE_START(2, 1, bl);
      ::encode(v, bl);
      ::encode(seq, bl);
      ENCODE_FINISH(bl);
    }

    void decode(bufferlist::iterator &bl) {
      DECODE_START(2, bl);
      if (struct_v >= 2)
    ::decode(v, bl);
      else
    v = 0;
      ::decode(seq, bl);
      DECODE_FINISH(bl);
    }

    void dump(Formatter *f) const {
      f->dump_unsigned("seq", seq);
    }
    ...
}

该结构体只有一个成员变量,即seq,当产生新的Header的时候,会该值会递增,写入LevelDB

1115 DBObjectMap::Header DBObjectMap::_generate_new_header(const ghobject_t &oid,
1116                               Header parent)
1117 {
1118   Header header = Header(new _Header(), RemoveOnDelete(this));
1119   header->seq = state.seq++;
1120   if (parent) {
1121     header->parent = parent->seq;
1122     header->spos = parent->spos;
1123   }
1124   header->num_children = 1;
1125   header->oid = oid;
1126   assert(!in_use.count(header->seq));
1127   in_use.insert(header->seq);
1128 
1129   write_state();
1130   return header;
1131 }

因为是全局的,为了防止竞争,需要加锁保护。

  Header generate_new_header(const ghobject_t &oid, Header parent) {
    Mutex::Locker l(header_lock);//加锁保护
    return _generate_new_header(oid, parent);
  }
  
DBObjectMap::Header DBObjectMap::lookup_create_map_header(
  const MapHeaderLock &hl, 
  const ghobject_t &oid,
  KeyValueDB::Transaction t)
{
  Mutex::Locker l(header_lock); // 加锁保护
  Header header = _lookup_map_header(hl, oid);
  if (!header) {
    header = _generate_new_header(oid, Header());                                                                                                      
    set_map_header(hl, oid, *header, t);
  }
  return header;
} 

理解了上述部分,DBObjectMap部分的其他代码,基本就是流水账了,可以比较容易的理解了,不再赘述。


上一篇     下一篇