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

[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.xdnf.cn/news/7066.html

相关文章:

  • muduo库TcpServer模块详解
  • 设备预测性维护的停机时间革命:中讯烛龙如何用AI重构工业设备管理范式
  • Linux梦开始的地方
  • 面试题总结一
  • 2025.05.17得物机考笔试真题第一题
  • iOS 工厂模式
  • 【图像生成大模型】Step-Video-T2V:下一代文本到视频生成技术
  • 数据结构与算法学习笔记(Acwing提高课)----动态规划·背包模型(四)
  • 人工智能100问☞第26问:什么是贝叶斯网络?
  • 第三章:UI 系统架构拆解与动态界面管理实录
  • PyQt5基本窗口控件(对话框类控件)
  • 配置代理服务器访问github、google
  • golang 安装gin包、创建路由基本总结
  • 定期更新Let‘s Encrypt SSL证书遇到的问题
  • 代码随想录算法训练营第六十五天| 图论10—卡码网94. 城市间货物运输 I,95. 城市间货物运输 II
  • C++11-(2)
  • 从零启动 Elasticsearch
  • C#接口(Interface)全方位讲解:定义、特性、应用与实践
  • Manus 全面开放注册,OpenAI 发布 Codex,ChatGPT 上线 GPT-4.1!| AI Weekly 5.12-18
  • 【Linux高级全栈开发】2.1.2 事件驱动reactor的原理与实现
  • FTP协议
  • elasticsearch kibana ik 各版本下载
  • 医疗信息系统安全防护体系的深度构建与理论实践融合
  • CSS- 4.4 固定定位(fixed) 咖啡售卖官网实例
  • 多指标组合策略思路
  • PopSQL:一个支持团队协作的SQL开发工具
  • 第9章 组件及事件处理
  • Vue+Go 自定义打字素材的打字网站
  • 18. 结合Selenium和YAML对页面继承对象PO的改造
  • 宝塔面板部署前后端项目SpringBoot+Vue2