LevelDB 源码层次上看读取过程

2023-12-01 04:18

本文主要是介绍LevelDB 源码层次上看读取过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 读取流程
    • 1)memtable查找
    • 2) immutable查找
    • 3)SSTable查找
  • 参考文献

读取流程

LevelDB的读取流程相对简单,从其中读取一个数据,会按照从上而下memtable->immutable->sstable的顺序读取,读不到就从下一个层级读取,因此LevelDB更适合读取新写入的数据。流程如下图:
在这里插入图片描述
Level0中的文件直接由Immutable通过dump产生,所以说,这一level的文件之中的key可能会相互重叠,所以需要对level0的每个文件依次查找。对于其他层次,LevelDB的归并过程保证了其中的key互相不重叠并且有序,因此可以直接使用二分方式进行数据查找。下面我们来通过代码看看这个过程:

所有发生的这一切过程,我们都可以在DBImpl::Get方法中看到,其原型如下:

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

而读取的过程如下:

  {mutex_.Unlock();LookupKey lkey(key, snapshot);//首先在memtable查找if (mem->Get(lkey, value, &s)) {//如果没有找到就在immutable查找} else if (imm != nullptr && imm->Get(lkey, value, &s)) {} else {//最后在SSTable查找s = current->Get(options, lkey, value, &stats);have_stat_update = true;}mutex_.Lock();}

1)memtable查找

我们看到这里用来查找的key都是LookupKey这个实例化类的对象,这个类的构造函数传入一个Slice类对象key和一个SequenceNumber类的对象seq序列号。而其主要作用就是对key进行封装,让这个key带上序列号seq信息。

/*** @brief 将序列号信息s封装到user_key后面* * @param user_key key* @param s 序列号信息*/
LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) {size_t usize = user_key.size();size_t needed = usize + 13;  // A conservative estimatechar* dst;//空间够用if (needed <= sizeof(space_)) {dst = space_;} else {dst = new char[needed];}//dst = [Varint32](usize+8) + [string](user_key) + [Varint64]((s<<8)|ValueType)start_ = dst;//(usize+8)dst = EncodeVarint32(dst, usize + 8);kstart_ = dst;//dst = user_keystd::memcpy(dst, user_key.data(), usize);dst += usize;//(s<<8)|ValueTypeEncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek));dst += 8;end_ = dst;
}

而memtable本质上是一个跳表,所以这里再memtable上查找就是一个查找跳表的过程。查找到之后,由于跳表存储从value实际上是一个聚合怪,其格式如下:
在这里插入图片描述
我们的目标只是其中的value,所以这里的另一个主要工作就是反解析出这个value。

bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) {//取出封装好的keySlice memkey = key.memtable_key();Table::Iterator iter(&table_);//memtable 搜寻key,这里其实是跳表的查找过程iter.Seek(memkey.data());if (iter.Valid()) {//找到了,格式中反解析出key的value,赋值给value// entry format is://    klength  varint32//    userkey  char[klength]//    tag      uint64//    vlength  varint32//    value    char[vlength]const char* entry = iter.key();uint32_t key_length;const char* key_ptr = GetVarint32Ptr(entry, entry + 5, &key_length);if (comparator_.comparator.user_comparator()->Compare(Slice(key_ptr, key_length - 8), key.user_key()) == 0) {// Correct user keyconst uint64_t tag = DecodeFixed64(key_ptr + key_length - 8);switch (static_cast<ValueType>(tag & 0xff)) {case kTypeValue: {Slice v = GetLengthPrefixedSlice(key_ptr + key_length);value->assign(v.data(), v.size());return true;}case kTypeDeletion:*s = Status::NotFound(Slice());return true;}}}return false;
}

2) immutable查找

这一步和上一步其实一模一样,就不多bb了

3)SSTable查找

SSTable查找,表面上调用了Version::Get,实际上真正的逻辑在Version::ForEachOverlapping,其逻辑描述也跟我们刚刚说的查找过程一样:
在这里插入图片描述

void Version::ForEachOverlapping(Slice user_key, Slice internal_key, void* arg,bool (*func)(void*, int, FileMetaData*)) {const Comparator* ucmp = vset_->icmp_.user_comparator();//在第0层寻找std::vector<FileMetaData*> tmp;tmp.reserve(files_[0].size());for (uint32_t i = 0; i < files_[0].size(); i++) {FileMetaData* f = files_[0][i];//这里是通过FileMetaData里面存储的每一层最大key和最小key,通过对比就知道key在不在这个文件之中if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 &&ucmp->Compare(user_key, f->largest.user_key()) <= 0) {//符合条件就push_backtmp.push_back(f);}}//比较所有符合条件的if (!tmp.empty()) {//这里排序的目的是找到最新的,找到就返回,后面不找了std::sort(tmp.begin(), tmp.end(), NewestFirst);for (uint32_t i = 0; i < tmp.size(); i++) {//这里调用State::Match函数,每次都会把磁盘上的SSTable打开,加载到table_cache,之后查找if (!(*func)(arg, 0, tmp[i])) {return;}}}// Search other levels.for (int level = 1; level < config::kNumLevels; level++) {size_t num_files = files_[level].size();  //获取这一层的文件数量if (num_files == 0) continue;//FindFile这里采用二分查找,找到符合条件范围的keyuint32_t index = FindFile(vset_->icmp_, files_[level], internal_key);if (index < num_files) {FileMetaData* f = files_[level][index];if (ucmp->Compare(user_key, f->smallest.user_key()) < 0) {// All of "f" is past any data for user_key} else {//查找 返回if (!(*func)(arg, level, f)) {return;}}}}
}

参考文献

[1] LevelDB 原理解析:数据的读写与合并是怎样发生的?(在原文基础上增添内容)

这篇关于LevelDB 源码层次上看读取过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/439771

相关文章

Java读取InfluxDB数据库的方法详解

《Java读取InfluxDB数据库的方法详解》本文介绍基于Java语言,读取InfluxDB数据库的方法,包括读取InfluxDB的所有数据库,以及指定数据库中的measurement、field、... 首先,创建一个Java项目,用于撰写代码。接下来,配置所需要的依赖;这里我们就选择可用于与Infl

C#读取本地网络配置信息全攻略分享

《C#读取本地网络配置信息全攻略分享》在当今数字化时代,网络已深度融入我们生活与工作的方方面面,对于软件开发而言,掌握本地计算机的网络配置信息显得尤为关键,而在C#编程的世界里,我们又该如何巧妙地读取... 目录一、引言二、C# 读取本地网络配置信息的基础准备2.1 引入关键命名空间2.2 理解核心类与方法

最新版IDEA配置 Tomcat的详细过程

《最新版IDEA配置Tomcat的详细过程》本文介绍如何在IDEA中配置Tomcat服务器,并创建Web项目,首先检查Tomcat是否安装完成,然后在IDEA中创建Web项目并添加Web结构,接着,... 目录配置tomcat第一步,先给项目添加Web结构查看端口号配置tomcat    先检查自己的to

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

SpringBoot集成SOL链的详细过程

《SpringBoot集成SOL链的详细过程》Solanaj是一个用于与Solana区块链交互的Java库,它为Java开发者提供了一套功能丰富的API,使得在Java环境中可以轻松构建与Solana... 目录一、什么是solanaj?二、Pom依赖三、主要类3.1 RpcClient3.2 Public

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

SpringBoot整合kaptcha验证码过程(复制粘贴即可用)

《SpringBoot整合kaptcha验证码过程(复制粘贴即可用)》本文介绍了如何在SpringBoot项目中整合Kaptcha验证码实现,通过配置和编写相应的Controller、工具类以及前端页... 目录SpringBoot整合kaptcha验证码程序目录参考有两种方式在springboot中使用k

SpringBoot使用Apache POI库读取Excel文件的操作详解

《SpringBoot使用ApachePOI库读取Excel文件的操作详解》在日常开发中,我们经常需要处理Excel文件中的数据,无论是从数据库导入数据、处理数据报表,还是批量生成数据,都可能会遇到... 目录项目背景依赖导入读取Excel模板的实现代码实现代码解析ExcelDemoInfoDTO 数据传输

Python读取TIF文件的两种方法实现

《Python读取TIF文件的两种方法实现》本文主要介绍了Python读取TIF文件的两种方法实现,包括使用tifffile库和Pillow库逐帧读取TIFF文件,具有一定的参考价值,感兴趣的可以了解... 目录方法 1:使用 tifffile 逐帧读取安装 tifffile:逐帧读取代码:方法 2:使用

SpringBoot整合InfluxDB的详细过程

《SpringBoot整合InfluxDB的详细过程》InfluxDB是一个开源的时间序列数据库,由Go语言编写,适用于存储和查询按时间顺序产生的数据,它具有高效的数据存储和查询机制,支持高并发写入和... 目录一、简单介绍InfluxDB是什么?1、主要特点2、应用场景二、使用步骤1、集成原生的Influ