CacheLib 原理说明

news2024/11/29 4:49:49

CacheLib 介绍

CacheLib 是 facebook 开源的一个用于访问和管理缓存数据的 C++ 库。它是一个线程安全的 API,使开发人员能够构建和自定义可扩展的并发缓存。

主要功能:

  1. 实现了针对 DRAM 和 NVM 的混合缓存,可以将从 DRAM 驱逐的缓存数据持久化到 NVM,存于 NVM 上的缓存数据在被查找命中时会写到DRAM缓存,这些对用户都是透明的。
  2. 支持缓存数据的持久化,使进程在重新启动后依然可以保持原有缓存数据不丢失。
  3. 缓存可变大小的对象。
  4. 提供零拷贝的并发访问。
  5. 提供丰富的缓存算法,如 LRU、分段 LRU、FIFO、2Q 和 TTL。
  6. 使用硬 RSS 内存限制以避免 OOM。
  7. 智能和自动调整缓存以动态更改工作负载。

下面挑选在上述功能中挑选几个重要的着重说明一下。

CacheLib 混合缓存

CacheLib 通过混合缓存功能支持使用多种硬件介质。为了使这对用户透明,提供了统一的 CacheAllocator API 封装,让用户对具体的硬盘读写无感知。CacheLib 支持 SSD 作为混合缓存的 NVM 介质。Navy 是基于 SSD 优化的缓存引擎。为了与 Navy 交互,CacheAllocator 针对 NVM 的操作包装为NvmCache。CacheAllocator 将 Navy的查找、插入和删除的功能完全交给 NvmCache 来实现,并保证其是线程安全的。 NvmCache 实现处理 Item 与 NVM 之间的转换的功能。CacheAllocator 中没有全局键级锁来保证 DRAM 和 NVM 之间的同步,因此 NvmCache 采用乐观并发控制原语来保证数据的正确性。

前置知识:

CacheLib 支持的缓存特性

CacheTrait 是 MMType、AccessType 和 AccessTypeLock 的组合。

MMType:内存管理类型,它控制缓存项的生命周期(add/remove/evict/recordAccess),类型包括 MMLru、MM2Q、MMTinyLFU。

AccessType:访问控制类型,它控制缓存项的访问方式(find/insert/remove),类型包括 ChainedHashTable。

AccessTypeLock:是支持多个锁定原语的访问容器的锁类型,类型包括 SharedMutexBuckets、SpinBuckets。

RefcountWithFlags

记录缓存项的引用计数和标识符

|--------18 bits----------|--3 bits---|------11 bits------|
┌─────────────────────────┬───────────┬───────────────────┐
│         Access Ref      │ Admin Ref │       Flags       │
└─────────────────────────┴───────────┴───────────────────┘

Access Ref:记录缓存项的引用计数

Admin Ref:记录谁有权限管理缓存项,如 kLinked(MMContainer)、kAccessible(AccessContainer)、kMoving。

Flags:指示缓存项当前的状态,如 kIsChainedItem、kHasChainedItem、kNvmClean、kNvmEvicted、 kUnevictable_NOOP。

混合缓存初始化

在创建 Cache 实例时都是通过 CacheAllocator 来实例化,这里分为两种方式,一种是不支持缓存持久化的启动方式,另一种是支持缓存持久化的启动方式。支持持久化的方式比不支持持优化的方式会多一个入参 SharedMemNewT 或 SharedMemAttachT。

SharedMemNewT 会使 Cache 多一个 shmManager,利用 posix shm 来做缓存的持久化,一般会生成两个文件:metadata 和NvmCacheState,具体可以参考 ShmManager.h 和NvmCacheState.h。metadata 会存储内存相关的元信息包括 shm_info、shm_cache、shm_hash_table 和 shm_chained_alloc_hash_table;NvmCacheState 会存储 NVM 的状态信息,因为 NVM 的元信息已经在缓存文件中记录,不需要在额外持久化。

SharedMemAttachT 会在启动时尝试 attach 之前持久化的元数据,如果成功可以继续使用存在的缓存,如果失败会抛出异常,所以一般的用法如下:

 try 
 {
     cache = std::make_unique<Cache>(Cache::SharedMemAttach, config);
 } 
 catch (const std::exception& ex) 
 {
     cache.reset();
     std::cout << "Couldn't attach to cache: " << ex.what() << std::endl;
     cache = std::make_unique<Cache>(Cache::SharedMemNew, config);
     default_pool = global_cache->addPool("default", 1024*1024*1024);
 }

Item 分配和淘汰

缓存在写入 CacheLib 时,会先写入 DRAM 中分配内存并将数据写入,写入后交由 MMContainer(缓存管理容器)进行管理。

insertInMMContainer(*(handle.getInternal()));

当 Item 满足淘汰策略要被淘汰时,有两种途径:一是直接从 CacheLib 中淘汰掉;二是从内存中淘汰掉进入 NVM。

因为 NVM 为了保证时刻都有着良好的性能,支持定制 Admission Policy,比如突然有过多的 kv 写入时可以按百分比拒绝掉部分 kv 写入请求。这样可以起到保护 NVM 的作用,同时在 CacheLib 淘汰的 key 就会直接被淘汰,不会写入 NVM。

当不满足阈值条件时,Item 就会被写入 NVM,如果 NVM 空间被占满同样也会触发淘汰机制。

这个 allocation 过程也就是insert的核心逻辑,对应 insert 还有一个实现是 insertOrReplace,insert 在写入时会因为有重复 key 导致写入失败,insertOrReplace 则会直接替换原先存在的 key,具体的实现就是在 check 后和写入前,将 NVM 做标记删除,具体可以参考createDeleteTombStone 函数。到此也引出一个逻辑就是,NVM 中的数据都是标记删除,并不会直接清空这部分数据,而是后续通过调用 removeAsync 完成异步删除。

在这里插入图片描述

混合缓存查找

在查找 key 时首先在内存中进行查找,为此 CacheLib 专门设计了下面这个函数,在找到 key 后会判断 key 是否过期,过期会标记为过期,并返回未找到。

auto handle = findFastImpl(key, mode);

如果内存中不存在,会尝试从 NVM 中查找,find 有同步的也有异步的实现,但 NVM 一般都异步的 find,性能会更好。

NVM 的 find 设计稍微有些复杂,首先为了避免磁盘的繁重搜索,加入了 enableFastNegativeLookups 功能,可以预先判断 Item 在内存中是不是肯定不存在。如果不确定存不存在,再进行 NVM 上的查找,其实这里就是一个 bloom 过滤器。

因为内存和磁盘的交互时间会比较长,从内存中驱逐到磁盘的过程(写 NVM )就会相对比较长,所以为此设计了异步查找功能,通过 GetContext 来追踪整个查找过程,最后使用回调函数来通知通知用户查找完成。

在查找过程中有可能会有多个 find 并发请求,CacheAllocator 为了性能考虑,并没有使用真正意义的锁,而是利用乐观所机制来保证 DARM 和 NVM 中数据的一致性。NvmCache 是通过维护一个 PutTokens 结构来实现的,每个正在写入 NVM 的 key 都有一个属于自己的 putToken,只有在 putToken 有效的情况下才能执行 NVM 的写入,以此来控制 key 的可见性。

举个例子,当线程1有一个 find 请求要查找 key1,此时有可能线程2正在执行 key1 的 NVM 写入,key1 会持有一个有效的 putToken,当线程1执行 find 时,key1 就会被 LRU 等规则激活,就不需要被内存淘汰,也不再需要写入 NVM,所以会将 key1 的 putToken 置为无效,线程2的写入操作发现 putToken 无效则不再继续写操作,这样可以保证从 DRAM 到 NVM 查找的顺序一致性。这里的 putToken 只有在写入 NVM 时才会生成,在 find 和 eviction 等操作时会被置为无效,并没有很复杂的状态变更。

在 NVM 查找完成后,Item 并不是直接返回给用户,而是先写入到 DRAM,同 allocation 逻辑,此时通过 nvmClean(true) flag 标识 DRAM 和 NVM 中该 kv 是一致的,如果 Item 再次被淘汰,则不需要再次写入 NVM,因为 NVM 已经存在该 key。如果 key 所对应的 value 被修改,nvmClean 会被置为 false,标识为不一致。NVM 中这部分不一致的会被后台异步线程清理掉。

在这里插入图片描述

Navy实现原理

Navy 是基于 SSD 优化的缓存引擎。Navy 的特点:

  1. 高效缓存 SSD 上的数十亿个小对象 (<1KB) 和数百万个大对象 (1KB - 16MB)。
  2. 高效的点查找
  3. 低 DRAM 开销

由于 Navy 是为缓存而设计的,因此在实现上选择牺牲数据的持久性。由于 Navy 是写密型的,NVM 写入寿命就成为一个关注点,Navy 也针对写入寿命进行了优化。

Navy整理架构

Navy 为用户提供了一个异步 API。Navy 使用 Small Item Engine 对小对象进行优化,使用 Large Item Engine 对大对象进行优化。每个引擎的设计都考虑了所需的DRAM开销,而不会影响读取效率。在下面,做了块设备的抽象,两个引擎都在块设备抽象之上运行。

在这里插入图片描述

Engine

Navy 针对 Item 的大小分别实现了 Small Item Engine 和 Large Item Engine,对应的具体实现分别是 BigHash 和 BlockCache,可以通过 Item 的大小控制 Item 是存储到 Small Item Engine 还是 Large Item Engine,在查询时会优先查询 Large Item Engine,如果不存在,再去查询 Small Item Engine。一般 Small Item Engine 都是存储 1K 以内的数据,大数量的缓存一般都是使用 Large Item Engine,Small Item Engine 也是可以通过配置禁用的。如果因为异常终止了还会进行重试。

在这里插入图片描述

BigHash 主要用于小对象,暂不做过多介绍。

BlockCache

Large Item Engine 的具体实现就是 BlockCache,下面是 BlockCache 的具体结构:

在这里插入图片描述

主要读写逻辑

  1. 根据 ssd 配置 deviceMaxWriteSize 和 blockCacheRegionSize(默认16MB)将 Device 分为多个 Region,并存入 CleanList。

  2. 写入流程:

    选择一个 Region,如果 Region 还未填满,则向该 Region 追加 Entry,如果当前 Region 已满,会向 CleanList 获取一个 Region。若 CleanList 为空,需要等待 eviction 和 gc 来释放 Region,最后更新 IndexMap。

  3. 查询流程:

    根据 key 找到 RegionId 和 offset,再通过 RegionId 和 offset 定位到 Entry,解析读取数据。

  4. 删除流程:

    只从 IndexMap 中删除,Entry 本身会被标记删除,等待 gc 异步处理。

  5. 淘汰流程:

    根据淘汰策略进行淘汰,策略有 FIFO、LRU、LRU2Q,淘汰是以 Region 为单位进行淘汰,如果觉得粒度太大,可以配置 reinsertion 根据命中率或频率进行重插入来保留。

Job调度

这里的 job 包含4类;

JobType::Read:对于对应于 Navy 读取作业(查找);

JobType::Write:对应于 Navy 写入作业,新的插入和删除都是以插入形式实现的;

JobType::Reclaim:执行内部淘汰操作;

JobType::Flush:执行任何内部异步缓冲写入;

Job是严格按顺序提交,保证写入和查询的顺序一致性,避免对同一个 key 同时执行多个操作。再遇到并发请求时,还是依赖NvmCache的乐观锁机制保证 DRAM 和 NVM 的数据一致性。

JobScheduler 提供两个线程池,一个负责读,另一个负责写。Job 会根据类型分配到两个线程池其中之一。并且在根据 key 分配到指定的线程上,每个线程都维护着一个 JobQueue,一般都是先入先出的,但是 JobType::Reclaim 和 JobType::Flush 会有更高的优先级,会优先执行。

为了使用线程池提高性能还要维持顺序一致性,引入了一个分片排序机制,首先通过配置参数可以指定分片的数量,一般为数百万个,提交的 Job 会根据 key hash 到不同的分片内,同一个 key 只会在一个分片内,保证对同一个 key 处理的顺序性,分配请求会从第一个分片开始遍历,一般一次只取一个 Job,如果连续的 Job 是不同的 key 没有冲突,可以一次取多个提交到线程池线程的 JobQueue 中,遍历到结尾处再次循环遍历,不断的完成 Job。

在这里插入图片描述

以上就是对 CacheLib 混合缓存原理的介绍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/331241.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ESP8266 + STC15基于AT指令通过TCP通讯协议控制IO状态

ESP8266 + STC15基于AT指令通过TCP通讯协议控制IO状态 📌ESP8266 AT固件基于安信可AT固件,相关刷AT固件可以参考《NodeMCU-刷写AT固件》当然WIFI模块也可以是esp01. STC15 单片机采用的是:STC15F2K60S2 晶振频率采用内部:22.1184MHz🌼功能介绍 通过电脑端的网络调试助手…

【C++】类和对象(第一篇)

文章目录1. 面向过程和面向对象初步认识2.类的引入3.类的定义3.1 类的两种定义方式3.2 成员变量命名规则建议4. 类的访问限定符及封装4.1 访问限定符4.2 封装5. 类的作用域6. 类的实例化7. 类对象模型7.1 类对象大小的计算7.2 类对象的存储方式猜测7.3 结构体内存对齐规则复习8…

JDK15 新特性详解,2020-09-15 正式发布

预览版&#xff1a;该功能在当前版本可以使用&#xff0c;如果效果不是很好的话&#xff0c;可能以后的其他版本就会删去该功能。 最终版&#xff1a;该功能在之前版本效果很好&#xff0c;之后的每个版本中都会存在该功能。 Java 5 中的泛型&#xff0c;Java 8 中的 Lambda …

Linux云服务器下怎么重置MySQL8.0数据库密码

文章目录一、修改my.cnf配置文件为mysql免登陆二、免密登陆mysql三.给root用户重置密码1、首先查看当前root用户相关信息&#xff0c;在mysql数据库的user表中2、把root密码置为空3、退出mysql&#xff0c;删除/etc/my.cnf文件中添加进去的skip-grant-tables 重启mysql服务4、使…

FPGA实现AD9708和AD9280波形收发输出HDMI模拟示波器,串口协议帧控制显示,提供工程源码和技术支持

目录1、AD9708芯片解读和电路设计2、AD9280芯片解读和电路设计3、FPGA设计框架4、AD9708波形生成并发送5、AD9280采集接收波形6、HDMI波形显示算法7、串口协议帧控制波形显示8、vivado工程9、上板调试验证10、福利&#xff1a;工程源码获取1、AD9708芯片解读和电路设计 AD9708…

85024A是德科技keysight高频探头

附加功能&#xff1a; 易于执行在线测量出色的频率响应和单位增益探头灵敏度高低失真度规格输入电容&#xff08;在 500 MHz 时&#xff09;&#xff1a;< 0.7pF&#xff08;标称值&#xff09;输入电阻&#xff1a;1 MΩ&#xff08;标称值&#xff09;带宽&#xff1a;30…

2月8日刷题总结

写题一点思路也没有&#xff0c;题解也不能看得很懂。所以系统性的学习DP ing……跟着进度来&#xff0c;因此刷了一些已经刷过的类型的题&#xff08;也算再次熟悉一下&#xff09;P1077 [NOIP2012 普及组] 摆花题目描述小明的花店新开张&#xff0c;为了吸引顾客&#xff0c;…

力扣SQL刷题5

目录597. 好友申请 I&#xff1a;总体通过率602. 好友申请 II &#xff1a;谁有最多的好友603. 连续空余座位1045. 买下所有产品的客户597. 好友申请 I&#xff1a;总体通过率 官方讲的题目太繁琐了&#xff0c;大概就是&#xff08;表2中列1列2不全相同的行数&#xff09;/&a…

测试开发之Vue学习笔记-Vue路由

Vue路由18. Vue-路由基础安装 cnpm install --save vue-router官方文档&#xff1a;https://router.vuejs.org/zh/src/main.js中&#xff08;1&#xff09;引入VueRouter&#xff1a;import VueRouter from "vue-router"&#xff08;2&#xff09;使用VueRouter&…

《知行合一王阳明》读书笔记

《知行合一王阳明》用通俗易懂的语言介绍了王阳明一生的传奇经历和他的心学的核心思想。这篇读后感主要介绍一下我对心学的理解。在我看来&#xff0c;心学最本质的要求是“致良知”&#xff0c;最核心的方法论是“知行合一”。致良知是说要遵从自己的本心。王阳明相信人性本善…

(蓝桥杯 刷题全集)【备战(蓝桥杯)算法竞赛-第1天(基础算法-上 专题)】( 从头开始重新做题,记录备战竞赛路上的每一道题 )距离蓝桥杯还有75天

&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6; 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&a…

兔年新佳绩,思迈特软件获奖喜讯纷至沓来

近年来&#xff0c;随着大数据、云计算、人工智能、5G等技术加速创新&#xff0c;越来越多的ToB企业开始下沉探索边际&#xff0c;纵深业务服务场景&#xff0c;通过技术与行业的深度融合&#xff0c;为客户提供全面的转型服务&#xff0c;尽一切可能创造客户价值和商业价值。思…

【C++: list的模拟实现】

目录 1 list的简单回顾 2 类中成员变量的声明 3 __list_iterator 中运算符重载 4 list中的迭代器 5 list中增删查改以及clear 6 const迭代器 6.1 __list_iterator的重新实现 6.2 list类的巧妙修改 7 构造函数&&拷贝构造&&赋值运算符重载 8 反向迭代器…

金三银四丨黑蛋老师带你剖析-二进制漏洞

作者&#xff1a;黑蛋二进制漏洞岗上篇文章我们初步了解了一下简历投递方式以及二进制方向相关逆向岗位的要求&#xff0c;今天我们就来看看二进制漏洞相关的岗位&#xff0c;当然&#xff0c;漏洞岗位除了分不同平台&#xff0c;也有漏洞挖掘岗和漏洞分析利用岗。同样&#xf…

[人工智能-综述-11]:ChatGPT, 通用人工智能还是要来了

该来的还是要来的&#xff01;补充信息&#xff1a;ChatGPT是由人工智能研究实验室OpenAI在2022年11月30日发布的全新聊天机器人模型&#xff0c;一款人工智能技术驱动的自然语言处理工具。它能够通过学习和理解人类的语言来进行对话&#xff0c;还能根据聊天的上下文进行互动&…

C语言共用体(C语言union用法)详解

我们知道结构体&#xff08;Struct&#xff09;是一种构造类型或复杂类型&#xff0c;它可以包含多个类型不同的成员。在C语言中&#xff0c;还有另外一种和结构体非常类似的语法&#xff0c;叫做共用体&#xff08;Union&#xff09;&#xff0c;它的定义格式为&#xff1a;un…

STM32的HAL库分析及使用

STM32的HAL库分析及使用 STM32的三种开发方式 通常新手在入门STM32的时候&#xff0c;首先都要先选择一种要用的开发方式&#xff0c;不同的开发方式会导致你编程的架构是完全不一样的。一般大多数都会选用标准库和HAL库&#xff0c;而极少部分人会通过直接配置寄存器进行开发…

【Mysql第八期 子查询】

文章目录前言1. 需求分析与问题解决1.2 子查询的基本使用1.3 子查询的分类2. 单行子查询2.1 单行比较操作符2.2 代码示例2.5 子查询中的空值问题3. 多行子查询3.1 多行比较操作符3.2 代码示例3.3 空值问题4. 相关子查询4.2 代码示例4.3 EXISTS 与 NOT EXISTS关键字4.4 相关更新…

开发者社区「运营官」招募启动啦!

国内首个聚焦AI3D视觉技术的开发者社区「运营官」招募启动啦&#xff01; 想积累实习经验&#xff0c;却苦于找不到大厂机会&#xff1f; 想进入AI3D视觉行业&#xff0c;却苦于没有知音伯乐&#xff1f; 想积累更多工作经历&#xff0c;却苦于路程奔波、天各一方&#xff1f…

我们的微服务中为什么需要网关?

说起 Spring Cloud Gateway 的使用场景&#xff0c;我相信很多小伙伴都能够脱口而出认证二字&#xff0c;确实&#xff0c;在网关中完成认证操作&#xff0c;确实是 Gateway 的重要使用场景之一&#xff0c;然而并不是唯一的使用场景。在微服务中使用网关的好处可太多了&#x…