mini-lsm通关笔记Week2Day6

项目地址:https://github.com/skyzh/mini-lsm

个人实现地址:https://gitee.com/cnyuyang/mini-lsm

Summary

在本章中,您将:

  • 实现WAL日志文件的编解码

  • 系统重启时使用WAL日志恢复memtable

要将测试用例复制到启动器代码中并运行它们,

cargo x copy-test --week 2 --day 6
cargo x scheck

Task 1-WAL Encoding

在此任务中,您需要修改:

src/wal.rs

在上一章中,我们已经实现了manifest文件,这样LSM状态就可以持久化了。同时我们实现了close函数,在停止引擎之前将所有的memtable转储到SST。现在,如果系统崩溃(即关机)怎么办?我们可以将memtable的修改记录到WAL(预写日志)中,并在重启数据库时读取WAL日志。只有当self.options.enable_wal = true时才启用WAL日志。

WAL日志的编码只是一个键值对列表。

| key_len | key | value_len | value |

您还需要实现recover函数来读取WAL并恢复memtable的状态。

请注意,我们使用BufWriter来写入WAL日志。使用BufWriter可以减少使用操作系统系统调用的次数,从而优化写入路径的耗时。当用户修改key-value键值对时,数据不能保证立刻写入磁盘。相反,引擎只保证在调用sync时持久化数据。要正确地将数据持久化到磁盘,您需要首先通过调用flush()将数据从缓冲区写入器刷盘到文件对象,然后通过使用get_mut().sync_all()对文件执行fsync。请注意,您只需要在调用引擎的sync时进行fsync。不需要每次写入数据都进行fsync。

记录WAL日志

put函数,依次写入key_lenkeyvalue_lenvalue。MemTable在记录WAL日志后,还会调用sync函数强制刷盘。

pub fn put(&self, _key: &[u8], _value: &[u8]) -> Result<()> {let mut file = self.file.lock();let mut buf: Vec<u8> = Vec::new();let key_len = _key.len();// 写入key_lenbuf.put_u16(key_len as u16);// 写入keybuf.put_slice(_key);let value_len = _value.len();// 写入value_lenbuf.put_u16(value_len as u16);// 写入valuebuf.put_slice(_value);file.write_all(&buf)?;Ok(())
}pub fn sync(&self) -> Result<()> {let mut file = self.file.lock();file.flush()?;file.get_mut().sync_all()?;Ok(())
}

构造函数

WAL的构造函数有两个:

  1. create:新MenTable,创建与其对应的WAL日志
  2. recover:恢复过程创建WAL
create

可以参考其他的构造函数,一层一层new出来:

pub fn create(_path: impl AsRef<Path>) -> Result<Self> {Ok(Self {file: Arc::new(Mutex::new(BufWriter::new(OpenOptions::new().read(true).create_new(true).write(true).open(_path).context("failed to create wal")?,))),})
}
recover

需要读取WAL日志,解析出对应的key_value键值对,插入到skiplist中:

pub fn recover(path: impl AsRef<Path>, skiplist: &SkipMap<Bytes, Bytes>) -> Result<Self> {let path = path.as_ref();let mut file = OpenOptions::new().read(true).append(true).open(path).context("failed to recover from WAL")?;let mut buf = Vec::new();// 将WAL日志内容读取到buf中file.read_to_end(&mut buf)?;let mut rbuf: &[u8] = buf.as_slice();while rbuf.has_remaining() {// 读取key_lenlet key_len = rbuf.get_u16() as usize;// 读取keylet key = Bytes::copy_from_slice(&rbuf[..key_len]);rbuf.advance(key_len);// 读取value_lenlet value_len = rbuf.get_u16() as usize;// 读取valuelet value = Bytes::copy_from_slice(&rbuf[..value_len]);rbuf.advance(value_len);// 插入key_value键值对skiplist.insert(key, value);}Ok(Self {file: Arc::new(Mutex::new(BufWriter::new(file))),})
}

Task 2-Integrate WALs

在此任务中,您需要修改:

src/mem_table.rs
src/wal.rs
src/lsm_storage.rs

MemTable有一个WAL变量。如果wal变量为Some(wal),则在更新memtable时需要追加到WAL中。在LSM引擎中,如果enable_wal = true,则需要创建WAL。您还需要在创建新的memtable时使用ManifestRecord::NewMemtable记录一条WAL日志。

可以使用create_with_wal函数创建带有WAL的memtable。WAL应该写入存储目录下的<memtable_id>.wal中。如果此memtable作为L0 SST被转储,则memtable id应该与SST id相同。

  1. mem_table.rs文件修改内容,两个构造函数以及在put数据时记录WAL日志:
// 创建新的MemTable,并创建与之配套的WAL日志
pub fn create_with_wal(_id: usize, _path: impl AsRef<Path>) -> Result<Self> {Ok(MemTable {id: _id,map: Arc::new(SkipMap::new()),wal: Some(Wal::create(_path.as_ref())?),approximate_size: Arc::new(AtomicUsize::new(0)),})
}// 从WAL日志中恢复MemTable
pub fn recover_from_wal(_id: usize, _path: impl AsRef<Path>) -> Result<Self> {let map = Arc::new(SkipMap::new());Ok(Self {id: _id,wal: Some(Wal::recover(_path.as_ref(), &map)?),map,approximate_size: Arc::new(AtomicUsize::new(0)),})
}pub fn put(&self, _key: &[u8], _value: &[u8]) -> Result<()> {...// 记录WAL日志if let Some(ref wal) = self.wal {wal.put(_key, _value);}Ok(())
}
  1. wal.rs中的修改内容在上一小节的内容已展示

  2. lsm_storage.rs需要在创建MenTable时,记录Manifest日志。存在两处,一是创建数据库实例时会初始化一个MemTable,二是冻结MemTable时,会生成新的MemTable。同时这两个地方都需要根据enable_wal开关是否开启,创建是否需要记录WAL日志的MemTable

force_freeze_memtable:

pub fn force_freeze_memtable(&self, _state_lock_observer: &MutexGuard<'_, ()>) -> Result<()> {let memtable_id = self.next_sst_id();// 根据enable_wal开关是否开启,创建是否需要记录WAL日志的MemTablelet new_men_table: Arc<MemTable> = if self.options.enable_wal {Arc::new(MemTable::create_with_wal(memtable_id,self.path_of_wal(memtable_id),)?)} else {Arc::new(MemTable::create(memtable_id))};{// 原始逻辑...}// 记录Manifest日志self.manifest.as_ref().unwrap().add_record(_state_lock_observer,ManifestRecord::NewMemtable(memtable_id),)?;self.sync_dir()?;Ok(())
}

Task 3-Recover from the WALs

在此任务中,您需要修改:

src/lsm_storage.rs

如果启用了WAL,则在加载数据库时需要根据WAL恢复memtable。您还需要实现数据库的同步功能。sync的基本保障是引擎确信数据持久化到磁盘(并在重启时恢复)。要实现这一点,您可以简单地同步当前memtable对应的WAL。

cargo run --bin mini-lsm-cli -- --enable-wal

记得从状态中恢复正确的next_sst_id,它应该是max{memtable id, sst id} + 1。在你的close函数中,如果enable_wal设置为true,你不应该将memtable转储到SST,因为WAL本身提供了持久化。在关闭数据库之前,应该等待所有的compaction和flush线程退出。

如图所示,需要在LsmStorageInner::open中添加如下流程:

同样的可以直接使用cat命令查看wal日志中的内容。

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

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

相关文章

《手札·开源篇》基于开源Odoo软件与Deepseek的智能企业管理系统集成方案

一、方案背景 随着企业数字化转型的深入&#xff0c;传统ERP系统需要结合AI技术实现智能化升级。本方案将开源ERP系统Odoo与深度求索&#xff08;Deepseek&#xff09;大模型能力深度整合&#xff0c;构建具备智能决策支持、自然语言交互和数据分析增强的企业管理平台。 二、…

【DeepSeek背后的技术】系列三:强化学习(Reinforcement Learning, RL)

目录 1 简介1.1 强化学习&#xff08;RL&#xff09;简介1.2 基于人类反馈的强化学习 (RLHF) 简介1.2.1 四个重要角色1.2.2 三个步骤 2 强化算法2.1 PPO&#xff08;Proximal Policy Optimization&#xff09;2.1.1 核心思想2.1.2 算法步骤2.1.3 优点2.1.4 缺点2.1.5 应用场景 …

LabVIEW的智能电源远程监控系统开发

在工业自动化与测试领域&#xff0c;电源设备的精准控制与远程管理是保障系统稳定运行的核心需求。传统电源管理依赖本地手动操作&#xff0c;存在响应滞后、参数调节效率低、无法实时监控等问题。通过集成工业物联网&#xff08;IIoT&#xff09;技术&#xff0c;实现电源设备…

SpringBoot开发(四)SpringBoot配置文件

1. SpringBoot配置文件 1.1. 配置端口号和路径 &#xff08;1&#xff09;在application.properties文件下配置端口号和路径。 server.port: 8081 server.servlet.context-path/demo&#xff08;2&#xff09;运行访问。 1.2. 自定义配置 1.2.1. 方式一 &#xff08;1&…

算法随笔_40: 爬楼梯

上一篇:算法随笔_39: 最多能完成排序的块_方法2-CSDN博客 题目描述如下: 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;2 解释&am…

C# List 列表综合运用实例⁓Hypak原始数据处理编程小结

C# List 列表综合运用实例⁓Hypak原始数据处理编程小结 1、一个数组解决很麻烦引出的问题1.1、RAW 文件尾部数据如下:1.2、自定义标头 ADD 或 DEL 的数据结构如下&#xff1a; 2、程序 C# 源代码的编写和剖析2.1、使用 ref 关键字&#xff0c;通过引用将参数传递&#xff0c;以…

win32汇编环境,窗口程序中自定义工具栏的使用示例

;运行效果 ;win32汇编环境,窗口程序中自定义工具栏的使用示例 ;工具栏一般放在菜单下面&#xff0c;相当于一个个小的对话框&#xff0c;当然你放在其它地方也可以。 ;原理是&#xff0c;创建一张BMP位图&#xff0c;比如下例用一张168*24的图&#xff0c;平均分成7部分&#x…

机器学习专业毕设选题推荐合集 人工智能

目录 前言 毕设选题 开题指导建议 更多精选选题 选题帮助 最后 前言 大家好,这里是海浪学长毕设专题! 大四是整个大学期间最忙碌的时光&#xff0c;一边要忙着准备考研、考公、考教资或者实习为毕业后面临的升学就业做准备,一边要为毕业设计耗费大量精力。学长给大家整理…

备考蓝桥杯嵌入式4:使用LCD显示我们捕捉的PWM波

上一篇博客我们提到了定时器产生PWM波&#xff0c;现在&#xff0c;我们尝试的想要捕获我们的PWM波&#xff0c;测量它的频率&#xff0c;我们应该怎么做呢&#xff1f;答案还是回到我们的定时器上。 我们知道&#xff0c;定时器是一个高级的秒表&#xff08;参考笔者的比喻&a…

ArrayList 扩容机制的源码剖析

在 Java 编程中&#xff0c;ArrayList是一个常用的集合类&#xff0c;它实现了List接口&#xff0c;底层基于数组实现。与普通定长数组不同&#xff0c;ArrayList能够根据元素的添加情况动态调整数组的大小&#xff0c;这就是其扩容机制。下面我们将深入剖析ArrayList扩容机制的…

Java 面试之结束问答

技术优化 线程池优化 设置最大线程数设置最小核心线程数设置额外线程存活时间选择线程池队列选择合适的线程池选择合适的饱和策略 锁优化 尽量不要锁住方法缩小同步代码块&#xff0c;只锁数据锁中尽量不要再包含锁将锁私有化&#xff0c;在内部管理锁进行适当的锁分解 HT…

【llm对话系统】大模型源码分析之 LLaMA 位置编码 RoPE

在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;Transformer 模型已经成为主流。然而&#xff0c;Transformer 本身并不具备处理序列顺序的能力。为了让模型理解文本中词语的相对位置&#xff0c;我们需要引入位置编码&#xff08;Positional Encoding&#xff09;…

【科研】 -- 医学图像处理方向,常用期刊链接

文章目录 文章目录 医学图像处理方向&#xff0c;期刊链接医学图像处理方向&#xff0c;会议 医学图像处理方向&#xff0c;期刊链接 Sicence https://www.science.org/ Nature https://www.nature.com/ Nature Communications https://www.nature.com/ncomms/ Nature Met…

The specified Gradle distribution ‘gradle-bin.zip‘ does not exist.

The specified Gradle distribution ‘https://services.gradle.org/distributions/gradle-bin.zip’ does not exist. distributionUrl不存在&#xff0c;关联不上&#xff0c;下载不了&#xff0c;那就匹配一个能下载的 distributionUrlhttps://services.gradle.org/distrib…

从零开始实现一个双向循环链表:C语言实战

文章目录 1链表的再次介绍2为什么选择双向循环链表&#xff1f;3代码实现&#xff1a;从初始化到销毁1. 定义链表节点2. 初始化链表3. 插入和删除节点4. 链表的其他操作5. 打印链表和判断链表是否为空6. 销毁链表 4测试代码5链表种类介绍6链表与顺序表的区别7存储金字塔L0: 寄存…

Cesium点集中获取点的id,使用viewer.value.entities.getById报错的解决方法

错误代码&#xff1a; viewer.value.entities.getById(pickedObject.id) 报错&#xff1a; 可以正常获取movement.position但是一直出现如下报错&#xff0c;无法获得航点的id&#xff0c;通过断点定位为 viewer.value.entities.getById(pickedObject.id)导致的报错 解决方…

java 进阶教程_Java进阶教程 第2版

第2版前言 第1版前言 语言基础篇 第1章 Java语言概述 1.1 Java语言简介 1.1.1 Java语言的发展历程 1.1.2 Java的版本历史 1.1.3 Java语言与C&#xff0f;C 1.1.4 Java的特点 1.2 JDK和Java开发环境及工作原理 1.2.1 JDK 1.2.2 Java开发环境 1.2.3 Java工作原理 1.…

ARM Linux Qt使用JSON-RPC实现前后台分离

文章目录 1、前言2、解决方案2.1、JSON-RPC2.2、Qt中应用JSON-RPC的框架图2.3、优点2.4、JSON-RPC 1.0 协议规范 3、程序示例3.1、Linux C&#xff08;只例举RPC Server相关程序&#xff09;3.2、Qt程序&#xff08;只例举RPC Client相关程序&#xff09; 4、编译程序4.1、交叉…

PyQt6/PySide6 的 QMainWindow 类

QMainWindow 是 PyQt6 或 PySide6 库中一个非常重要的类&#xff0c;它提供了一个主窗口应用程序的框架&#xff0c;该框架可以包含菜单栏、工具栏、状态栏以及中心部件等。QMainWindow 为 GUI 应用程序提供了基本的结构和布局管理功能&#xff0c;非常适合用来创建复杂的用户界…

9. k8s二进制集群之kube-controller-manager部署

同样在部署主机上创建证书请求文件(为之后的证书生成做准备)根据上面的证书文件创建证书(结果会在当前目录下产生kube-controller-manager证书)创建kube-controller-manager服务配置文件创建kube-controller-manager服务启动文件同步kube-controller-manager证书到对应mast…