mini-lsm通关笔记Week1Day7

news/2024/9/19 22:00:00/文章来源:https://www.cnblogs.com/cnyuyang/p/18395556

Summary

在上一章中,您已经构建了一个具有get/scan/put支持的存储引擎。在本周末,我们将实现SST存储格式的一些简单但重要的优化。欢迎来到Mini-LSM的第1周零食时间!

在本章中,您将:

  • 在SST上实现布隆过滤器,并集成到LSM读路径get中。
  • 以SST块格式实现对key存储的压缩。

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

cargo x copy-test --week 1 --day 7
cargo x scheck

Task 1-Bloom Filters

布隆过滤器是维护一组键的概率数据结构。您可以将键添加到布隆过滤器中,并且您可以知道在添加到布隆过滤器中的键集合中,哪些键可能存在/一定不存在。

为了构造布隆过滤器,通常需要有一个散列函数,一个键可以有多个散列。让我们看看下面的例子。假设我们已经有了一些键的哈希,并且布隆过滤器有7位。

【注:如果你想更好的理解布隆过滤器,可以看这里】

hash1 = ((character - a) * 13) % 7
hash2 = ((character - a) * 11) % 7
b -> 6 4
c -> 5 1
d -> 4 5
e -> 3 2
g -> 1 3
h -> 0 0

如果我们将b, c, d插入到7位布隆过滤器中,我们将得到:

    bit  0123456
insert b     1 1
insert c  1   1
insert d     11
result   0101111

在探测布隆过滤器时,我们为key生成哈希,并查看是否设置了相应的位。如果它们都设置为true,那么key可能存在于bloom过滤器中。否则,该键一定不存在于布隆过滤器中。

对于e -> 3 2,由于位2位置上的bit位没有设置为true,所以它一定不存在于原始集合中。对于g -> 1 3,由于两个位都被设置为true,它可能存在于集合中,也可能不存在于集合中。对于h -> 0 0,两个位(实际上它是一个位)都没有设置,因此它不应该在原始集合中。

b -> maybe (actual: yes)
c -> maybe (actual: yes)
d -> maybe (actual: yes)
e -> MUST not (actual: no)
g -> maybe (actual: no)
h -> MUST not (actual: no)

还记得在上一章的最后,我们实现了基于key范围的SST过滤。现在,在get读取路径上,我们还可以使用布隆过滤器来忽略不包含用户想要查找的key的SST,从而减少从磁盘读取文件的数量。

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

src/table/bloom.rs

在实现中,您将从键散列(是u32数字)构建一个布隆过滤器。对于每个哈希,您需要设置k位。这些位的计算方式如下:

let delta = (h >> 17) | (h << 15); // h is the key hash
for _ in 0..k {// TODO: use the hash to set the corresponding bith = h.wrapping_add(delta);
}

我们提供了执行神奇数学的所有框架代码。您只需要实现构建布隆过滤器和探测布隆过滤器的步骤。

参考资料:

https://leveldb-handbook.readthedocs.io/zh/latest/bloomfilter.html#

https://blog.csdn.net/jiaomeng/article/details/1495500

Task 2-Integrate Bloom Filter on the Read Path

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

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

对于布隆过滤器编码,您可以将布隆过滤器附加到SST文件的末尾。您需要在文件末尾存储布隆过滤器偏移量,并相应地计算元偏移量。

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

我们使用farmhash来计算键的哈希值。在构建SST时,您还需要通过使用farmhash::Factory32计算密钥哈希来构建布隆过滤器。您需要在block的元信息中对布隆过滤器进行编码/解码。你可以为你的布隆过滤器选择误报率0.01。根据需要,您可能需要向结构中添加新的字段,而不是在启动器代码中提供的字段。

之后,可以修改get读取路径,根据bloom filter过滤SST。

我们没有对此部分进行集成测试,您需要确保您的实现仍然通过之前的所有章节测试。

本任务就是借助任务1实现的布隆过滤器,优化性能。在SsTableBuilder中添加成员变量key_hashes

SsTableBuilderadd操作中,添加键的hash值:

self.key_hashes.push(farmhash::fingerprint32(key.raw_ref()));

SsTableBuilderbuild操作中,添加完元信息后,再添加编码后的布隆过滤器:

let bloom = Bloom::build_from_key_hashes(&self.key_hashes,Bloom::bloom_bits_per_key(self.key_hashes.len(), 0.01),
);
let bloom_offset = buf.len();
bloom.encode(&mut buf);
buf.put_u32(bloom_offset as u32);

SsTableopen操作中,先读取解码出布隆过滤器:

let bloom_offset = (&file.read(len - 4, 4)?[..]).get_u32() as u64;
let raw_bloom = file.read(bloom_offset, len - 4 - bloom_offset)?;
let bloom_filter = Bloom::decode(&raw_bloom)?;

LsmStorageInnerscan扫描函数中,使用布隆过滤器优化代码执行效率:

if table.bloom.is_some()&& !table.bloom.as_ref().unwrap().may_contain(farmhash::fingerprint32(_key))
{continue;
}

Task 3-Key Prefix Encoding + Decoding

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

src/block/builder.rs
src/block/iterator.rs

由于SST文件是按顺序存储键的,所以有可能用户存储的是相同前缀的键,我们可以在SST编码中对前缀进行压缩,以节省空间。

我们将当前键与块中的第一个键进行比较。我们按如下方式存储密钥:

key_overlap_len (u16) | rest_key_len (u16) | key (rest_key_len)

key_overlap_len表示块中的第一个key有多少字节相同。例如,如果我们看到一条记录:5|3|LSM,其中块中的第一个key是mini-something,那么我们可以将当前key恢复为mini-LSM

完成编码后,还需要在块迭代器中实现解码。根据需要,您可能需要向结构中添加新字段,而不是在启动器代码中提供的字段。

从数据添加进Block开始,先对BlockBuilderadd函数进行改造。

通过下面函数计算出相同的长度:

fn compute_overlap(first_key: KeySlice, key: KeySlice) -> usize {let mut i = 0;loop {if i >= first_key.len() || i >= key.len() {break;}if first_key.raw_ref()[i] != key.raw_ref()[i] {break;}i += 1;}i
}

编码结果如下,先写入相同部分的长度,再写入剩下部分的长度,最后写入差异部分的数据:

let overlap = compute_overlap(self.first_key.as_key_slice(), key);
// Encode key overlap.
self.data.put_u16(overlap as u16);
// Encode key length.
self.data.put_u16((key.len() - overlap) as u16);
// Encode key content.
self.data.put(&key.raw_ref()[overlap..]);

再对BlockIterator的解码函数seek_to_index进行改造。

通过下面函数解码出first_key,因为第一个key没有比较的对象,所以相似的长度是02个字节读取后不使用。

impl Block {fn get_first_key(&self) -> KeyVec {let mut buf = &self.data[..];buf.get_u16();let key_len = buf.get_u16();let key = &buf[..key_len as usize];KeyVec::from_vec(key.to_vec())}
}

对原来的key的读取进行改造:

let overlap_len = entry.get_u16() as usize;
let key_len = entry.get_u16() as usize;
let key = &entry[..key_len];
self.key.clear();
self.key.append(&self.first_key.raw_ref()[..overlap_len]);
self.key.append(key);

先拼接与first_key相似的部分,再拼接不同的部分。

个人理解:这种编码手段是一种简单且有效的编码手段。但是编码不一定有收益,若没有收益的情况强行使用这种编码方式,反而会带来空间的膨胀。

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

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

相关文章

记一次我的博客园页面突然无法显示markdown数学公式

记一次我的博客园页面突然无法显示markdown数学公式,之前都还好好的,今天突然给我数学公式卡没了......之前都还好好的,今天突然给我数学公式卡没了......具体情况如下但是我编辑的时候预览明明可以摘要里显示也没有问题给官方写了封邮件后得到回复如下 您好,我们这边测试一…

RRAM流片调试心得

RRAM流片调试心得 去年进行了一次RRAM的流片工作,也是人生第一次流片,一些工作细节不便涉及,但是可以谈谈这次流片以及后续测试中碰到的问题,以便后续查阅。 芯片于UMC完成180nm的CMOS前道工艺,共生长5层金属(到V5),随后出Fab,送到所里生长RRAM和M6完成后道工艺,版图…

C#自定义控件—文本显示、文本设值

C#用户控件之文本显示、设定组件 如何绘制一个便捷的文本显示组件、文本设值组件(TextShow,TextSet)?绘制此控件的目的就是方便一键搞定标签显示(可自定义方法显示文本颜色等),方便自定义方法又省略了挨个拖拽的过程纯定义属性 【文本设定】:字体、标签、值、单位;事件…

搜索组件优化 - Command ⌘K

今天心血来潮想在 `blog` 上找一篇文章,用搜素的功能发现搜不出来😂,搜索挂了?然后突然想起来之前由于想着在 `blog` 中可能加一些私有的配置或者尝鲜的功能,所有 `fork` 了一份变成 私有项目了,这样就不符合 `DocSearch` 的 网站必须是公开的这个限制了。前言: DevNow…

项目协同开发 or 拷贝项目

项目协同开发 or 拷贝项目 给另人项目时一般需要给代码 requiremenets.txtpip freeze > requiremenets.txt #requiremenets.txt 生成方式获得别人代码 pip install -r requiremenets.txt # 自动将:requiremenets.txt 对应的版本进行安装无网络问题-解决安装第三方模块…

深入浅出Stream流

Java 8的新特性之一就是流stream,配合同版本出现的 Lambda ,使得操作集合(Collection)提供了极大的便利。 案例引入 在JAVA中,涉及到对数组、Collection等集合类中的元素进行操作的时候,通常会通过循环的方式进行逐个处理,或者使用Stream的方式进行处理。 假设遇到了这么…

决策树之——ID3算法及示例

0 前言本文主要介绍决策树ID3算法,并举出构建示例帮助理解。 读者需要具备的知识:信息熵、条件熵、信息增益。 本文使用数据集为:游玩数据集 1.1节。1 ID3算法简述 ID3(Iterative Dichotomiser 3)算法是一种经典的决策树学习算法,由Ross Quinlan于1986年提出。该算法的主…

AtCoder ABC 369题解

题解前言 本题解部分思路来源于网络,仅供参考 ! A - 369 题目大意 给定 \(A\) , \(B\) 两个整数,求有多少个整数 \(x\) 使得可以通过某种排列使得 \(A\) ,\(B\) ,\(x\) 为等差数列。解题思路 稍加分析即可得到:如果 \(A = B\) 则结果为 \(1\) 。如果 \(A = B\) 但 \((A …

三路快速排序

// 快速排序 function quickSort(arr) {if (arr.length <= 1) {return arr;}const pivot = arr[0];const left = [];const right = [];for (let i = 1; i < arr.length; i++) {if (arr[i] < pivot) {left.push(arr[i]);} else {right.push(arr[i]);}}return quickSort…

三路排序

// 快速排序 function quickSort(arr) {if (arr.length <= 1) {return arr;}const pivot = arr[0];const left = [];const right = [];for (let i = 1; i < arr.length; i++) {if (arr[i] < pivot) {left.push(arr[i]);} else {right.push(arr[i]);}}return quickSort…

8.30 ~ 9.8

文化课奥赛8.30 返校日。 又回到了原来的班(和化奥一个班),一个班有 69 个人; 然后我坐在最角上 🙃 第二天还要开学考试,还有 60% 的原题,这我拿头靠考啊; 遂摆。 发现了坐最后面的好处:离得远老师看不到。 8.31 开学考试。 考试顺序:语 \(\to\) 物 \(\to\) 数 \(\t…

【Spring Boot】整合MyBatis

**整合MyBatis**前言 Spring Boot和MyBatis都是非常流行的Java框架。Spring Boot简化了Spring应用的开发,而MyBatis则是一个优秀的持久层框架,支持自定义SQL、存储过程以及高级映射。 mybatis官方文档:http://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconf…