leveldb seek与get调用对指定key查询结果的影响

1. 问题现象

2. 问题分析

3. 结论

1. 问题现象

  • osd启动从db数据库加载pg元数据信息失败导致osd无法启动
  • 用ceph-kvstore-tool读取该pg对应的元数据进行解码又没问题
osd/PG.cc: 3067: FAILED assert(0 == "unable to open pg metadata")

在这里插入图片描述

  • 用ceph-objectstore-tool去查pg元数据信息和osd日志peek_map_epoch返回的结果一样,如下图
    在这里插入图片描述
    都是返回时-1怀疑db数据库出问题了,但为什么用ceph-kvstore-tool查看又是ok的,接下来我们带着这个疑问进行分析下

2. 问题分析

2.1 peek_map_epoch接口分析

该接口很简单主要调用store->omap_get_values去加载pg的_epoch以及_info信息;这里补充一下,osd为了兼容不同store支持,把读写,attr操作以及omap操作进行了抽象即抽象出了objectstore层,这里也体现了面向接口编程的思想。omap主要接口有:

  • omap_get_values
  • omap_get_keys
  • omap_get_header
  • omap_get
  • get_omap_iterator
  • _omap_rmkeys
  • _omap_rmkeyrange
  • _omap_setheader
  • _omap_setkeys
  • _omap_clear

对于filestore,omap_get_values最终调用DBOjectMap的get_values接口;DBObjectMap是FileStore的一部分,封装了对KeyValue数据库操作一系列的API;接下来看下 DBObjectMap::get_values过程

2.2 DBObjectMap::get_values流程分析

在这里插入图片描述

DBObjectMap db采用的是leveldb,因此db空间的迭代器如下图所示
在这里插入图片描述

  • leveldb抽象出一个Iterator基类
  • 重现给用户的iterator是一个MergingIterator,它是由mem_、imm_、level0、level1~n对应的迭代器组成的
  • mem_与imm_是内存操作,采用的是跳表数据结构实现的,因此迭代器是基于跳表实现的
  • level0层的sst文件因为key有可能重复,因此level0每个sst文件对应一个TwoLevelIterator迭代器
  • level1~leveln由于每层sst文件key范围不会重复,并且有序的,因此使用一个迭代器NewConcatenatingIterator即可完成整个level的迭代
  • TwoLevelIterator采用的两个iterator实现的,可以简单的认为一个用于索引的iter另一个用于查找真正data的iter;比如针对sst文件的key查询,首先通过key的索引iter到data数据块,然后iter数据块得到key的值
  • Block::Iter是针对sst的index block以及data block的迭代器

DBObjectMap迭代器
在这里插入图片描述

  • IteratorImpl作为DBObjectMapIteratorImpl与具体db的迭代器沟通的桥梁即抽象层
  • LevelDBWholeSpaceIteratorImpl对db的接口进行了封装

在这里插入图片描述
结合以上过程,可以得出:
db_iter->lower_bound最终调用MergingIterator的Seek接口

2.3 leveldb MergingIterator seek过程

  virtual void Seek(const Slice& target) {
    for (int i = 0; i < n_; i++) {
      children_[i].Seek(target);
    }
    FindSmallest();
    direction_ = kForward;
  }

该Seek的逻辑主要包括:

  • 调用之前MergingIterator封装好的各种类型的Iterator迭代器并返回第一个大于等于给定的key的key;
  • 从所有迭代器的结果找出最小的key即第一个大于等于指定key的key返回;

从代码逻辑可以看出Seek会去遍历所有的children迭代器,因此只要有children迭代的过程出异常,则这个MergingIterator的迭代结果就是异常的;但是有可能最小的key是能够正确返回的;

如下图,某个sstable文件的block出现了损坏,其中红框标记的部分应该是block的type/crc footer固定是5个字节的;这里可以明显的看出这个footer被写坏了
在这里插入图片描述

2.4 ceph-kvstore-tool 查询某个key的流程

整体逻辑上和leveldb MergingIterator seek过程相似,不同的地方在于

  • 直接调用DBImpl::Get接口
  • DBImpl::Get实现过程也是调用mem_,imm_,current对应的Get接口,而这些Get实现其实就是迭代的过程
  • Get只要查到key的就返回了,但是seek会遍历这个db的内容,包括内存的memtable以及磁盘的sstable文件

3. 结论

  • omap_get_values会对迭代器执行完的状态即status进行判断,如果不是ok的(例如出现block损坏)则返回-1,因此就出现了peek_map_epoch失败的导致osd加载pg失败;
  • filestore层调用omap_get_values去查看key的值时,应该调用db的get接口而不是seek结果,否则无法得到正确的值

猜你喜欢

转载自blog.csdn.net/sefaxiaoji/article/details/89844394