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

mini-lsm通关笔记Week2Day7

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

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

在上一章中,您已经构建了一个完整的基于LSM的存储引擎。在本周末,我们将实现存储引擎的一些简单但重要的优化。欢迎来到Mini-LSM的第2周零食时间!

在本章中,您将:

  • 实现批量写入接口。
  • 添加checksum到块、SST元数据、manifest和WAL。

注意:本章没有单元测试。只要你通过了之前的所有测试,并确保校验和在你的文件格式中正确编码,就可以了。

Task 1-Write Batch Interface

在本任务中,我们将通过添加写入批处理API来为本教程的第3周做好准备。您需要修改:

src/lsm_storage.rs

用户向write_batch提供一批要写入数据库的记录。这些记录是WriteBatchRecord<T:AsRef<[u8]>>,因此它可以是Bytes&[u8]Vec<u8>。有两种类型的记录:deleteput。您可以以与您的putdelete函数相同的方式处理它们。

之后,你可以重构你原来的putdelete函数来调用write_batch。

在实现此功能之后,您应该通过前面章节中的所有测试用例。

该任务就是实现write_batch函数,其实就是把之前写在putdelete中的函数,放在一起循环遍历:

pub fn write_batch<T: AsRef<[u8]>>(&self, _batch: &[WriteBatchRecord<T>]) -> Result<()> {for record in _batch {match record {WriteBatchRecord::Del(key) => {// 原来delete中的逻辑...}WriteBatchRecord::Put(key, value) => {// 原来put中的逻辑...}}}Ok(())
}

再修改putdelete的实现,调用该函数:

pub fn put(&self, _key: &[u8], _value: &[u8]) -> Result<()> {self.write_batch(&[WriteBatchRecord::Put(_key, _value)])
}pub fn delete(&self, _key: &[u8]) -> Result<()> {self.write_batch(&[WriteBatchRecord::Del(_key)])
}

Task 2-Block Checksum

在此任务中,当编码SST时,您需要在每个块的末尾添加块校验和。您需要修改:

src/table/builder.rs
src/table.rs

SST的格式将更改为:

---------------------------------------------------------------------------------------------------------------------------
|                   Block Section                     |                            Meta Section                           |
---------------------------------------------------------------------------------------------------------------------------
| data block | checksum | ... | data block | checksum | metadata | meta block offset | bloom filter | bloom filter offset |
|   varlen   |    u32   |     |   varlen   |    u32   |  varlen  |         u32       |    varlen    |        u32          |
---------------------------------------------------------------------------------------------------------------------------

我们使用crc32作为我们的校验和算法。您可以在构建block块后使用crc32fast::hash生成block块的校验和。

通常,当用户在存储选项中指定目标block块大小时,大小应包括块内容和校验和。例如,如果目标block块大小为4096,校验和占用4字节,则实际block块内容目标大小应为4092。但是,为了避免破坏之前的测试用例,并且为了简单起见,在我们的教程中,我们仍然将使用目标block块大小作为目标内容大小,并简单地在块的末尾附加校验和。

当你读取块时,你应该在read_block函数中校验校验和,以保证读取到正确的存储内容。在实现此功能之后,您应该通过前面章节中的所有测试用例。

如题目要求需要修改SST的格式,在block块数据后存储校验和。读取的时候通过校验和校验数据是否正确。

计算校验和,并存储(src/table/builder.rs):

fn finish_block(&mut self) {...// 计算校验和let checksum = crc32fast::hash(&encoded_block);// 存储数据self.data.append(&mut encoded_block.to_vec());// 尾部附加校验和self.data.put_u32(checksum);
}

读取校验和,并校验(table.rs):

pub fn read_block(&self, block_idx: usize) -> Result<Arc<Block>> {let offset = self.block_meta[block_idx].offset;let offset_end = self.block_meta.get(block_idx + 1).map_or(self.block_meta_offset, |x| x.offset);// block块的长度let block_len = offset_end - offset - 4;// 读取block块以及校验和let block_data_with_chksum: Vec<u8> = self.file.read(offset as u64, (offset_end - offset) as u64)?;// 数据块数据let block_data = &block_data_with_chksum[..block_len];// 校验和let checksum = (&block_data_with_chksum[block_len..]).get_u32();// 校验数据是否正确if checksum != crc32fast::hash(block_data) {bail!("block checksum mismatched");}Ok(Arc::new(Block::decode(block_data)))
}

Task 3-SST Meta Checksum

在此任务中,您需要为布隆过滤器和块元数据添加块校验和:

src/table/builder.rs
src/table.rs
src/bloom.rs
----------------------------------------------------------------------------------------------------------
|                                                Meta Section                                            |
----------------------------------------------------------------------------------------------------------
| no. of block | metadata | checksum | meta block offset | bloom filter | checksum | bloom filter offset |
|     u32      |  varlen  |    u32   |        u32        |    varlen    |    u32   |        u32          |
----------------------------------------------------------------------------------------------------------

您需要在Bloom::codingBloom::decode中的Bloom过滤器的末尾添加校验和。请注意,我们的大多数API采用一个现有的缓冲区,实现将写入该缓冲区,例如,Bloom::code。因此,在写入编码内容之前,应该记录bloom filter开始的偏移量,并且只对bloom filter本身进行校验和,而不是对整个缓冲区进行校验和。

之后,您可以在块元数据的末尾添加校验和。您可能会发现在小节的开头添加一段元数据会很有帮助,这样在解码块元数据时更容易知道在哪里停止。

元数据校验和

编码(table.rs):

pub fn encode_block_meta(block_meta: &[BlockMeta],#[allow(clippy::ptr_arg)] // remove this allow after you finishbuf: &mut Vec<u8>,
) {let original_len = buf.len();buf.put_u32(block_meta.len() as u32);for meta in block_meta {...// 填充Meta数据}// 计算并写入校验和,只计算Meta数据部分buf.put_u32(crc32fast::hash(&buf[original_len + 4..]));
}

解码(table.rs):

pub fn decode_block_meta(mut buf: &[u8]) -> Result<Vec<BlockMeta>> {let num = buf.get_u32();// 计算校验和let checksum = crc32fast::hash(&buf[..buf.remaining() - 4]);let mut block_meta: Vec<BlockMeta> = Vec::with_capacity(num as usize);for i in 0..num {...// 读取Meta数据}// 读取校验和 并 校验读取的和计算的是否相同if buf.get_u32() != checksum {bail!("meta checksum mismatched");}Ok(block_meta)
}

布隆过滤器校验和

编码(bloom.rs):

pub fn encode(&self, buf: &mut Vec<u8>) {let offset = buf.len();... // 编码布隆过滤器// 计算并写入布隆过滤器let checksum = crc32fast::hash(&buf[offset..]);buf.put_u32(checksum);
}

解码(bloom.rs):

pub fn decode(buf: &[u8]) -> Result<Self> {// 读取校验和并校验let checksum = (&buf[buf.len() - 4..buf.len()]).get_u32();if checksum != crc32fast::hash(&buf[..buf.len() - 4]) {bail!("checksum mismatched for bloom filters");}...
}

Task 4-WAL Checksum

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

src/wal.rs

我们将在预写日志中执行每个记录的校验和。为此,您有两个选择:

  • 生成key-value记录的缓冲区,并使用crc32fast::hash一次性计算校验和。
  • 一次写入一个字段(例如,密钥长度,密钥切片),并使用crc32fast:哈希器对每个字段增量计算校验和。

这取决于您的选择,您将需要选择您自己的冒险。只要正确处理大/小端差异,这两种方法都应该产生完全相同的结果。新的WAL编码应该如下所示:

| key_len | key | value_len | value | checksum |

编码(wal.rs),一次写入一个字段(例如,密钥长度,密钥切片),并使用crc32fast:哈希器对每个字段增量计算校验和:

pub fn put(&self, _key: &[u8], _value: &[u8]) -> Result<()> {...let mut hasher = crc32fast::Hasher::new();hasher.write_u16(key_len as u16);...hasher.write(_key);...hasher.write_u16(value_len as u16);...hasher.write(_value);buf.put_u32(hasher.finalize());...
}

解码(wal.rs),就是逆过程:

let mut hasher = crc32fast::Hasher::new();
...
hasher.write_u16(key_len as u16);
...
hasher.write(&key);
...
hasher.write_u16(value_len as u16);
...
hasher.write(&value);
...// 读取并校验
let checksum = rbuf.get_u32();
if hasher.finalize() != checksum {bail!("checksum mismatch");
}

Task 5-Manifest Checksum

最后,让我们在Manifest文件中添加校验和。Manifest类似于WAL,不同的是,我们不存储每条记录的长度。为了使实现更简单,我们现在在记录的开头添加记录长度的标头,并在记录的末尾添加校验和。

新的Manifest格式如下:

| len | JSON record | checksum | len | JSON record | checksum | len | JSON record | checksum |

在实现所有内容之后,您应该通过之前的所有测试用例。本章不提供新的测试用例。

编码(manifest.rs):

pub fn add_record_when_init(&self, record: ManifestRecord) -> Result<()> {...// 序列化数据let mut buf = serde_json::to_vec(&record)?;// 计算校验和let hash = crc32fast::hash(&buf);// 写入数据段长度file.write_all(&(buf.len() as u64).to_be_bytes())?;// 在数据末尾添加校验和buf.put_u32(hash);// 将数据、校验和写入文件file.write_all(&buf)?;file.sync_all()?;Ok(())
}

解码(manifest.rs),就是逆过程:

while buf_ptr.has_remaining() {// 读取长度let len = buf_ptr.get_u64();let slice = &buf_ptr[..len as usize];// 反序列化let json = serde_json::from_slice::<ManifestRecord>(slice)?;buf_ptr.advance(len as usize);// 读取并校验校验和let checksum = buf_ptr.get_u32();if checksum != crc32fast::hash(slice) {bail!("checksum mismatched!");}records.push(json);
}

相关文章:

mini-lsm通关笔记Week2Day7

项目地址&#xff1a;https://github.com/skyzh/mini-lsm 个人实现地址&#xff1a;https://gitee.com/cnyuyang/mini-lsm 在上一章中&#xff0c;您已经构建了一个完整的基于LSM的存储引擎。在本周末&#xff0c;我们将实现存储引擎的一些简单但重要的优化。欢迎来到Mini-LSM的…...

Typora免费使用

一.下载地址 https://typoraio.cn/ 二.修改配置文件 1.找到安装路径下的LicenseIndex.180dd4c7.4da8909c.chunk.js文件 文件路径为:安装路径\resources\page-dist\static\js\LicenseIndex.180dd4c7.4da8909c.chunk.js 将js中的 e.hasActivated"true"e.hasActiva…...

AI驱动的无线定位:基础、标准、最新进展与挑战

1. 论文概述 研究目标:本论文旨在综述AI在无线定位领域的应用,包括其基础理论、标准化进展、最新技术发展,以及面临的挑战和未来研究方向。主要发现: AI/ML 技术已成为提升无线定位精度和鲁棒性的关键手段,特别是在 3GPP 标准的推动下。论文系统性地分析了 AI 在 LOS/NLOS…...

苹果再度砍掉AR眼镜项目?AR真的是伪风口吗?

曾经&#xff0c;AR游戏一度异常火热&#xff0c;宝可梦go让多少人不惜翻墙都要去玩&#xff0c;但是也没过去几年&#xff0c;苹果被曝出再度砍掉了AR眼镜项目&#xff0c;面对着市场的变化&#xff0c;让人不禁想问AR真的是伪风口吗&#xff1f; 一、苹果再度砍掉AR眼镜项目&…...

18 大量数据的异步查询方案

在分布式的应用中分库分表大家都已经熟知了。如果我们的程序中需要做一个模糊查询&#xff0c;那就涉及到跨库搜索的情况&#xff0c;这个时候需要看中间件能不能支持跨库求交集的功能。比如mycat就不支持跨库查询&#xff0c;当然现在mycat也渐渐被摒弃了(没有处理笛卡尔交集的…...

DRM系列八:Drm之DRM_IOCTL_MODE_ADDFB2

本系列文章基于linux 5.15 在上一篇文章DRM系列七&#xff1a;Drm之DRM_IOCTL_MODE_CREATE_DUMB获取buf的handle和pitch之后&#xff0c;接着使用ioctl(fd, DRM_IOCTL_MODE_ADDFB2, &fb_cmd)创建一个新的帧缓冲区对象&#xff08;framebuffer object),并将帧缓冲区对象与显…...

软件测试用例篇

设计测试用例是测试面试的必考题,务必好好学 1. 测试用例 测试用例的概念 测试⽤例&#xff08;Test Case&#xff09;是为了实施测试而向被测试的系统提供的⼀组集合&#xff0c;这组集合包含&#xff1a;测试环境、操作步骤、测试数据、预期结果等要素。 设计测试⽤…...

PopupMenuButton组件的功能和用法

文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了Sliver综合示例相关的内容&#xff0c;本章回中将介绍PopupMenuButton组件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1 概念介绍 我们在本章回中介绍的PopupMenuButton组件位于AppBar右侧&#xff0c;…...

Python进行模型优化与调参

在数据科学与机器学习领域,模型的优化与调参是提高模型性能的重要步骤之一。模型优化可以帮助提高模型的准确性和泛化能力,而合理的调参则能够充分发挥模型的潜力。这篇教程将重点介绍几种常用的模型优化与调参方法,特别是超参数调整和正则化技术的应用。这些技术能够有效地…...

vue2-组件通信

文章目录 vue2-组件通信1. 为什么需要组件通信2. props传递数据3. $emit触发自定义事件4.ref5. EventBus6. p a r e n t 和 parent和 parent和root7. a t t r s 和 attrs和 attrs和listeners8. provide和inject9. vuex10. 总结 vue2-组件通信 1. 为什么需要组件通信 在VUE中…...

20250205确认荣品RK3566开发板在Android13下可以使用命令行reboot -p关机

20250205确认荣品RK3566开发板在Android13下可以使用命令行reboot -p关机 2025/2/5 16:10 缘起&#xff1a;荣品RK3566开发板在Android13下&#xff0c;希望通过Native C语言程序来控制RK3566的关机。 通过ADB&#xff0c;很容易通过reboot -p命令关机。 最开始以为需要su/root…...

设计模式---观察者模式

设计模式—观察者模式 定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。 主要解决的问题&#xff1a;一个对象状态改变给其他对象通知的问题&#xff0c;而且要考虑到易用和低耦合&#xff0c;…...

初八开工!开启数字化转型新征程!

新年新气象&#xff0c;大年初八&#xff0c;我们斗志昂扬&#xff0c;共同奔赴充满希望的新一年&#xff01; 2025 年意义非凡&#xff0c;这是广州市开利网络科技有限公司成立的第 18 个年头 。回首过往&#xff0c;我们一路拼搏&#xff0c;一路成长&#xff0c;积累了深厚的…...

文本分析NLP的常用工具和特点

1&#xff09;非上下文感知型文本分析工具和特点 特性VADERTextBlob适合文本类型短文本、非正式语言&#xff08;如评论、推文&#xff09;中等长度、正式文本情感强度分析支持&#xff08;正面、负面、中性&#xff09;支持&#xff08;极行、主观性&#xff09;处理表情符号…...

DeepSeek 与 ChatGPT 对比分析

一、技术背景与研发团队 ChatGPT 由 OpenAI 开发&#xff0c;自 2015 年 OpenAI 成立以来&#xff0c;经过多年的技术积累和迭代&#xff0c;从 GPT-1 到 GPT-4o&#xff0c;每一次升级都带来了技术上的突破。OpenAI 拥有雄厚的技术实力和海量的数据、强大的算力支持&#xff…...

vite---依赖优化选项esbuildOptions详解

optimizeDeps.esbuildOptions vite.optimizeDeps.esbuildOptions 是 Vite 配置中的一个选项&#xff0c;它允许你在 Vite 启动时&#xff0c;给 esbuild&#xff08;Vite 用来处理代码转换和优化的工具&#xff09;传递额外的配置。通过这个配置项&#xff0c;你可以自定义 esb…...

ElasticSearch 学习课程入门(二)

引子 前文已经介绍了ES的增删改查基本操作&#xff0c;接下来&#xff0c;我们学习下高级点的用法。OK&#xff0c;那就让我们开始吧。 一、ES高级操作 1、条件查询 &#xff08;1&#xff09;GET https://127.0.0.1:9200/shopping/_search?qcategory:小米 &#xff08;2&…...

使用 Redis Streams 实现高性能消息队列

1. 引言 在后端开发中&#xff0c;消息队列是一个常见的组件&#xff0c;主要用于解耦系统、提高吞吐量以及实现异步处理。常见的消息队列包括 Kafka、RabbitMQ 以及 ActiveMQ&#xff0c;但 Redis Streams 作为 Redis 5.0 引入的新特性&#xff0c;也提供了一种高效、轻量的消…...

深度学习|表示学习|卷积神经网络|DeconvNet是什么?|18

如是我闻&#xff1a; DeconvNet&#xff08;反卷积网络&#xff09;是一种可视化 CNN&#xff08;卷积神经网络&#xff09;内部特征的方法&#xff0c;用于理解 CNN 是如何提取图像特征的。这个方法由 Zeiler & Fergus&#xff08;2013&#xff09; 提出&#xff0c;目的…...

(优先级队列(堆)) 【本节目标】 1. 掌握堆的概念及实现 2. 掌握 PriorityQueue 的使用

优先级队列&#xff08;堆&#xff09; 1. 优先级队列1.1 概念 2. 优先级队列的模拟实现2.1 堆的概念2.2 堆的存储方式2.3 堆的创建2.3.1 堆向下调整2.3.2 堆的创建2.3.3 建堆的时间复杂度 【本节目标】 掌握堆的概念及实现掌握 PriorityQueue 的使用 1. 优先级队列 1.1 概念…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

华为OD机试-最短木板长度-二分法(A卷,100分)

此题是一个最大化最小值的典型例题&#xff0c; 因为搜索范围是有界的&#xff0c;上界最大木板长度补充的全部木料长度&#xff0c;下界最小木板长度&#xff1b; 即left0,right10^6; 我们可以设置一个候选值x(mid)&#xff0c;将木板的长度全部都补充到x&#xff0c;如果成功…...

elementUI点击浏览table所选行数据查看文档

项目场景&#xff1a; table按照要求特定的数据变成按钮可以点击 解决方案&#xff1a; <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...