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

【LevelDB】memtable、immutable memtable的切换源码

本文主要分析leveldb项目的MakeRoomForWrite方法及延伸出的相关方法。
努力弄清memtable 和 immutable memtable的切换过程细节,

背景总结:

LevelDB 是一个基于 Log-Structured Merge-Tree (LSM Tree) 的高性能键值存储系统。
在 LevelDB 中,MemTable 和 SSTable 是两种关键的数据结构,它们共同支持快速的读写操作和高效的存储管理。

MemTable 是 LevelDB 中的一个内存数据结构,它提供了快速的键值对读写能力。
SSTable(Sorted String Table)是 LevelDB 中用于持久化存储数据的结构。

当 MemTable 达到一定大小时,LevelDB 会将其转换为一个不可变的 memtable。这个过程称为 MemTable 切换(MemTable Switch)。新的 MemTable 会被创建,用于处理新的写入操作。被切换的 MemTable 会被持久化到磁盘上,成为一个 SSTable。这个过程通常涉及到写入一个 SSTable 文件,并可能触发 Compaction 操作来优化存储结构。

通过这种方式,LevelDB 能够在提供快速写入和读取操作的同时,有效地管理内存和磁盘空间,保持长期的存储效率。

有了以上基本知识背景后,我们来看源码怎么实现的?

MakeRoomForWrite源码分析

主要逻辑在db_impl.cc 的MakeRoomForWrite方法里。
这个方法的调用点在DBImpl::Write,即写数据的时候。

// REQUIRES: mutex_ is held
// REQUIRES: this thread is currently at the front of the writer queue
Status DBImpl::MakeRoomForWrite(bool force) {// 这个方法需要持有锁。mutex_.AssertHeld();assert(!writers_.empty());bool allow_delay = !force;Status s;while (true) {// 如果后台有error,将error赋给Status对象,然后跳出while循环if (!bg_error_.ok()) {// Yield previous errors = bg_error_;break;// 如果没有error,允许写延迟并且当前level0级别的文件数大于阈值。// 那这时就slow down writers。休眠1ms。期间释放mutex_, 休眠结束之后再获取mutex_} else if (allow_delay && versions_->NumLevelFiles(0) >=config::kL0_SlowdownWritesTrigger) {// We are getting close to hitting a hard limit on the number of// L0 files.  Rather than delaying a single write by several// seconds when we hit the hard limit, start delaying each// individual write by 1ms to reduce latency variance.  Also,// this delay hands over some CPU to the compaction thread in// case it is sharing the same core as the writer.mutex_.Unlock();env_->SleepForMicroseconds(1000);allow_delay = false;  // Do not delay a single write more than oncemutex_.Lock();// 走到下面这个分支,说明level0级别的文件数还没超过阈值。并且当前正在使用的memtable// 的内存使用小于4MB,说明还有空间预留给写操作,直接退出循环。// 注:预估内存使用情况使用的是leveldb自己实现的内存管理库Arena。} else if (!force &&(mem_->ApproximateMemoryUsage() <= options_.write_buffer_size)) {// There is room in current memtablebreak;// 走到下面这个分支,说明当前memtable没有足够空间给写操作了。并且已经有一个immutable memtable存在了,// 此时不能继续创建immutable memtable了,打印一下日志,等待后台任务发出唤醒信号(imm_数据已经被flush到磁盘,并且引用被销毁)。} else if (imm_ != nullptr) {// We have filled up the current memtable, but the previous// one is still being compacted, so we wait.Log(options_.info_log, "Current memtable full; waiting...\n");background_work_finished_signal_.Wait();// 到下面这个分支,说明imm_指针为null,判断当前level0级别文件个数是否超过12个// 超过12个就需要等待后台任务发信号唤醒(compact结束)} else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) {// There are too many level-0 files.Log(options_.info_log, "Too many L0 files; waiting...\n");background_work_finished_signal_.Wait();// 走到最后这个分支里,说明可以直接创建immutable memtable。} else {// 尝试切换到新的memtable,并且触发旧的文件的compaction。// Attempt to switch to a new memtable and trigger compaction of oldassert(versions_->PrevLogNumber() == 0);uint64_t new_log_number = versions_->NewFileNumber();WritableFile* lfile = nullptr;// 创建新的日志文件。用lfile指针指向。s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile);if (!s.ok()) {// Avoid chewing through file number space in a tight loop.versions_->ReuseFileNumber(new_log_number);break;}// 删除当前日志文件的Writer指针	delete log_;// 关闭旧的日志文件 s = logfile_->Close();if (!s.ok()) {// We may have lost some data written to the previous log file.// Switch to the new log file anyway, but record as a background// error so we do not attempt any more writes.//// We could perhaps attempt to save the memtable corresponding// to log file and suppress the error if that works, but that// would add more complexity in a critical code path.RecordBackgroundError(s);}// 释放旧的日志文件对象delete logfile_;// logfile_指针指向新创建的日志文件logfile_ = lfile;logfile_number_ = new_log_number;// log_指针指向新日志文件创建出来的Writer对象log_ = new log::Writer(lfile);// imm_指针指向旧的mem_,即immutable memtable指向当前的memtable。imm_ = mem_;// has_imm_是个原子bool。has_imm_.store(true, std::memory_order_release);// mem_指针指向一个新的MemTable。mem_ = new MemTable(internal_comparator_);// 增加mem_的引用计数mem_->Ref();force = false;  // Do not force another compaction if have room// 可能会触发compaction。MaybeScheduleCompaction();}}return s;
}

TODO:
1、leveldb里NewWritableFile、LogFileName。

http://www.lryc.cn/news/324929.html

相关文章:

  • 力扣面试150 x 的平方根 二分 换底法 牛顿迭代法 一题多解
  • 【JavaScript】JavaScript 程序流程控制 ⑤ ( 嵌套 for 循环 | 嵌套 for 循环概念 | 嵌套 for 循环语法结构 )
  • 情感计算:大模型在情感识别与交互优化中的作用
  • 集合系列(十四) -ConcurrentHashMap详解
  • 数据结构面试题
  • python爬虫之xpath入门
  • TikTok云手机是什么原理?
  • 24.3.24 《CLR via C#》 笔记10
  • SpringBoot 3整合Elasticsearch 8
  • 突破编程_C++_查找算法(分块查找)
  • 学习java第二十二天
  • 每天学习一个Linux命令之systemctl
  • 【机器学习入门】人工神经网络(二)卷积和池化
  • 公司内部局域网怎么适用飞书?
  • JVM的知识
  • 大模型日报2024-03-24
  • Android kotlin全局悬浮窗全屏功能和锁屏页面全屏悬浮窗功能一
  • 图像识别在安防领域的应用
  • 前端面试集中复习 - http篇
  • C++ - 类和对象(上)
  • mysql基础4sql优化
  • 实现Spring Web MVC中的文件上传功能,并处理大文件和多文件上传
  • 搭建vite项目
  • Docker 安装mysql 主从复制
  • GPT每日面试题—如何实现二分查找
  • 机器学习神经网络由哪些构成?
  • 代码随想录算法训练营day19 | 二叉树阶段性总结
  • 数据库引论:3、中级SQL
  • 毕业设计:日志记录编写(3/17起更新中)
  • (一)基于IDEA的JAVA基础7