[LevelDB]LevelDB版本管理的黑魔法-为什么能在不锁表的情况下管理数据?

文章摘要

  1. LevelDB的日志管理系统是怎么通过双链表来进行数据管理
  2. 为什么LevelDB能够在不锁表的情况下进行日志新增

适用人群:

  1. 对版本管理机制有开发诉求,并且希望参考LevelDB的版本开发机制。
  2. 数据库相关从业者的专业人士。
  3. 计算机狂热爱好者,对计算机的存储机制有强烈技术追求的同志。

阅读建议:

  1. 作者本人功底有限不太可能考虑到所有读者的阅读细节,建议读者先通盘阅读下本文,先熟悉本文中会出现哪些关键概念和关键流程,并配合上AI工具对文章中个别流程进行细致理解。

LevelDB版本管理机制

核心抽象

在这里插入图片描述
主要分为

  1. 版本管理层:版本抽象相关的操作和逻辑。
  2. 文件管理层: 主要和文件磁盘上的物化数据打交道。
  3. 快照管理层:依托快照对外暴露固化查询的服务。
抽象职能
Version版本管理的最小单位,维护特定时刻的数据库状态,管理文件集合
VersionSet版本集合管理器,负责管理所有版本,维护当前版本,处理版本切换
VersionEdit版本变更记录,记录版本间的差异,支持变更的序列化和反序列化
Builder版本构建器,负责构建新版本,应用版本变更
Compaction压缩任务管理,处理文件压缩,生成新的版本变更
FileMetaData文件元数据,记录文件的基本信息(大小、范围等)
Manifest清单文件管理,持久化版本信息,支持数据库恢复
Snapshot数据库某一时刻的快照,提供一致性读取视图,基于序列号实现
SnapshotList快照列表管理,维护所有活跃的快照,管理快照的生命周期

这里面的重点是:

  • Version是整个版本管理机制的最小单元抽象
  • compaction 的机制非常复杂,本文不赘述,感兴趣移步: [LevelDB]揭秘LevelDB暗藏的合并秘技,Compaction内部的超神操作让工程师都惊呆了!

版本管理层(Version)核心逻辑

在这里插入图片描述

  1. 客户端向VersionSet抽象请求版本变更操作
  2. VersionSet 通过创建VersionEdit来记录一些当前操作的变更
  3. 使用Build模式来创建新版本并更新当前的版本

亮点设计:

  1. 使用新建Version的方式来实现无锁读取,简化并发控制。
  2. 使用Builder(构建者模式)来进行构建,对代码进行解耦。
  3. 使用VersionEdit进行增量更新,并且能够通过VersionEdit来进行日志记录。

源代码细节说明

客户端(通常理解是应用LevelDB的机器)调用LevelDB的LogAndApply接口来进行版本新增

// 调用入口
s = versions_->LogAndApply(&edit, &mutex_);

简单来说,就是生成一个新的版本Version,并添加到当前的版本管理链表中。
在这里插入图片描述
LogAndApply方法主要有以下的核心阶段(流程图见下文

  • 初始化一些必要参数,如log_number_(日志编号: 用于后续清理WAL无用日志), file_number(文件编号-用于实现文件的唯一性),Sequence(用于实现序列号唯一性)
    在这里插入图片描述
    源码参考:
  // 日志版本号-用于实现WAL 预写入机制-用于清理当时用不了的文件if (edit->has_log_number_) {assert(edit->log_number_ >= log_number_);assert(edit->log_number_ < next_file_number_);} else {edit->SetLogNumber(log_number_);}if (!edit->has_prev_log_number_) {edit->SetPrevLogNumber(prev_log_number_);}// file_number 文件编号-用于实现文件的唯一性  // sequence: 用于控制序列唯一性edit->SetNextFile(next_file_number_);edit->SetLastSequence(last_sequence_);
  • 构建新版本(包括对版本的压缩打分)
    在这里插入图片描述
  1. 创建新版本 -亮点: 使用Builder模式来构建新版本
  2. Apply 方法 用于根据edit中的信息来生成对应的build构造器
  3. SaveTo 方法 用于将build构造器中的信息应用到新的version中
  Version* v = new Version(this);{Builder builder(this, current_);builder.Apply(edit);builder.SaveTo(v);}// 计算 最佳压缩层级Finalize(v);
  • MANIFEST处理阶段, 这里的MANIFEST
    在这里插入图片描述
 // MANIFEST 文件处理std::string new_manifest_file;Status s;if (descriptor_log_ == nullptr) {// 日志assert(descriptor_file_ == nullptr);// 底层文件操作new_manifest_file = DescriptorFileName(dbname_, manifest_file_number_);s = env_->NewWritableFile(new_manifest_file, &descriptor_file_);if (s.ok()) {descriptor_log_ = new log::Writer(descriptor_file_);// 写入快照-本质上是向 Manifest 日志中写入当前的文件状态,防止记录丢失s = WriteSnapshot(descriptor_log_);}}
  • 文件同步阶段
    在这里插入图片描述
{mu->Unlock();if (s.ok()) {std::string record;edit->EncodeTo(&record);s = descriptor_log_->AddRecord(record);if (s.ok()) {s = descriptor_file_->Sync();}if (!s.ok()) {Log(options_->info_log, "MANIFEST write: %s\n", s.ToString().c_str());}}if (s.ok() && !new_manifest_file.empty()) {s = SetCurrentFile(env_, dbname_, manifest_file_number_);}// 重新获取锁mu->Lock();}
  • 完成安装阶段
    在这里插入图片描述
// 让基于VersionEdit和老版本if (s.ok()) {// 将新版本添加到版本链表AppendVersion(v);// 更新日志文件号log_number_ = edit->log_number_;prev_log_number_ = edit->prev_log_number_;} else {//  快照写入失败delete v;if (!new_manifest_file.empty()) {// 清理新创建的 MANIFEST 文件相关资源delete descriptor_log_;delete descriptor_file_;descriptor_log_ = nullptr;descriptor_file_ = nullptr;env_->RemoveFile(new_manifest_file);}}return s;
}

猜你喜欢

C++多线程: https://blog.csdn.net/luog_aiyu/article/details/145548529
一文了解LevelDB数据库读取流程:https://blog.csdn.net/luog_aiyu/article/details/145946636
一文了解LevelDB数据库写入流程:https://blog.csdn.net/luog_aiyu/article/details/145917173
关于LevelDB存储架构到底怎么设计的:https://blog.csdn.net/luog_aiyu/article/details/145965328?spm=1001.2014.3001.5502

PS

你的赞是我很大的鼓励
我是darkchink,一个计算机相关从业者&一个摩托佬&AI狂热爱好者
本职工作是某互联网公司数据相关工作,欢迎来聊,内推或者交换信息
vx 二维码见: https://www.cnblogs.com/DarkChink/p/18598402

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

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

相关文章

【C++进阶篇】C++容器完全指南:掌握set和map的使用,提升编码效率

C容器的实践与应用&#xff1a;轻松掌握set、map与multimap的区别与用法 一. 序列式容器与关联式容器1.1 序列式容器 (Sequential Containers)1.2 关联式容器 (Associative Containers) 二. set系列使用2.1 set的构造和迭代器2.2 set的增删查2.2.1 插入2.2.2 查找2.2.3 删除 2.…

2_Spring【IOC容器中获取组件Bean】

Spring中IOC容器中获取组件Bean 实体类 //接口 public interface TestDemo {public void doSomething(); } // 实现类 public class HappyComponent implements TestDemo {public void doSomething() {System.out.println("HappyComponent is doing something...")…

安卓开饭-ScrollView内嵌套了多个RecyclerView,只想与其中一个RecyclerView有联动

在 Android 开发中&#xff0c;将 RecyclerView 嵌套在 ScrollView 内通常会导致性能问题和滚动冲突&#xff0c;应尽量避免这种设计。以下是原因和替代方案&#xff1a; 为什么不推荐 RecyclerView ScrollView&#xff1f;​​ 性能损耗​ RecyclerView 本身已自带高效回收复…

HTTP 请求中 Content-Type 头部

HTTP 请求中 Content-Type 头部可以设置的各种不同的传输格式。multipart/form-data 只是其中一种,主要用于传输包含文件的数据。 以下是一些常见的 HTTP 请求体的 Content-Type 及其用途: 常见的数据传输格式 (Content-Type) 列表: application/json: 描述: 用于传输 JSO…

【U-boot 命令使用】

文章目录 1 查询有哪些命令2 信息查询命令dbinfo - 查看板子信息printenv- 输出环境变量信息version - 输出uboot版本信息 3 环境变量操作命令修改环境变量新建环境变量删除环境变量 4 内存操作命令md命令nm命令mm命令mv命令cp命令cmp命令 5 网络操作命令与网络有关的环境变量p…

初学者如何用 Python 写第一个爬虫?

初学者如何用 Python 写第一个爬虫&#xff1f; 一、爬虫的基本概念 &#xff08;一&#xff09;爬虫的定义 爬虫&#xff0c;英文名为 Web Crawler&#xff0c;也被叫做网络蜘蛛、网络机器人。想象一下&#xff0c;有一个勤劳的小蜘蛛&#xff0c;在互联网这个巨大的蜘蛛网中…

IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 VSCode + IoT Link 插件

文章目录 概述IDE安装安装旧版本VSCode安装插件安装问题和解决手动安装SDK包手动下载依赖工具 IoTLink配置IoTLink Home用户设置-工具链-编译器用户设置-工具链-构建器用户设置-工具链-烧录器用户设置-SDK管理工程设置-SDK配置工程设置-编译器工程设置-调试器 创建工程Demo 源码…

深度剖析:Dify+Sanic+Vue+ECharts 搭建 Text2SQL 项目 sanic-web 的 Debug 实战

目录 项目背景介绍sanic-web Dify\_service handle\_think\_tag报错NoneType问题描述debug Dify调用不成功&#xff0c;一直转圈圈问题描述debug 前端markdown格式只显示前5页问题描述debug1. 修改代码2.重新构建1.1.3镜像3.更新sanic-web/docker/docker-compose.yaml4. 重新部…

理想AI Talk第二季-重点信息总结

一、TL&#xff1b;DR 理想为什么要做自己的基模&#xff1a;座舱家庭等特殊VLM场景&#xff0c;deepseek/openai没有解决理想的基模参数量&#xff1a;服务端-300B&#xff0c;VLencoder-32B/3.6B&#xff0c;日常工作使用-300B&#xff0c;VLA-4B为什么自动驾驶可以达成&…

TensorRT

TensorRT 下载 TensorRT 7.1.3.4 TAR压缩包&#xff0c;解压到安装目录&#xff1a; tar xzvf TensorRT-7.1.3.4.Ubuntu-16.04.x86_64-gnu.cuda-11.0.cudnn8.0.tar.gz 添加 TensorRT lib 到环境变量&#xff1a; gedit ~/.bashrc # 添加 export LD_LIBRARY_PATH$LD_LIBRARY_PAT…

【NGINX】 -9 nginx + tomcat实现的多级反向代理

文章目录 1、tomcat的安装 (centos版本)1.1 安装Java依赖环境1.2 安装tomcat 2、tomcat的虚拟主机的配置2.1 配置多级目录 3、利用nginx的反向代理实现将转发指向一个虚拟机3.1 nginx服务器的配置3.2 客户端配置 4、 反向多级代理代理服务器操作nginx 1 服务器nginx 2 服务器to…

基于requests_html的python爬虫

前言&#xff1a;今天介绍一个相对性能更高的爬虫库requests_html&#xff0c;会不会感觉和requests有点联系&#xff1f;是的。为什么开始不直接介绍呢&#xff1f;因为我觉得requests是最基本入门的东西&#xff0c;并且在学习过程中也能学到很多东西。我的python老师在介绍这…

【架构篇】架构类型解释

架构设计的本质&#xff1a;从模糊概念到系统化思维 摘要 “架构”是系统设计的灵魂&#xff0c;但许多人对它的理解仍停留在抽象层面。本文系统解析架构的8大核心维度&#xff0c;结合设计原则、案例与误区分析&#xff0c;帮助开发者建立从战略到落地的完整认知框架。 一、架…

用Python绘制梦幻星空

用Python绘制梦幻星空 在这篇教程中&#xff0c;我们将学习如何使用Python创建一个美丽的星空场景。我们将使用Python的图形库Pygame和随机库来创建闪烁的星星、流星和月亮&#xff0c;打造一个动态的夜空效果。 项目概述 我们将实现以下功能&#xff1a; 创建深蓝色的夜…

PyTorch循环神经网络(Pytotch)

文章目录 循环神经网络&#xff08;RNN&#xff09;简单的循环神经网络长短期记忆网络&#xff08;LSTM&#xff09;门控循环单元&#xff08;GRU&#xff09; 循环神经网络&#xff08;RNN&#xff09; 循环神经网络&#xff08;RecurrentNeuralNetwork&#xff0c;RNN&#…

用算术右移实现逻辑右移及用逻辑右移实现算术右移

函数srl()用算术右移实现逻辑右移&#xff0c;函数sra()用逻辑右移实现算术右移。 程序代码 int sra(int x,int k); unsigned int srl(unsigned int x, int k);void main() {int rx1,k,x1;unsigned int rx2,x2;k3;x10x8777;x20x8777;rx1sra(x1, k);rx2srl(x2, k);while(1); }…

pojo层、dao层、service层、controller层的作用

在Java Web开发中&#xff0c;常见的分层架构&#xff08;如Spring Boot项目&#xff09;通常包含POJO层、DAO层、Service层和Controller层&#xff0c;各层职责明确&#xff0c;协同工作。以下是各层的作用及相互关系&#xff1a; 1. POJO层&#xff08;Model/Entity层&#…

【Linux网络】五种IO模型与阻塞IO

IO 在Linux网络环境里&#xff0c;IO&#xff08;Input/Output&#xff09;指的是网络数据在系统与外部网络&#xff08;像其他设备、服务器或者客户端&#xff09;之间进行传输的过程。 它是网络编程和系统性能优化的核心内容。 IO &#xff1a;INPUT和OUTPUT&#xff08;站…

入门OpenTelemetry——应用自动埋点

埋点 什么是埋点 埋点&#xff0c;本质就是在你的应用程序里&#xff0c;在重要位置插入采集代码&#xff0c;比如&#xff1a; 收集请求开始和结束的时间收集数据库查询时间收集函数调用链路信息收集异常信息 这些埋点数据&#xff08;Trace、Metrics、Logs&#xff09;被…

大数据场景下数据导出的架构演进与EasyExcel实战方案

一、引言&#xff1a;数据导出的演进驱动力 在数字化时代&#xff0c;数据导出功能已成为企业数据服务的基础能力。随着数据规模从GB级向TB级甚至PB级发展&#xff0c;传统导出方案面临三大核心挑战&#xff1a; ‌数据规模爆炸‌&#xff1a;单次导出数据量从万级到亿级的增长…