既然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. java字节码文件

    查看字节码文件: javap  -verbose  HellloWorld.class

  2. editplus tag

    #T=HTML<!DOCTYPE html><html lang="zh-CN"><head><meta content="te ...

  3. Vue + Element UI 实现权限管理系统 前端篇(九):接口格式定义

    接口请求格式定义 前台显示需要后台数据,我们这里先把前后端交互接口定义好,没有后台的时候,也方便用mock模拟. 接口定义遵循几个规范: 1. 接口按功能模块划分. 系统登录:登录相关接口 用户管理: ...

  4. nginx反向代理如何获取真实IP?

    由于客户端和web服务器之间增加了中间层,因此web服务器无法直接拿到客户端的ip,通过$remote_addr变量拿到的将是反向代理服务器的ip地址. 1.安装--with-http_realip_ ...

  5. Hibernate的集合一对多与多对一

    需求:   部门与员工 一个部门有多个员工;       [一对多] 多个员工,属于一个部门    [多对一] 1.javaBean ——Dept.java package com.gqx.oneto ...

  6. Linux-(telnet,wget)

    telnet命令 telnet命令通常用来远程登录.telnet程序是基于TELNET协议的远程登录客户端程序.Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和 ...

  7. mac 入门操作

    1. 打开制定目录 在finder里使用command+shift+g 快捷键可以完成到达某路径的操作选中文件/目录,显示简介可以查看路径 2. home end pageUp pageDown 在苹 ...

  8. Spring 通过Java代码装配bean

    1. 背景 书接上文Spring自动化装配bean 尽管在很多场景下通过组件扫描和自动装配实现Spring的自动化扫描配置是更为推荐的方式,但在有些情况下自动化扫描的方案行不通,如想要将第三方库中的组 ...

  9. SPA页面初试

    之前一直很好奇,SPA应用到底是怎么实现的,昨天无意间看到了有一篇介绍的文章,就想着来试一下水(以下根据我的理解所写,可能会让你看的云里雾里,如果想加深了解,最好先了解下window.location ...

  10. 自动加载的iframe高度自适应

    动态产生iframe,自动加载至body中,还有一个功能就是iframe的高度自适应,下面代码测试于IE和Firefox,Chrome: