既然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的更多相关文章

  1. leveldb源码分析--Key结构

    [注]本文参考了sparkliang的专栏的Leveldb源码分析--3并进行了一定的重组和排版 经过上一篇文章的分析我们队leveldb的插入流程有了一定的认识,而该文设计最多的又是Batch的概念 ...

  2. leveldb源码分析--SSTable之block

    在SSTable中主要存储数据的地方是data block,block_builder就是这个专门进行block的组织的地方,我们来详细看看其中的内容,其主要有Add,Finish和CurrentSi ...

  3. leveldb源码分析--WriteBatch

    从[leveldb源码分析--插入删除流程]和WriteBatch其名我们就很轻易的知道,这个是leveldb内部的一个批量写的结构,在leveldb为了提高插入和删除的效率,在其插入过程中都采用了批 ...

  4. Leveldb源码分析--1

    coming from http://blog.csdn.net/sparkliang/article/details/8567602 [前言:看了一点oceanbase,没有意志力继续坚持下去了,暂 ...

  5. leveldb源码分析--日志

    我们知道在一个数据库系统中为了保证数据的可靠性,我们都会记录对系统的操作日志.日志的功能就是用来在系统down掉的时候对数据进行恢复,所以日志系统对一个要求可靠性的存储系统是极其重要的.接下来我们分析 ...

  6. leveldb源码分析之Slice

    转自:http://luodw.cc/2015/10/15/leveldb-02/ leveldb和redis这样的优秀开源框架都没有使用C++自带的字符串string,redis自己写了个sds,l ...

  7. LevelDB源码分析--Cache及Get查找流程

    本打算接下来分析version相关的概念,但是在准备的过程中看到了VersionSet的table_cache_这个变量才想起还有这样一个模块尚未分析,经过权衡觉得leveldb的version相对C ...

  8. leveldb源码分析--SSTable之TableBuilder

    上一篇文章讲述了SSTable的格式以后,本文结合源码解析SSTable是如何生成的. void TableBuilder::Add(const Slice& key, const Slice ...

  9. leveldb源码分析之内存池Arena

    转自:http://luodw.cc/2015/10/15/leveldb-04/ 这篇博客主要讲解下leveldb内存池,内存池很多地方都有用到,像linux内核也有个内存池.内存池的存在主要就是减 ...

随机推荐

  1. ThreadPoolExecutor参数讲解

    1. 线程池可以节省创建多个线程带来的开销问题. 2. 线程池的参数如下: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSiz ...

  2. solr(一) : 整合 tomcat

    前面 lucene 初探 都是为了solr打基础的. 虽然lucene 的filter 没有涉及, 但是打基础, 差不多够用了. 一. solr 和 lucene 的区别 这里我就用自己的理解来说了, ...

  3. 【IT笔试面试题整理】二叉搜索树转换为双向链表

    [试题描述] 将二叉搜索树转换为双向链表 对于二叉搜索树,可以将其转换为双向链表,其中,节点的左子树指针在链表中指向前一个节点,右子树指针在链表中指向后一个节点. 思路一: 采用递归思想,对于二叉搜索 ...

  4. [转]SQL Collation冲突解决 临时表

    本文转自:http://ju.outofmemory.cn/entry/191163 问题描述 在SQL Server中使用一些复杂的存储过程时,我们需要借用临时表来完成一些逻辑的处理,例如:数据的临 ...

  5. jQuery.Form.js 异步提交表单使用总结

    jQuery.Form.js 是一个用于使用jQuery异步提交表单的插件,它使用方法简单,支持同步和异步两种方式提交. 第一步:引入jQuery与jQuery.Form.js <script ...

  6. Python的两种运行方式

    从2015年5月19日注册博客园,立志于要通过写博客的方式,记录自己编程的点点滴滴,由于自己太懒,一直拖到现在,“拖延症”是病得改,今天终于写自己第一篇博客了,有点小激动! Python是由Guido ...

  7. Elasticsearch Query DSL 整理总结(三)—— Match Phrase Query 和 Match Phrase Prefix Query

    目录 引言 Match Phase Query slop 参数 analyzer 参数 zero terms query Match Phrase 前缀查询 max_expansions 小结 参考文 ...

  8. qsort函数的用法

    qsort函数的用法   qsort 功 能: 使用快速排序例程进行排序  用 法: void qsort(void *base, int nelem, int width, int (*fcmp)( ...

  9. Apache poi 使用经验分享

    我在使用apache poi导入导出Excel做项目过程中,遇到了许许多多的问题,在此简单罗列. 1.xls和xlsx的区分:poi将2003以前和2007以后的excel封装成了两个不同的实现:HS ...

  10. LeetCode DB : Delete Duplicate Emails

    Write a SQL query to delete all duplicate email entries in a table named Person, keeping only unique ...