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

[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

相关文章:

  • bus hound抓取的数据包各字段含义解释
  • DAY26 函数定义与参数
  • 2025年- H28-Lc136- 24.两两交换链表中的节点(链表)---java版
  • Java开发经验——阿里巴巴编码规范实践解析3
  • 创建指定版本的vite项目
  • 【Retinanet】训练自己的数据集
  • InfluxDB 3 Core + Java 11 + Spring Boot:打造高效物联网数据平台
  • LangChain框架实战:从入门到开发大模型应用
  • Pandas 构建并评价聚类模型② 第六章
  • 红黑树的实现
  • MidJourney生成王昭君全身像提示词
  • 学习是有方法的——费曼学习法
  • golang选项设计模式
  • 参考文献标准中与永久标识相关的PID、URN、DOI等概念的理解和区分
  • 【MyBatis-11】MyBatis批处理:提升数据操作性能的利器
  • 【LeetCode 热题100】17:电话号码的字母组合(详细解析)(Go语言版)
  • 【typenum】 11 私有模块(private.rs)
  • 开机自启rc.local
  • ctr查看镜像
  • 需求文档不完整,如何确保开发理解一致?
  • 学人、学术、学科、学脉:新时代沾溉下的中国西方史学史
  • 莱布雷希特专栏:古典乐坛边缘人
  • 国际观察丨美中东政策生变,以色列面临艰难选择
  • 经济日报金观平:促进信贷资金畅达小微企业
  • 解锁儿时愿望!潘展乐战胜孙杨,全国冠军赛男子400自夺冠
  • 阿里上财年营收增6%,蒋凡:会积极投资,把更多淘宝用户转变成即时零售用户