leveldb源码剖析---缓存系统

2023-12-16 16:38

本文主要是介绍leveldb源码剖析---缓存系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

通过前面的分析可以知道,leveldb为了提高写的性能,牺牲了部分的读性能。最差的情况可能需要遍历各个level中的每个文件。为了缓解读性能,leveldb引入了缓存机制,当然,版本信息中包含各个level的文件元信息在一定程度上也可以提高读性能。


leveldb提供的缓存系统的底层数据结构是一个开链哈希

class ShardedLRUCache : public Cache :LRUCache shard_[kNumShards];
  • 1
  • 2
  • 3

它是一个数组,其中数组中的每个元素包含两个链表,和一个用于加速链表元素查找的哈希表 table_。

class LRUCache:....LRUHandle lru_;LRUHandle in_use_; HandleTable table_;....
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

链表中的每个元素就表示一个缓存单元–LRUHandle

LRUHandle :void* value;char key_data[1];....LRUHandle* next_hash; //这个指针是用来将LRUHandle链到hashtable中的LRUHandle* next;LRUHandle* prev;...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里我们只关注这一部分元素。首先是链表元素的主要内容,我们可以看到,每个链表元素都是一个key-value形式的数据

1. 其中key 为对应sstable的file_number,通过它可以打开磁盘上的其所指向的sstable文件。
2. value为TableAndFile,里面包含这个sstable文件的许多元信息,包括 index_block,metadataindex_block信息等。

这里需要提一下,前面在分析版本控制时,可以看到每个版本都会包含所有level的各个文件的元信息,需要注意的是,版本信息里里面的元信息为FileMetaData,和它相比,TableAndFile包含有文件的更多信息。


这里我们简单理一下FileMetaData和TableAndFile是怎么互相帮助提高读性能的:
当用户调用leveldb的get函数,期望获得对应key的value时
1. leveldb先从版本信息中检查各个level中的各个文件的FileMetaData,查看这个key是否有可能存在于对应的sstable中
2. 如果有可能存在在这个sstable中,则根据这个sstable文件的FileMetaData提供的file_number在缓存中找到对应的TableAndFile;当然,如果缓存中没有这个sstable的TableAndFile,则从磁盘中将其读进来。
3. 进一步根据TableAndFile中的index_block,metadata_block等信息确定该key是否在这个sstable中。

具体的细节可以从

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

函数的实现中找到


下面我们继续分析缓存系统:

除了存储缓存具体数据的元素之外,缓存链表中还包括链表组织的数据
next,prev表示这是一个双向链表。它将缓存系统中的所有sstable缓存元数据组织了起来。前面讲到,为了方便缓存查找,leveldb使用了开链哈希。但是开链哈希也有可能存在问题,比如如果开链数组中单个元素指向的链表过长,则对这个链表的线性扫描还是会非常耗费时间。为了解决这个问题,leveldb在每个链表上再建立了一层哈希。这就是结构中next_hash的作用。因此leveldb利用了二维哈希来缓解了对于开链哈希可能存在的单个链表过长的问题。

如果我们深入看HandleTable的代码,就会发现,它也是一个开链哈希。通过next_hash将元素链在这个哈希表中。

最后,缓存系统中有两条链表 :in_lru_和in_use_。

作用是显而易见的。我们知道链表中的每个元素(LRUHandle)用

 uint32_t refs; 
  • 1

来做为引用计数标记该元素目前正在被多少个用户使用,显然如果refs>0,这个元素是不能从缓存中被删除的。这里需要注意的是,缓存系统本身也是这个元素的一个用户,因此从 Cache::Handle* LRUCache::Insert函数中可以看到,每个插入新插入的元素的refs都为2,表示它有两个用户,分别是
1. cache系统本身
2. 调用insert的用户

那很自然的一个问题是这个元素什么时候才被删除呢?自然是它的用户数为0的时候。当一个元素的引用计数为1时,说明它只有cache系统一个用户,此时,将它移动到lru_链表中,在这个链表中的元素随时可以被删除掉,因为没有上层用户对它使用了。它的真正删除发生在缓存系统的容量满载时,判断满载自然也是在 LRUCache::Insert函数中。当元素只cache一个用户时,则放在lru_链表中,当缓存满载时,从lru_链表中删除。如果有上层用户在使用这个元素,则将它放在in_use_链表中。缓存系统的负载是使用

 size_t usage_;
  • 1

来记录的。缓存系统的容量是用

 size_t capacity_;
  • 1

记录的。

至此leveldb的缓存系统的主要部分就讲完了。


总结

leveldb引入了缓存系统来改善它的读性能。缓存系统本质上是将最近读过的sstable文件的元信息(TableAndFile)放在内存中,以避免每次读取数据时都要直接读盘。在读取某个给定key的value时,leveldb首先通过版本信息找到可能包含该key的文件的file_number,然后通过这个file_number找到缓存系统中保存的更加完整的文件元信息。如果缓存系统中没有这个file_number对应的元素,则需要读盘,并在内存中创建对应这个sstable文件的TableAndFile,并插入缓存中。

缓存系统是通过二维开链哈希将缓存中所有的sstable的元信息组织起来的。并利用引用计数设计了二层的缓存,将暂时不用的元素也依旧缓存在系统中,只有负载过高时才真正删除。当然为了避免同步问题,对缓存元素的链表操作必须在临界区内。

这篇关于leveldb源码剖析---缓存系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用

Linux系统之dns域名解析全过程

《Linux系统之dns域名解析全过程》:本文主要介绍Linux系统之dns域名解析全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、dns域名解析介绍1、DNS核心概念1.1 区域 zone1.2 记录 record二、DNS服务的配置1、正向解析的配置

Linux修改pip和conda缓存路径的几种方法

《Linux修改pip和conda缓存路径的几种方法》在Python生态中,pip和conda是两种常见的软件包管理工具,它们在安装、更新和卸载软件包时都会使用缓存来提高效率,适当地修改它们的缓存路径... 目录一、pip 和 conda 的缓存机制1. pip 的缓存机制默认缓存路径2. conda 的缓

Redis解决缓存击穿问题的两种方法

《Redis解决缓存击穿问题的两种方法》缓存击穿问题也叫热点Key问题,就是⼀个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击,本文给大家介绍了Re... 目录引言解决办法互斥锁(强一致,性能差)逻辑过期(高可用,性能优)设计逻辑过期时间引言缓存击穿:给

Linux系统中配置静态IP地址的详细步骤

《Linux系统中配置静态IP地址的详细步骤》本文详细介绍了在Linux系统中配置静态IP地址的五个步骤,包括打开终端、编辑网络配置文件、配置IP地址、保存并重启网络服务,这对于系统管理员和新手都极具... 目录步骤一:打开终端步骤二:编辑网络配置文件步骤三:配置静态IP地址步骤四:保存并关闭文件步骤五:重

Spring 中 BeanFactoryPostProcessor 的作用和示例源码分析

《Spring中BeanFactoryPostProcessor的作用和示例源码分析》Spring的BeanFactoryPostProcessor是容器初始化的扩展接口,允许在Bean实例化前... 目录一、概览1. 核心定位2. 核心功能详解3. 关键特性二、Spring 内置的 BeanFactory

Windows系统下如何查找JDK的安装路径

《Windows系统下如何查找JDK的安装路径》:本文主要介绍Windows系统下如何查找JDK的安装路径,文中介绍了三种方法,分别是通过命令行检查、使用verbose选项查找jre目录、以及查看... 目录一、确认是否安装了JDK二、查找路径三、另外一种方式如果很久之前安装了JDK,或者在别人的电脑上,想