[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)核心逻辑

在这里插入图片描述

  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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值