leveldb深度剖析-查询流程

2023-11-13

至此,将插入流程以及压缩流程都已介绍完毕了,本篇主要介绍查询流程。

一、查询流程

首先来看一下查询接口具体实现内容:

/**
 * 查询
 * @param options 查询选项
 * @param key 查询key
 * @param value 输出参数 如果找到则赋值给value
 */
Status DBImpl::Get(const ReadOptions& options,
                   const Slice& key,
                   std::string* value) {
  Status s;
  MutexLock l(&mutex_);
  SequenceNumber snapshot;
  if (options.snapshot != NULL) {
    snapshot = reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_;
  } else {
    snapshot = versions_->LastSequence();//查询操作 不增加序列号
  }

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

  bool have_stat_update = false;
  Version::GetStats stats;

  // 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)) {//查找mem table
      // Done
    } else if (imm != NULL && imm->Get(lkey, value, &s)) {//查找immutable mem table
      // Done
    } else {
      // 查找ldb文件 最新的版本信息Version都保存在current中
      s = current->Get(options, lkey, value, &stats);
      have_stat_update = true;
    }
    mutex_.Lock();
  }
  //是否需要启动压缩流程
  if (have_stat_update && current->UpdateStats(stats)) {
    MaybeScheduleCompaction();//表示由于某个文件seek次数过多需要合并
  }
  mem->Unref();
  if (imm != NULL) imm->Unref();
  current->Unref();
  return s;
}

查找顺序按照:MemTable -> Immutable MemTable -> ldb文件(sst文件),只要有一个流程中找到则返回。对于查找MemTable流程可参考《leveldb深度剖析-MemTable》。下面介绍在ldb中查询流程

二、ldb查询流程

/**
 * 进入此方法 说明在memtable中没有找到 因此需要在文件中查找
 * @param options 查询流程选项
 * @param k 查询关键字
 * @param value 查询value 输出参数
 * @param 查询结果 输出参数
 */
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 = NULL;
  stats->seek_file_level = -1;
  FileMetaData* last_file_read = NULL;
  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++) {// 循环遍历每一层文件
    size_t num_files = files_[level].size();
    if (num_files == 0) continue;

    // Get the list of files to search in this level
    FileMetaData* const* files = &files_[level][0];
    if (level == 0) {// level0文件中 key是有重叠的
      // 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;//说明key不存在当前level中所有文件
      // 
      std::sort(tmp.begin(), tmp.end(), NewestFirst);
      files = &tmp[0];
      num_files = tmp.size();
    } else {//非level0文件
      // Binary search to find earliest index whose largest key >= ikey.
      uint32_t index = FindFile(vset_->icmp_, files_[level], ikey);
      if (index >= num_files) {//表示没有找到
        files = NULL;
        num_files = 0;
      } else {//当前文件中最大值比key要大 因此只能说明待查找的key可能存在当前文件中 
        tmp2 = files[index];
        if (ucmp->Compare(user_key, tmp2->smallest.user_key()) < 0) {
          // All of "tmp2" is past any data for user_key
          // 要查找的key 比当前文件中最小的key 还要小 说明不在此文件中
          files = NULL;
          num_files = 0;
        } else {// 待查找key 都已经满足文件最大值和最小值  但是此时仍然不能确定是否存在
          files = &tmp2;
          num_files = 1;
        }
      }
    }

    //到这里说明:我们要查找的文件范围已经确定了  具体有没有我们需要的key则需要进一步查询
    //num_files已经重新赋值 当num_files不为0 只能说明待查找key可能存在文件中 需要进一步对比
    for (uint32_t i = 0; i < num_files; ++i) {
      if (last_file_read != NULL && stats->seek_file == NULL) {
        // We have had more than one seek for this read.  Charge the 1st file.
        // 该分支如果进入了 只会进入一次  表示第一个文件不存在我们要查找的key  相当于没有命中 那么我们
        // 就要将其记录下来 并且在UpdateStats函数中对器allow_seek进行减一操作
        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;

      Saver saver;
      saver.state = kNotFound;
      saver.ucmp = ucmp; //比较器
      saver.user_key = user_key;
      saver.value = value; //输出参数 保存value返回给用户
      //构建 table cache 基于局部性原理+LRU算法
      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;
      }
    }
  }

  return Status::NotFound(Slice());  // Use an empty error message for speed
}

说明:

1)该函数有两个for循环,第一个for循环按照层次遍历文件,凡是符合这个条件: smallest.user_key <= 待查找user_key <= largest.user_key的文件都是我们需要查询的。

2) 第二个for循环,按照第一个for循环确定出来的文件,进行深入查询对比。这里采用cache思想来提升性能。

3)leveldb采用LRU方式管理open的文件,table_cache->Get返回后判断saver状态,如果是kFound/kDeleted则说明找到了直接退出,否则继续查找下一个文件。

三、总结

查找流程其实不是特别复杂,主要逻辑还是在于TableCache这部分。由于TableCache功能比较独立,这里单独放一篇博客,来详细说明TableCache具体实现。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

leveldb深度剖析-查询流程 的相关文章

  • gnuradio的安装以及安装常见错误

    本文是从纯小白 0基础的出发点上 从概念入手 不仅介绍gnuradio在Linux上的安装流程 及安装时的常见错误 还普及了一些小白需要了解的必备知识 目录 1 虚拟机的安装 2 Linux系统的安装 3 gnuradio的安装 4 安装常
  • Facebook存储65亿张照片的存储框架

    Facebook存储65亿张照片的存储框架 从未用过Facebook 但是还是对Facebook应对大容量的非结构化数据存储方案感兴趣 本文是通过在线网络广播 webcast 经本人翻译得来的 因此 本人并不能确保本文中叙述的内容与原文we
  • 字符集与编码

    字符集就是客观世界上存在的各种语言符号 如英语26个字符 汉字 拉丁字符等 编码就是将字符集用二级制表示出来的一种规范 常用字符集有ASCII字符集 GB2312字符集 BIG5字符集 GB18030字符集 Unicode字符集等 ASCI
  • 46 openEuler搭建Nginx服务器-管理Nginx

    文章目录 46 openEuler搭建Nginx服务器 管理Nginx 46 1 概述 46 2 前提条件 46 3 启动服务 46 4 停止服务 46 5 重启服务 46 6 验证服务状态 46 openEuler搭建Nginx服务器 管
  • zotero配置

    1 下载安装 2 配置坚果云同步 编辑 首选项 同步 输入zotero账户密码进行数据同步 文件同步选择坚果云同步 3 配置茉莉花插件 安装pdftk
  • 在离线渲染器中应用MERL BRDF

    BRDF Bidirectional Reflection Density Function 即出射光线的radiance和入射光线的irradiance的比值 在图形学中被用来描述物体的表面反射属性 BRDF的值一般来说由几个参数决定 入
  • 数据库SQL千万级数据规模处理概要

    我在前年遇到过过亿条的数据 以至于一个处理过程要几个小时的 后面慢慢优化 查找一些经验文章 才学到了一些基本方法 综合叙之 与君探讨之 1 数据太多 放在一个表肯定不行 比如月周期表 一个月1000万 一年就1 2亿 如此累计下去肯定不行的
  • 菜鸟学习nginx之HTTP请求处理(2)

    在上一篇介绍了Nginx定义的11个阶段 本篇将深入介绍Nginx是如何处理HTTP请求的 一 ngx http process request 处理HTTP请求 param r HTTP请求 当成功接收到请求行和Header时 就可以处理
  • fatal error C1083: 无法打开预编译头文件:“Debug\opencv.pch”: No such file or directory

    步骤 方法右键点击你创建的项目 选择 属性标签 点击属性 弹出 项目属性页 在左侧找到以下位置 配置属性 gt C C gt 预编译头 并选择它 在右边的菜单中选择 创建 使用预编译头 中的 不使用预编译头文件 点击 确定 按钮退出即可原因
  • 存储IOPS指标说明

    二 IOPS 说明 2 1 IOPS Input OutputPer Second IOPS 即每秒的输入输出量 或读写次数 是衡量磁盘性能的主要指标之一 IOPS是指单位时间内系统能处理的I O请求数量 一般以每秒处理的I O请求数量为单
  • 安装raw文件下的apk文件

    有时候我们需要将一些小软件嵌在我们的软件里面 那么我们就可以将这些apk放在我们的raw或者assets文件下进行暂时存储 那么下面我们用放在raw文件下进行展示安装这一过程 首先我们要把我们需要隐藏我apk文件放在raw文件下 raw是在
  • BMP to AVI 及其压缩的实现

    1 设计方案的产生 这个设计方案是物光院嵌入式系统试验室的基于CDMA技术的无线视频传输监控系统的设计的一部分 我简要说明此系统的原理 系统单片机部分主要模块由CDMA DSP与ARM处理器 FLASH ROM组成 此单片机用来获取监控所在
  • 索引表简介

    索引表简介 1 索引的内部构造 因为在索引表中涉及到索引的内部构造知识 所以下面会进行简单的介绍 首先 如果没有索引 当你想要去查找某个值的时候 你不得不对数据进行顺序扫描 如果一张表有n行 那么使用顺序扫描的平均扫描行数为n 2 一旦表的
  • 转】M1卡密钥破解,收藏

    M1卡说明及使用proxmark3破解方法 看了网上写的一些关于M1卡的文章 多数有些误导之嫌 首先谈谈M1卡的规格 M1卡的容量为1KB 好多网上写8KB 这里其实是有个误区 应该是8K位 1Byte 1B 8位 其实也就是说8k位想到于
  • 27 openEuler管理网络-通过ifcfg文件配置网络

    文章目录 27 openEuler管理网络 通过ifcfg文件配置网络 27 1 配置静态网络 27 2 配置动态网络 27 3 配置默认网关 27 openEuler管理网络 通过ifcfg文件配置网络 说明 通过ifcfg文件配置的网络
  • 34 openEuler使用LVM管理硬盘-创建并挂载文件系统

    文章目录 34 openEuler使用LVM管理硬盘 创建并挂载文件系统 34 1 创建文件系统 34 2 手动挂载文件系统 34 3 自动挂载文件系统 34 openEuler使用LVM管理硬盘 创建并挂载文件系统 在创建完逻辑卷之后 需
  • 模拟实现通讯录<二>(动态模拟)

    继静态模拟通讯录 实现动态模拟 静态模拟通讯录博客链接 http blog csdn net bitboss article details 51374654 实现一个通讯录 通讯录可以动态存储信息 每个人的信息包括 姓名 性别 年龄 电话
  • 短视频矩阵系统:批量剪辑+矩阵分发+线索收集产品开发搭建

    短视频矩阵系统是一种综合性的短视频营销链路 通过在不同平台上传布 推广和转载短视频内容 以达到品牌宣传的效果 通过不同平台的内容进行组合 剪辑 形成全方位 多渠道的短视频推广网络链路 一 短视频矩阵系统搭建常见问题 1 抖去推的短视频AI矩
  • SQL查询与修改数据库逻辑文件名,移动数据库存储路径示例

    Author htl258 Tony Date 2010 06 26 21 51 30 Version Microsoft SQL Server 2008 RTM 10 0 1600 22 Intel X86 Jul 9 2008 14 4
  • 函数getopt(),及其参数optind

    getopt被用来解析命令行选项参数 转载地址 http hi baidu com xlt1888 blog item 703148383008492670cf6c2d html include

随机推荐

  • 第十二讲-讲为什么我的MySQL会“抖”一下

    为什么我的MySQL会 抖 一下 当内存数据页跟磁盘数据页内容不一致的时候 我们称这个内存页为 脏页 内存数据写入到磁盘后 内存和磁盘上的数据页的内容就一致了 称为 干净页 抖动的时候应该是在刷脏页 第一种场景是 粉板满了 记不下了 这时候
  • 用opencv2的C api 给图片画矩形框

    使用opencv 3 4 6 Ubuntu系统 步骤 安装opencv 过程可以参考 https docs opencv org master d7 d9f tutorial linux install html 编程 opencv 3 4
  • 0基础也能看懂,软件测试怎么去介绍一个项目的测试流程?

    软件测试流程及规范 一 目标 制定完整且具体的测试路线和流程 为快速 高效和高质量的软件测试提供基础流程框架 最终目标是实现软件测试规范化 标准化 二 测试流程说明 三 需求分析 需求分析由SA制定 要求细化每一个功能的细节 每一个按钮的位
  • 华为OD机试真题-最大报酬【2023.Q1】

    题目内容 小明每周上班都会拿到自己的工作清单 工作清单内包含n项工作 每项工作都有对应的耗时时间 单位h 和报酬 工作的总报酬为所有已完成工作的报酬之和 那么请你帮小明安排一下工作 保证小明在指定的工作时间内工作收入最大化 输入描述 输入的
  • 基于c++的包装者模式初探。

    ifndef INPUTSTREAM H define INPUTSTREAM H class Inputstream public virtual void read 0 virtual Inputstream endif ifndef
  • TensorRT学习笔记--序列化(Serialize)和反序列化(Deserialize)

    目录 前言 1 序列化和反序列化的概念 2 代码实现 2 1 序列化并保存模型 2 2 反序列化加载模型 2 3 完整代码 前言 所有代码均基于 Tensor RT 8 2 5 版本 使用的是 Python 环境 1 序列化和反序列化的概念
  • 电路交换,报文交换与分组交换

    1 电路交换 由于电路交换在通信之前要在通信双方之间建立一条被双方独占的物理通路 由通信双方之间的交换设备和链路逐段连接而成 因而有以下优缺点 优点 由于通信线路为通信双方用户专用 数据直达 所以传输数据的时延非常小 通信双方之间的物理通路
  • Java开发环境不再需要配置classpath!

    前言 之前发布了关于java开发环境配置的文章 经过与网友的交流 我了解到在jdk1 5以后 java开发环境配置的时候 确实不需要对classpath进行配置 但市面上的书籍 以及一些博客 还是老一套 继续推荐配置classpath 并且
  • Jmeter 调用python3脚本

    前言 Jmeter 调用 Jython的Jar包 虽然可以执行python代码 但是只支持python2 7及2 7以下版本的 目前使用的都是py3 0以上的版本 所以放弃这种方法 解决方法 通过jmeter的BeanShell取样器 通过
  • websocket 简介

    一 WebSocket是html5新增加的一种通信协议 目前流行的浏览器都支持这个协议 例如Chrome Safrie Firefox Opera IE等等 对该协议支持最早的应该是chrome 从chrome12就已经开始支持 随着协议草
  • Centos7 修改主机名

    简介 在CentOS中 有三种定义的主机名 静态的 static 瞬态的 transient 和灵活的 pretty 静态主机名也称为内核主机名 是系统在启动时从 etc hostname自动初始化的主机名 瞬态主机名是在系统运行时临时分配
  • Windows+Ubuntu双系统下卸载Ubuntu

    记录一下自己卸载Ubuntu的步骤 防止以后再卸载重新找教程 1 删除Ubuntu的分区 步骤1 打开 我的电脑 选择 管理 点击 磁盘管理 步骤2 确定Ubuntu系统所在的磁盘分区 我的是磁盘1的磁盘分区6 7 8 9 然后一一删除 选
  • python每日一题_Python每日一题 001

    Talk is Cheap show me the code Linus Torvalds 将你的 QQ 头像 或者微博头像 右上角加上红色的数字 类似于微信未读信息数量那种提示效果 类似于图中效果 环境准备 安装PIL模块 Windows
  • MySQL之KEY分区和LINEAR KEY分区

    KEY分区 KEY分区与HASH分区相似 当然有不同点 1 在HASH分区中 可以使用整数列或者基于列值的表达式 即PARTITION BY HASH expr 而在KEY分区中 直接基于列 PARTITION BY KEY column
  • css清除默认样式

    清除默认样式 初始化样式 清除内外边距 margin 0 padding 0 自减盒子模型 box sizing border box ul ol 清除默认圆点 list style none a 取消下划线 text decoration
  • 华南x79 主板说明书下载_主板说明书找不到 机箱连线照样秒安装

    点击上方 电脑爱好者关注我们 小伙伴们在安装 升级电脑的时候 在主板上安装各种配件应该问题不大 但是连线呢 特别是机箱上那些细碎的小接口们 即使照着说明书都要琢磨半天 如果是老主板 一下子找不到说明书怎么办 这里小编就跟大家说一些简单的办法
  • windows上 nginx 配置好了access.log路径后,但是访问日志并没有记录

    windows 上配置 nginx access log 日志 1 问题 2 解决方案 2 1 为当前文件夹配置权限 3 结果 1 问题 之前 本机上配置了 nginx access log 日志 但是 并没有 记录 当时因为太忙了 就没有
  • 【Mybatis源码分析】动态标签的底层原理,DynamicSqlSource源码分析

    DynamicSqlSource 源码分析 一 DynamicSqlSource 源码分析 DynamicContext源码分析 SqlNode源码分析 动态SQL标签 Mybatis 动态SQL标签 举例 调试 SqlNode源码分析 M
  • Ubuntu/Linux下安装DosBox配置汇编环境

    Ubuntu Linux下安装DosBox配置汇编环境 微信关注公众号 夜寒信息 致力于为每一位用户免费提供更优质技术帮助与资源供给 感谢支持 一 首先我们去DosBox官网下载DosBox 0 73 或者直接启用终端命令行输入以下代码 s
  • leveldb深度剖析-查询流程

    至此 将插入流程以及压缩流程都已介绍完毕了 本篇主要介绍查询流程 一 查询流程 首先来看一下查询接口具体实现内容 查询 param options 查询选项 param key 查询key param value 输出参数 如果找到则赋值给