leveldb源码分析--Comparator
既然leveldb是一个按Key序组织的LSM-Tree实现,那么对于Key的比较就是非常之重要了,这个Key的比较在leveldb中是Comparator的形式出现的。我们首先来看看Comparator的基本方法有哪些
// 实际的比较函数
virtual int Compare(const Slice& a, const Slice& b) const = ; // 名称,主要是为了防止建立和读取时使用了不同的Comparator
virtual const char* Name() const = ; //找出 [start, limit)之间的一个短的串,主要作用是降低一些存储空间
virtual void FindShortestSeparator(
std::string* start,
const Slice& limit) const = ; //作用类似,但无上端限制
virtual void FindShortSuccessor(std::string* key) const = ;
在leveldb中已经实现的类有两个,一个是内置的BytewiseComparatorImpl,另一个是InternalKeyComparator。我们首先来分析BytewiseComparatorImpl的实现,实现十分简单,我们这里只对实现的功能用注释的方式进行说明
// Bytewise直接调用Slice的Compare,按memcmp的方式进行比较,然后再比较长短
int Compare(const Slice& a, const Slice& b)
//对start和limit的公共部分外的start中的可以uint8方式+1的字节+1,清除该位之后的数据
void FindShortestSeparator(std::string* start, const Slice& limit)//直接对key中第一个可以uint8方式+1的字节+1,清除该位后面的数据
void FindShortSuccessor(std::string* key)
我们分析InternalKeyComparator的内部实现,看其成员变量其包含了一个Comparator类型的user_comparator_,其比较都用到了这个成员变量的方法,这个类的实现是在使用这些方法的过程中加入了一些解码的过程。根据其解码过程和名字我们可以看出这个比较器是用来比较传入为InternalKey对象,我们知道其组成为
user_key (string) | sequence ( byte) | value_type ( byte)
对于InternalKeyComparator的三个函数的具体实现说明为
// 传入的值解码得到user_key后对user_key进行比较
int Compare(const Slice& a, const Slice& b)
//解码后对user_key FindShortestSeparator,然后再最后加入kMaxSequenceNumber|kValueTypeForSeek
void FindShortestSeparator(std::string* start, const Slice& limit)//将key的第一个可以+1的字节+1,然后加上kMaxSequenceNumber|kValueTypeForSeek
void FindShortSuccessor(std::string* key)
我们再来看看MemTable关于Table 的定义
typedef SkipList<const char*, KeyComparator> Table;
Table table_; 而 KeyComparator的定义为:
struct KeyComparator {
const InternalKeyComparator comparator;
explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { }
int operator()(const char* a, const char* b) const;
};
由此可知KeyComparator是一个引用了InternalKeyComparator作为成员变量的结构体,而InternalKeyComparator又引用了一个 Comparator类型的user_comparator_。理清了了这些所谓的Comparator的引用层次关系以后,我们来看看leveldb中定义SkipList是使用的哪个Comparator。首先看MemTable的构造函数
MemTable::MemTable(const InternalKeyComparator& cmp)
: comparator_(cmp),
refs_(),
table_(comparator_, &arena_) {
}
可以看到构造函数接收一个InternalKeyComparator类型的参数,然后构建内部的KeyComparator,然后在将其传递给SkipList 的table_,而通过查找我们看到其构造是在DBImpl的构造函数中被调用的,看代码
DBImpl::DBImpl(const Options& raw_options, const std::string& dbname)
: env_(raw_options.env),
internal_comparator_(raw_options.comparator),
......
mem_(new MemTable(
internal_comparator
_)),
再继续往下找到有如下代码
Status DB::Open(const Options& options, const std::string& dbname,
DB** dbptr) {
DBImpl* impl = new DBImpl(options, dbname);
......
}
所以我们可以得出结论是最终的SkipList中使用的Comparator就是在Open数据库的时候传入的参数Option中的成员变量comparator,所以我们在实现自己的Comparator的时候只有仿照BytewiseComparatorImpl实现一个,然后通过option的方式传递给leveldb即可。
最后我们整理一下思路:
1. SkipList中使用的KeyComparator仅仅是对InternalKeyComparator的一个包含式的封装;
2. 而InternalKeyComparator是对key的简单编解码后使用option中传入的Comparator,默认为BytewiseComparatorImpl
InternalKeyComparator中的编解码是user_key和InternalKey之间的转换,所以最终的顺序(大小)的比较其实都是user_key(Put,Get,Delete传入的Key值)根据option中的Comparator(默认为BytewiseComparatorImpl)进行compare得出顺序。
理清这个顺序以后对leveldb中的各个Comparator就比较容易理解了。
leveldb源码分析--Comparator的更多相关文章
- leveldb源码分析--Key结构
[注]本文参考了sparkliang的专栏的Leveldb源码分析--3并进行了一定的重组和排版 经过上一篇文章的分析我们队leveldb的插入流程有了一定的认识,而该文设计最多的又是Batch的概念 ...
- leveldb源码分析--SSTable之block
在SSTable中主要存储数据的地方是data block,block_builder就是这个专门进行block的组织的地方,我们来详细看看其中的内容,其主要有Add,Finish和CurrentSi ...
- leveldb源码分析--WriteBatch
从[leveldb源码分析--插入删除流程]和WriteBatch其名我们就很轻易的知道,这个是leveldb内部的一个批量写的结构,在leveldb为了提高插入和删除的效率,在其插入过程中都采用了批 ...
- Leveldb源码分析--1
coming from http://blog.csdn.net/sparkliang/article/details/8567602 [前言:看了一点oceanbase,没有意志力继续坚持下去了,暂 ...
- leveldb源码分析--日志
我们知道在一个数据库系统中为了保证数据的可靠性,我们都会记录对系统的操作日志.日志的功能就是用来在系统down掉的时候对数据进行恢复,所以日志系统对一个要求可靠性的存储系统是极其重要的.接下来我们分析 ...
- leveldb源码分析之Slice
转自:http://luodw.cc/2015/10/15/leveldb-02/ leveldb和redis这样的优秀开源框架都没有使用C++自带的字符串string,redis自己写了个sds,l ...
- LevelDB源码分析--Cache及Get查找流程
本打算接下来分析version相关的概念,但是在准备的过程中看到了VersionSet的table_cache_这个变量才想起还有这样一个模块尚未分析,经过权衡觉得leveldb的version相对C ...
- leveldb源码分析--SSTable之TableBuilder
上一篇文章讲述了SSTable的格式以后,本文结合源码解析SSTable是如何生成的. void TableBuilder::Add(const Slice& key, const Slice ...
- leveldb源码分析之内存池Arena
转自:http://luodw.cc/2015/10/15/leveldb-04/ 这篇博客主要讲解下leveldb内存池,内存池很多地方都有用到,像linux内核也有个内存池.内存池的存在主要就是减 ...
随机推荐
- RESTful API后台系统架构设计(Java)
最近设计和实现了一个JAVA的RESTful API的后台业务系统架构,主要基于Java平台.设计要求是: 性能:平均响应时间(RESTful API)小于2s(平均负载的情况下),并发访问200个以 ...
- Java你不知道的那些事儿—Java隐藏特性(上)
每种语言都很强大,不管你是像我一样的初学者还是有过N年项目经验的大神,总会有你不知道的东西.就其语言本身而言,比如Java,也许你用Java开发了好几年,对其可以说是烂熟于心,但你能保证Java所有的 ...
- FutureTask源码解析
在Java中一般通过继承Thread类或者实现Runnable接口这两种方式来创建多线程,但是这两种方式都有个缺陷,就是不能在执行完成后获取执行的结果,因此Java 1.5之后提供了Callable和 ...
- Linux QT 连接 Sqlite数据库
#include <QApplication> #include <QDebug> #include <QSqlQuery> #include <QSqlDa ...
- UVa 10562 Undraw the Trees(递归遍历)
题目链接: https://cn.vjudge.net/problem/UVA-10562 Professor Homer has been reported missing. We suspect ...
- 1.1 PIL:Python图像处理类库
from PIL import Image img = Image.open('Husky.jpg') # 看看这货长什么样子 img # 看看它的大小 print('The size of this ...
- the first blog in 2017——《论作为程序员的我考研》
怎么说,人总是有了压力才有动力.想来如今已经是2017年2月13日,2017年已经使用了43天,距离成绩出来还有3天.这个过年是最不充实的一个年,也是时间长达一个月久的“寒假”,因为“考研”.至于考到 ...
- C++虚析构函数解析
当派生类对象从内存中撤销时一般先运行派生类的析构函数,然后再调用基类的析构函数. 如果用new运算符建立的派生类的临时对象,对指向基类的指针指向这个临时对象当用delete运算符撤销对象时,系统执行的 ...
- js 筛选数据
<input type="text" id="filterName"> <div class="scope fr"> ...
- F5刷新缘何会引起表单重复提交
首先,页面第一次加载,在未进行任何操作,表单没有提交过的前提下,此时点击F5刷新,是没有任何问题的. F5刷新引起表单重复提交 前提条件: 用户已通过 (1)submit按钮 (2)js的form.s ...