Get

LevelDB提供了Get接口用于给定key的查找:

Status DBImpl::Get(const ReadOptions &options,
                   const Slice &key,
                   std::string *value)

Get操作可以指定在某个snapshot的情况下进行,如果指定了snapshot,则获取该snapshot的sequencenumber,如果没有指定snapshot,就取当前最新的sequencenumber:

    Status s;
    MutexLock l(&mutex_);
    SequenceNumber snapshot;
    if (options.snapshot != nullptr)
    {
        snapshot =
            static_cast<const SnapshotImpl *>(options.snapshot)->sequence_number();
    }
    else
    {
        snapshot = versions_->LastSequence();
    }

    MemTable *mem = mem_;
    MemTable *imm = imm_;
    Version *current = versions_->current();
    mem->Ref();
    if (imm != nullptr)
        imm->Ref();
    current->Ref();

首先在memtable里找,如果找到了就结束查找,然后再immutable memtable里找(如果immutable memtable存在),如果找到了就结束查找,在这两个地方查找使用的都是MemTable类提供的Get接口函数(在这里有分析https://www.cnblogs.com/YuNanlong/p/9426795.html)。最后使用Version类提供的Get接口函数在sstable中查找:

    // Unlock while reading from files and memtables
    {
        mutex_.Unlock();
        // First look in the memtable, then in the immutable memtable (if any).
        LookupKey lkey(key, snapshot);
        if (mem->Get(lkey, value, &s))
        {
            // Done
        }
        else if (imm != nullptr && imm->Get(lkey, value, &s))
        {
            // Done
        }
        else
        {
            s = current->Get(options, lkey, value, &stats);
            have_stat_update = true;
        }
        mutex_.Lock();
    }

如果在sstable中查找了,会更新查找涉及到的sstable的seek次数,可能会触发compact条件,因此需要调用MaybeScheduleCompaction函数进行可能的compact操作(在这里有分析https://www.cnblogs.com/YuNanlong/p/9440548.html):

    if (have_stat_update && current->UpdateStats(stats))
    {
        MaybeScheduleCompaction();
    }
    mem->Unref();
    if (imm != nullptr)
        imm->Unref();
    current->Unref();
    return s;

接下来分析Version类封装的Get函数:

Status Version::Get(const ReadOptions &options,
                    const LookupKey &k,
                    std::string *value,
                    GetStats *stats)

首先是一些变量必要的初始化:

    Slice ikey = k.internal_key();
    Slice user_key = k.user_key();
    const Comparator *ucmp = vset_->icmp_.user_comparator();
    Status s;

    stats->seek_file = nullptr;
    stats->seek_file_level = -1;
    FileMetaData *last_file_read = nullptr;
    int last_file_read_level = -1;

    // We can search level-by-level since entries never hop across
    // levels.  Therefore we are guaranteed that if we find data
    // in an smaller level, later levels are irrelevant.
    std::vector<FileMetaData *> tmp;
    FileMetaData *tmp2;

在每一层中搜索:

    for (int level = 0; level < config::kNumLevels; level++)
    {

如果该level没有文件则直接跳过:

        size_t num_files = files_[level].size();
        if (num_files == 0)
            continue;

如果当前位于level0,将所有可能包含key的文件都加入files中:

        // Get the list of files to search in this level
        FileMetaData *const *files = &files_[level][0];
        if (level == 0)
        {
            // Level-0 files may overlap each other.  Find all files that
            // overlap user_key and process them in order from newest to oldest.
            tmp.reserve(num_files);
            for (uint32_t i = 0; i < num_files; i++)
            {
                FileMetaData *f = files[i];
                if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 &&
                    ucmp->Compare(user_key, f->largest.user_key()) <= 0)
                {
                    tmp.push_back(f);
                }
            }
            if (tmp.empty())
                continue;

            std::sort(tmp.begin(), tmp.end(), NewestFirst);
            files = &tmp[0];
            num_files = tmp.size();
        }

如果当前不是level0,则调用FindFile进行二分查找,找到file后验证要找的key是不是在file中,如果是,加入files:

        else
        {
            // Binary search to find earliest index whose largest key >= ikey.
            uint32_t index = FindFile(vset_->icmp_, files_[level], ikey);
            if (index >= num_files)
            {
                files = nullptr;
                num_files = 0;
            }
            else
            {
                tmp2 = files[index];
                if (ucmp->Compare(user_key, tmp2->smallest.user_key()) < 0)
                {
                    // All of "tmp2" is past any data for user_key
                    files = nullptr;
                    num_files = 0;
                }
                else
                {
                    files = &tmp2;
                    num_files = 1;
                }
            }
        }

遍历找到的files,如果seek的文件不止一个,则记录下第一个seek的文件,之后要将这个文件的seek减一(调用UpdateStats函数):

        for (uint32_t i = 0; i < num_files; ++i)
        {
            if (last_file_read != nullptr && stats->seek_file == nullptr)
            {
                // We have had more than one seek for this read.  Charge the 1st file.
                stats->seek_file = last_file_read;
                stats->seek_file_level = last_file_read_level;
            }

            FileMetaData *f = files[i];
            last_file_read = f;
            last_file_read_level = level;

调用table_cache_->Get函数在文件中搜索key值,如果没有找到,则继续搜索下一个file,如果找到了,不论是删除的还是过期的,都返回(因为之后就算找到了key,也比现在的key旧,被现在的key覆盖):

            Saver saver;
            saver.state = kNotFound;
            saver.ucmp = ucmp;
            saver.user_key = user_key;
            saver.value = value;
            s = vset_->table_cache_->Get(options, f->number, f->file_size,
                                         ikey, &saver, SaveValue);
            if (!s.ok())
            {
                return s;
            }
            switch (saver.state)
            {
            case kNotFound:
                break; // Keep searching in other files
            case kFound:
                return s;
            case kDeleted:
                s = Status::NotFound(Slice()); // Use empty error message for speed
                return s;
            case kCorrupt:
                s = Status::Corruption("corrupted key for ", user_key);
                return s;
            }
        }

230 Love u

LevelDB源码分析-Get的更多相关文章

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

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

  2. leveldb源码分析--WriteBatch

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

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

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

  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内核也有个内存池.内存池的存在主要就是减 ...

  10. 【转】Leveldb源码分析——1

    先来看看Leveldb的基本框架,几大关键组件,如图1-1所示. Leveldb是一种基于operation log的文件系统,是Log-Structured-Merge Tree的典型实现.LSM源 ...

随机推荐

  1. webView 获取内容高度不准确的原因是因为你设置了某个属性

    不管是UIWebView 还是 WKWebView 这里 获取js属性 获取高度的方法 我就不一一细说了 ,本文最主要不说这个 ,网上有太多的方法 我最不摘几个 CGFloat webViewHeig ...

  2. QT | QT MSVC 2015 + VS 2015开发环境配置及GIT设置

    1.下载: 所有Qt版本的下载地址: http://download.qt.io/archive/qt/ 实际使用了http://download.qt.io/archive/qt/5.7/5.7.1 ...

  3. freeswitch黑名单mod_blacklist使用

    freeswitch自带黑名单模块"mod_blacklist",此文只是对该模块简单使用的实例. 最近接到客户投诉有大量骚扰电话,而从源头查不太容易,因此想到的笨方法是将投诉人加 ...

  4. Python全栈之路----数据类型—元组类型

    元组类型:有序,不可变的,又叫只读列表  names = ("alex","jack","eric") 1.元组本身不可变,但如果元组中有可 ...

  5. Codeforces 1080C- Masha and two friends

    AC代码 #include <bits/stdc++.h> #define ll long long const int maxn=1e6+10; using namespace std; ...

  6. 求数组的相邻子数组的最大值(txt文件存储)

    package mypackage; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File ...

  7. 关于JVM的一些冷知识

    (1) Java加载类的一般顺序: 1.静态属性,静态方法声明,静态块. 2.动态属性,普通方法声明,构造块. 3.构造方法. 当加载一个类时,JVM会根据属性的数据类型第一时间赋默认值(一举生成的) ...

  8. myelipse与maven结合配置

    添加插件 指定maven中jar包默认仓库地址 maven中debug模式中断点关联不到项目代码的问题 解决方法参考文章:https://blog.csdn.net/urnot/article/det ...

  9. 深入学习Motan系列(四)—— 客户端

    困惑的袋鼠,对框架的把握有些茫然,但是仍然一步步向前,行动总比一直迷茫停止不前要好,您说呢,各位客官? 这篇开始客户端的分析.有些地方的代码,就不每段都标出了,中间有跳跃的地方,请自己对照代码来看.鄙 ...

  10. PythonStudy——字符串、列表、元组、字典、集合 (str、list、tuple、dict、set)常用方法总结

    字符串: # 字符串类型的一些操作(str)st1='hello world 'print(st1[0]) # 根据字符串索引来取字符h 找不到直接崩print(st1[-1]) # 根据索引倒取st ...