当前位置: 首页 > news >正文

大白话解析LevelDB:ShardedLRUCache

文章目录

    • Cache 接口定义
    • ShardedLRUCache 的实现
      • ShardedLRUCache 的构造函数
      • ShardedLRUCache::Insert(const Slice& key, void* value, size_t charge, void (\*deleter)(const Slice& key, void* value))
      • ShardedLRUCache::Lookup(const Slice& key)
      • ShardedLRUCache::Release(Handle* handle)
      • ShardLRUCache::Erase(const Slice& key)
      • ShardedLRUCache::Value(Handle* handle)
      • ShardedLRUCache::NewId()
      • ShardedLRUCache::Prune()
      • ShardedLRUCache::TotalCharge()
    • 总结

ShardedLRUCacheCache的一种实现,所以在看ShardedLRUCache的实现之前,我们需要先了解下Cache的定义。

Cache 接口定义

我们可以先看下Cache的使用姿势,再来看Cache的接口定义。

Cache* cache = NewLRUCache(100);
Handle* handle = cache->Insert("key", "value", 5, nullptr);
cache->Erase("key"); // 即使这里将缓存项从 Cache 中移除了,但该缓存项还是会保留在内存中
cache->Value(handle); // 此时还是可以通过 handle 获取到缓存项的 Value
cache->Release(handle); // 这里将缓存项的引用计数减一,如果引用计数为 0,那么该缓存项会被销毁。

Cache的接口定义如下:

class LEVELDB_EXPORT Cache {public:Cache() = default;Cache(const Cache&) = delete;Cache& operator=(const Cache&) = delete;// 用户调用 Insert() 方法插入一个缓存项时,会同时传入一个 deleter。// 当 Cache 析构时,会调用所有缓存项的 deleter 来对 Cache 里的// 所有缓存项进行清理。virtual ~Cache();// 声明了一个抽象的 Handle 类型,用于表示 Cache 中的一个缓存项。struct Handle {};// 这个方法用于向 Cache 中插入一对 Key-Value,Cache 的实现类会在内部// 基于这对 Key-Value 生成一个 Handle 对象,将该 Handle 插入到 Cache// 中,并把这个 Handle 的指针返回给用户。//// 返回 Handle 指针,相当于这个 Cache 缓存项的引用计数加一,即使另外一个线程// 将这个缓存项从 Cache 中移除了,该缓存项还是会保留在内存中。// 用户需要在使用完这个缓存项后,调用 Release() 方法来将该缓存项的引用计数减一。// 可以通过以下例子来理解://      Cache* cache = NewLRUCache(100);//      Handle* handle = cache->Insert("key", "value", 5, nullptr);//      cache->Erase("key"); // 即使这里将缓存项从 Cache 中移除了,但该缓存项还是会保留在内存中//      cache->Value(handle); // 此时还是可以通过 handle 获取到缓存项的 Value//      cache->Release(handle); // 这里将缓存项的引用计数减一,如果引用计数为 0,那么该缓存项会被销毁。//// charge 参数表示这个缓存项的大小,因为缓存项里只存储了 value 的指针,所以计算出// 该缓存项的大小,需要用户告知 Cache。virtual Handle* Insert(const Slice& key, void* value, size_t charge,void (*deleter)(const Slice& key, void* value)) = 0;// 通过 key 查找 Cache 中的缓存项://      - 如果找到了,返回该缓存项的 Handle 指针。//      - 同时将该缓存项的引用计数加一。如果没有找到,返回 nullptr。virtual Handle* Lookup(const Slice& key) = 0;// 将缓存项的引用计数减一,如果引用计数为 0,那么该缓存项会被销毁。virtual void Release(Handle* handle) = 0;// 返回缓存项的 Value。virtual void* Value(Handle* handle) = 0;// 指定一个 key,从 Cache 中移除一个缓存项。// 如果该缓存项的引用计数为 0,那么该缓存项会被销毁。virtual void Erase(const Slice& key) = 0;// 返回一个新的数字,作为该 Cache 的 ID。virtual uint64_t NewId() = 0;// 移除 Cache 中所有没有正在被使用的缓存项。// 比如在一些内存紧张的情况下,客户端可能会希望把 Cache 里没有正在被使用的缓存项移除,// 腾出一些内存空间。virtual void Prune() {}// 计算 Cache 中所有缓存项的大小之和。virtual size_t TotalCharge() const = 0;
};

ShardedLRUCache 的实现

ShardedLRUCache 的构造函数

ShardedLRUCache内部有一个LRUCache数组,LRUCache shard_[kNumShards]

意思是ShardedLRUCache不是一个大Cache,而是将这个大的Cache shard 为多个小Cache,每个小Cache叫做一个shard

所以叫做ShardedLRUCache

// capacity: ShardedLRUCache 的总容量
// 根据总容量计算每个 shard 里的 LRUCache 的容量。
explicit ShardedLRUCache(size_t capacity) : last_id_(0) {// 计算 per_shard: 每个 shard 的容量。// per_shard = ⌈capacity / kNumShards⌉ (向上取整)const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards;for (int s = 0; s < kNumShards; s++) {// 给每个 shard 里的 LRUCache 设置容量。shard_[s].SetCapacity(per_shard);}
}

ShardedLRUCache::Insert(const Slice& key, void* value, size_t charge, void (*deleter)(const Slice& key, void* value))

计算keyhash值,然后根据hash值选择一个shard,将key插入到该shardLRUCache中。

Handle* Insert(const Slice& key, void* value, size_t charge,void (*deleter)(const Slice& key, void* value)) override {// 计算 key 的 hash 值,然后根据 hash 值选择一个 shard,// 将 key 插入到该 shard 的 LRUCache 中。const uint32_t hash = HashSlice(key);return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter);
}

HashSlice(key)的实现如下:

static inline uint32_t HashSlice(const Slice& s) { return Hash(s.data(), s.size(), 0); }

继续看Hash(s.data(), s.size(), 0)的实现:

LevelDB 在MurMurHash 算法的基础上做了一点修改,主要是为了提高性能。

相比于其他 Hash 算法, MurmurHash 对于规律性较强的 Key,随机分布特征表现更良好。

uint32_t Hash(const char* data, size_t n, uint32_t seed) {// Similar to murmur hashconst uint32_t m = 0xc6a4a793;const uint32_t r = 24;const char* limit = data + n;uint32_t h = seed ^ (n * m);// Pick up four bytes at a timewhile (data + 4 <= limit) {uint32_t w = DecodeFixed32(data);data += 4;h += w;h *= m;h ^= (h >> 16);}// Pick up remaining bytesswitch (limit - data) {case 3:h += static_cast<uint8_t>(data[2]) << 16;FALLTHROUGH_INTENDED;case 2:h += static_cast<uint8_t>(data[1]) << 8;FALLTHROUGH_INTENDED;case 1:h += static_cast<uint8_t>(data[0]);h *= m;h ^= (h >> r);break;}return h;
}

LRUCache::Insert(key, hash, value, charge, deleter)的实现可移步参考大白话解析LevelDB:LRUCache;

ShardedLRUCache::Lookup(const Slice& key)

Handle* Lookup(const Slice& key) override {// 使用与 Insert 相同的 Hash 算法计算 key 的 hash 值,// 找到对应的 shard,然后在该 shard 的 LRUCache 中查找 key。const uint32_t hash = HashSlice(key);return shard_[Shard(hash)].Lookup(key, hash);
}

ShardedLRUCache::Release(Handle* handle)

void Release(Handle* handle) override {// Handle 中已经存好 hash 值了,不需要重新计算。// 找到对应的 shard,然后让该 shard 的 LRUCache 释放 handle。LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);shard_[Shard(h->hash)].Release(handle);
}

我们可以移步到大白话解析LevelDB:LRUCache看下LRUHandle的定义,LRUCache::Insert(key, hash, value, charge, deleter)里会将keyhash生成一个LRUHandle缓存项,该缓存项里存储了非常多的信息。

所以只要拿到handle,就可以直接读取出该缓存项的hash值了,不需要重新计算。

ShardLRUCache::Erase(const Slice& key)

void Erase(const Slice& key) override {// 使用与 Insert 相同的 Hash 算法计算 key 的 hash 值,// 找到对应的 shard,然后让该 shard 的 LRUCache 移除 key。const uint32_t hash = HashSlice(key);shard_[Shard(hash)].Erase(key, hash);
}

ShardedLRUCache::Value(Handle* handle)

handle里已经存储了value,从handle中直接获取即可。

void* Value(Handle* handle) override { return reinterpret_cast<LRUHandle*>(handle)->value; }

ShardedLRUCache::NewId()

返回一个新的数字,作为该 Cache 的 ID。

uint64_t NewId() override {MutexLock l(&id_mutex_);return ++(last_id_);
}

ShardedLRUCache::Prune()

没啥好说的,挨个调用每个 shard 里LRUCacheLRUCache::Prune()

void Prune() override {for (int s = 0; s < kNumShards; s++) {shard_[s].Prune();}
}

ShardedLRUCache::TotalCharge()

没啥好说的,挨个调用每个 shard 里LRUCacheLRUCache::TotalCharge()

size_t TotalCharge() const override {size_t total = 0;for (int s = 0; s < kNumShards; s++) {total += shard_[s].TotalCharge();}return total;
}

总结

可以看到,ShardedLRUCache只是一个LRUCache的封装,包含多个LRUCache shard。

插入、查找、删除等操作都是基于LRUCache的操作,只是在操作之前,需要先计算出keyhash值,然后根据hash值选择一个shard,再在该shardLRUCache中进行操作。

接下来我们移步看下LRUCache的实现: 大白话解析LevelDB:LRUCache。

相关文章:

大白话解析LevelDB:ShardedLRUCache

文章目录 Cache 接口定义ShardedLRUCache 的实现ShardedLRUCache 的构造函数ShardedLRUCache::Insert(const Slice& key, void* value, size_t charge, void (\*deleter)(const Slice& key, void* value))ShardedLRUCache::Lookup(const Slice& key)ShardedLRUCach…...

GDOI2024游记

Day0 中午一点钟从学校出发去东莞&#xff0c;大概坐了一个多小时车&#xff0c;两点半多到酒店。住的八方精选酒店&#xff08;ljh说他们住九方精选酒店&#xff0c;乐&#xff09;&#xff0c;说的是景区酒店&#xff0c;但打开外窗&#xff0c;近处是简陋的阳台&#xff0c…...

学编程怎么样才能更快入手,编程怎么简单易学

学编程怎么样才能更快入手&#xff0c;编程怎么简单易学 一、前言 对于初学编程建议先从简单入手&#xff0c;然后再学习其他复杂的编程语言。 今天给大家分享的中文编程开发语言工具 进度条构件的用法。 编程入门视频教程链接 https://edu.csdn.net/course/detail/39036 …...

Android 通知--判断通知是否有跳转

一. 从应用层来分析 在 Android 中&#xff0c;可以通过 PendingIntent 来实现有跳转的通知和没有跳转的通知的区别。具体来说&#xff0c;有跳转的通知会设置一个 PendingIntent&#xff0c;当用户点击通知时会触发该 PendingIntent&#xff0c;打开指定的界面或执行特…...

【计算机网络】IO多路转接之poll

文章目录 一、poll函数接口二、socket就绪条件三、poll的优点四、poll的缺点五、poll使用案例--只读取数据的server服务器1.err.hpp2.log.hpp3.sock.hpp4.pollServer.hpp5.main.cc 一、poll函数接口 #include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int t…...

性能比较:in和exists

当在Hive SQL中使用NOT IN和NOT EXISTS时&#xff0c;性能差异主要取决于底层数据的组织方式、数据量大小、索引的使用情况以及具体查询的复杂程度。下面是对这两种方法的性能分析&#xff1a; 1. NOT IN&#xff1a;- 工作原理&#xff1a;NOT IN子查询会逐个比较主查询中的值…...

【Java设计模式】五、建造者模式

文章目录 1、建造者模式2、案例&#xff1a;共享单车的创建3、其他用途 1、建造者模式 某个对象的构建复杂将复杂的对象的创建 和 属性赋值所分离&#xff0c;使得同样的构建过程可以创建不同的表示建造的过程和细节调用者不需要知道&#xff0c;只需要通过构建者去进行操作 …...

nginx代理minio教程 避坑过的教程 避开SignatureDoesNotMatch

本次教程使用的是单机minio进行演示&#xff0c;集群minio也和这个差不多。 按照这个教程&#xff0c;可以避开nginx代理minio之后&#xff0c;只能访问文件&#xff0c;但是通过预签名url上传文件就会报SignatureDoesNotMatch的坑 暂定如下&#xff1a; 你已经下载好miniom…...

Linux进程详细介绍

文章目录 Linux进程1、计算机体系结构和操作系统管理1.1、计算机体系结构 -- 硬件1.2、操作系统&#xff08;Operator System&#xff09; -- 软件 2、进程2.1、进程基本概念2.2、进程标识符2.2.1、获取当前进程标识符和当前进程的父进程标识符2.2.2、通过系统调用创建进程 -- …...

2024年3月产品认证基础考试简答题及答案

产品认证基础 46.产品认证的工厂检查有哪几种路线&#xff1f;各有什么优缺点&#xff1f; 答案&#xff1a;两种常用的检查路线&#xff1a; 1.按照要素或过程检查 按照认证规则规定的工厂应满足的要素要求&#xff08;包括质量保证能力要求&#xff09;&#xff0c;结合部…...

嵌入式蓝桥杯做题总结

第十二届省赛 按键代码 ——自认为比较巧妙&#xff0c;定时器3被设置为10ms进入一次中断&#xff0c;代替了HAL_Delay(10)的方法消抖&#xff1b; 运用状态机机思想实现检测多个按键检测——且分为两个状态&#xff0c;其中一个状态PB&#xff11;和PB&#xff12;的按键不…...

Spring Boot 常用注解大全

以下是Spring Boot中常用的注解及其详细解释以及相应的代码示例&#xff1a; SpringBootApplication: 这个注解用于标识一个Spring Boot应用的主类。它整合了 Configuration&#xff0c;EnableAutoConfiguration 和 ComponentScan。 SpringBootApplication public class Demo…...

(MATLAB)第十二章-数列与极限

目录 12.1 数列 12.1.1 数列求和 1. 累计求和函数sum() 2. 忽略NaN累计求和函数 nansum() 3. 求此元素位置之前的元素和函数cumsum() 4. 求梯形累计和函数cumtrapz() 12.1.2 数列求积 1. 元素连续相乘函数 prod() 2. 求累计积函数 cumprod() 3. 阶乘函数 ffactorial(n…...

OJ输入问题+准备

写在之前&#xff1a; 发现题目输入是这样的&#xff1a; 我的问题&#xff1a;如何通过空格分割这些输入的字符串并分别保存&#xff01;&#xff01;&#xff08;C语言scanf好解决一点但我选择C....&#xff09; C引入了ostringstream、istringstream、stringstream这三个类…...

软考高级:主动攻击和被动攻击概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…...

cuda python torch 虚拟环境配置

以下是Pytorch和CUDA对应的版本 以下是Pytorch和Python对应的版本 检查cuda与Python版本是否匹配 import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.empty(3,4,devicecuda))cuda 删除cuda conda uninstall cudatoolkit --forceconda u…...

激光炸弹 刷题笔记

前置知识 二维前缀和 子矩阵的和 刷题笔记 {二维前缀和}-CSDN博客 思路 参考二维前缀和 将子矩阵的和 做成动态矩阵 一个个矩阵搜索 符合要求边长 矩阵中的元素和最大值 将x1,y1用i-k,j-k表示即可 x2,y2用i&#xff0c;j表示 代码 #include<iostream> #include<…...

Vue3学习记录(三)--- 组合式API之生命周期和模板引用

一、生命周期 1、简介 ​ 生命周期&#xff0c;指的是一个 Vue 实例从创建到销毁的完整阶段&#xff0c;强调的是一个时间段。 ​ 生命周期钩子函数&#xff0c;指的是 Vue 实例提供的内置函数&#xff0c;函数的参数为一个回调函数。这些钩子函数会在实例生命周期的某些固定…...

Batch Normalization和Layer Normalization和Group normalization

文章目录 前言一、Group normalization二、批量规范化(Batch Normalization)三、层规范化&#xff08;Layer Normalization&#xff09; 前言 批量规范化和层规范化在神经网络中的每个批次或每个层上进行规范化&#xff0c;而GroupNorm将特征分成多个组&#xff0c;并在每个组内…...

命名实体识别NER(综合代码示例)

一、命名实体识别发展方向 二、中文数据集 CCKS2017开放的中文的电子病例测评相关的数据。 评测任务一&#xff1a;https://biendata.com/competition/CCKS2017_1/ 评测任务二&#xff1a;https://biendata.com/competition/CCKS2017_2/ CCKS2018开放的音乐领域的实体识别任务…...

城市综合管廊远程监控与智慧运维系统方案

某新区城市建设综合管廊&#xff0c;涵盖电力、燃气、供排水、通信等多种生命线&#xff0c;部署有风机、排水泵、电动阀门、气体传感器、温湿度传感器、液位传感器等设备&#xff0c;核心控制器为西门子PLC&#xff08;S7协议&#xff09;&#xff0c;负责采集管廊内气体浓度、…...

HarmonyOS APP<<古今职鉴定>>开源教程第20篇:农历日期与节日计算

本篇学习农历算法&#xff0c;实现年俗内容的日期驱动图&#xff1a;农历日期与节日计算 的关键流程与实现要点。 学习目标 完成本篇后&#xff0c;你将能够&#xff1a; ✅ 理解农历算法原理✅ 实现公历转农历✅ 计算传统节日✅ 实现年俗日期匹配 预计学习时间 约 90 分钟…...

利用Taotoken CLI工具一键配置团队开发环境中的大模型调用参数

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 利用Taotoken CLI工具一键配置团队开发环境中的大模型调用参数 在团队开发环境中&#xff0c;统一管理大模型调用参数是一个常见痛…...

区块链前端技术栈介绍

这是一份完整区块链前端的学习路径&#xff0c;基于当前市场需求和技术趋势。 &#x1f5fa;️ 技术路线总览 阶段 1&#xff1a;基础入门 (1-2个月) 阶段 2&#xff1a;核心 Web3 技能 (2-3个月) 阶段 3&#xff1a;高级应用开发 (3-4个月) 阶段 4&#xff1a;架构与优化 (2-…...

3分钟掌握智慧职教刷课脚本:全平台自动学习解决方案

3分钟掌握智慧职教刷课脚本&#xff1a;全平台自动学习解决方案 【免费下载链接】auto-play-course 简单好用的刷课脚本[支持平台:职教云,智慧职教,资源库] 项目地址: https://gitcode.com/gh_mirrors/hc/auto-play-course 还在为重复的网课学习任务烦恼吗&#xff1f;智…...

Perplexity语法查询功能深度解析(官方未公开的7个语法边界场景)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;Perplexity语法查询功能的核心定位与设计哲学 Perplexity语法查询功能并非通用搜索引擎的简单变体&#xff0c;而是面向技术深度用户的语义化推理引擎。其核心定位在于将自然语言提问转化为可执行、可验证、可…...

DownKyi终极教程:3步掌握B站视频下载,免费打造个人媒体库

DownKyi终极教程&#xff1a;3步掌握B站视频下载&#xff0c;免费打造个人媒体库 【免费下载链接】downkyi 哔哩下载姬downkyi&#xff0c;哔哩哔哩网站视频下载工具&#xff0c;支持批量下载&#xff0c;支持8K、HDR、杜比视界&#xff0c;提供工具箱&#xff08;音视频提取、…...

OpenAI 模型攻克离散几何 80 年难题:Erdős 单位距离猜想被 AI 证明

OpenAI 模型攻克离散几何 80 年难题&#xff1a;Erdős 单位距离猜想被 AI 证明 一场改写数学史的AI突破 2026年5月20日&#xff0c;OpenAI 宣布其内部通用推理模型成功证明了一个困扰数学界近80年的开放问题——Erdős 单位距离问题&#xff08;Unit Distance Problem&#…...

终极指南:ChatGPT-Web-Midjourney-Proxy如何实现实时AI交互的WebSocket通信

终极指南&#xff1a;ChatGPT-Web-Midjourney-Proxy如何实现实时AI交互的WebSocket通信 ChatGPT-Web-Midjourney-Proxy是一套集成ChatGPT、Midjourney和GPTs功能的全栈UI解决方案&#xff0c;通过WebSocket技术实现了流畅的实时AI交互体验。本文将深入解析其WebSocket通信机制…...

3分钟学会使用PPT计时器:告别演讲超时的终极解决方案

3分钟学会使用PPT计时器&#xff1a;告别演讲超时的终极解决方案 【免费下载链接】ppttimer 一个简易的 PPT 计时器 项目地址: https://gitcode.com/gh_mirrors/pp/ppttimer 你是否在PPT演示时总是担心超时&#xff1f;是否希望有一个工具能自动帮你管理演讲时间&#x…...