文章目录
1. 分析场景举例
分析osd启动过程中加载pg时去leveldb查找pg所对应的版本号整个db操作流程
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的接口进行了封装
结合以上过程,可以得出:到leveldb层最终调用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是能够正确返回的;
2.4 ceph-kvstore-tool 查询某个key的流程
整体逻辑上和leveldb MergingIterator seek过程相似,唯一不同的地方在于
- 直接调用DBImpl::Get接口
- DBImpl::Get实现过程也是调用mem_,imm_,current对应的Get接口,而这些Get实现其实就是迭代的过程
- Get只要查到key的就返回了,但是seek会遍历这个db的内容,包括内存的memtable以及磁盘的sstable文件