mini-lsm通关笔记Week2Day3

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

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

Summary

在本章中,您将:

  • 实现tiered合并策略并在压缩模拟器上对其进行模拟。
  • tiered合并策略纳入系统。

我们在本章所讲的tiered合并和RocksDB的universal合并是一样的。我们将互换使用这两个术语。

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

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

Task 1-Universal Compaction

在本章中,您将实现RocksDB的universal合并,它是tiered合并家族的合并策略。与simple leveled合并策略类似,在此合并策略中,我们只使用文件数量作为指标。当我们触发合并任务时,我们总是在合并任务中包含一个完整的排序run(层)。

Task 1.0-Precondition

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

src/compact/tiered.rs

universal合并中,我们不使用LSM状态下的L0 SST。相反,我们直接将新的SST转储到单个排序run(称为层)。在LSM状态中,levels现在将包括所有层,其中最小的索引是最新转储的SSTlevels数组中的每个元素存储一个元组:级别ID(用作层ID)和该级别中的SST。每次转储L0 SST时,都应该将SST转储到放置在向量前面的层中。compaction模拟器根据第一个SST id生成层id,您应该在您的实现中执行相同的操作。

只有在层数(排序run数)大于num_tiers时,universal合并才会触发任务。否则,它不会触发任何合并。

该子任务就是在generate_compaction_task函数中先判断_snapshot.levels数组的大小是否大于num_tiers。若不大于则无需进行任务合并操作。

pub fn generate_compaction_task(&self,_snapshot: &LsmStorageState,
) -> Option<TieredCompactionTask> {if _snapshot.levels.len() < self.options.num_tiers {return None}None
}

Task 1.1-Triggered by Space Amplification Ratio

universal合并的第一个触发因素是空间放大比。正如我们在概述章节中所讨论的,空间放大可以通过engine_size/last_level_size来估计。在我们的实现中,我们通过除最后一级之外的所有级别大小之和/最后一级大小来计算空间放大比,从而可以将比值缩放为[0,+inf]而不是[1, +inf]。这也与RocksDB的实现是一致的。

除最后一级之外的所有级别大小之和/最后一级大小 >= max_size_amplification_percent * 100%时,我们将需要触发一个完整的合并。

实现了这个触发器之后,就可以运行合并模拟器了。您将看到:

cargo run --bin compaction-simulator tiered
--- After Flush ---
L3 (1): [3]
L2 (1): [2]
L1 (1): [1]
--- Compaction Task ---
compaction triggered by space amplification ratio: 200
L3 [3] L2 [2] L1 [1] -> [4, 5, 6]
--- After Compaction ---
L4 (3): [3, 2, 1]

有了这个触发器,我们只有在达到空间放大比的时候才会触发完全压缩。在模拟结束时,您将看到:

--- After Flush ---
L73 (1): [73]
L72 (1): [72]
L71 (1): [71]
L70 (1): [70]
L69 (1): [69]
L68 (1): [68]
L67 (1): [67]
L40 (27): [39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 13, 14, 15, 16, 17, 18, 19, 20, 21]

合并模拟器中的num_tiers设置为3。然而,LSM状态中的层远远不止3层,这导致了很大的读放大。

当前触发合并机制只能减少空间放大。我们需要在合并算法中添加新的触发器,以减少读取放大。

generate_compaction_task
  • 统计除最后一级之外的所有级别大小之和,因为每个SST的大小都是固定的,所以可以近似的使用数量代替大小计算
  • 计算除最后一级之外的所有级别大小之和/最后一级大小,若大于阈值,则创建新的合并任务
pub fn generate_compaction_task(&self,_snapshot: &LsmStorageState,
) -> Option<TieredCompactionTask> {// Task 1.0-Preconditionif _snapshot.levels.len() < self.options.num_tiers {return None;}// Task 1.1-Triggered by Space Amplification Ratiolet levels_num = _snapshot.levels.len();let last_level_size = _snapshot.levels[levels_num - 1].1.len();let mut engine_size = 0;for (index, level) in _snapshot.levels.iter().enumerate() {if index == levels_num - 1 {break;}engine_size += level.1.len();}println!("levels_num:{}, last_level_size:{}, engine_size:{}", levels_num, last_level_size, engine_size);if engine_size as f64 / last_level_size as f64 >= self.options.max_size_amplification_percent as f64 / 100.0f64 {return Some(TieredCompactionTask { tiers: _snapshot.levels.clone(), bottom_tier_included: true });}None
}
apply_compaction_result
  • 将此前的都清空删除,新合并的SST放置到最底层
pub fn apply_compaction_result(&self,_snapshot: &LsmStorageState,_task: &TieredCompactionTask,_output: &[usize],
) -> (LsmStorageState, Vec<usize>) {let mut snapshot = _snapshot.clone();let mut files_to_remove = Vec::new();let tiered_id = _output.first().unwrap();if _task.bottom_tier_included {snapshot.levels.clear();}for levels in _task.tiers.iter() {files_to_remove.extend(levels.clone().1)}snapshot.levels.insert(0, (*tiered_id, _output.to_vec()));(snapshot, files_to_remove)
}

如图展示前两次的情况,第一次触发合并:

第二次触发合并:

可以看到后续触发合并的条件越来越苛刻,以至于层数过多,这将导致读放大。因为每一层都需要进行一次IO。

通过运行合并模拟器可以看到最后转储50个SST后的结果,可以看到存在24层:

=== Iteration 49 ===
--- After Flush ---
L89 (1): [89]
L88 (1): [88]
L87 (1): [87]
L86 (1): [86]
L85 (1): [85]
L84 (1): [84]
L83 (1): [83]
L82 (1): [82]
L81 (1): [81]
L80 (1): [80]
L79 (1): [79]
L78 (1): [78]
L77 (1): [77]
L76 (1): [76]
L75 (1): [75]
L74 (1): [74]
L73 (1): [73]
L72 (1): [72]
L71 (1): [71]
L70 (1): [70]
L69 (1): [69]
L68 (1): [68]
L67 (1): [67]
L40 (27): [39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 13, 14, 15, 16, 17, 18, 19, 20, 21]
--- Compaction Task ---
--- Compaction Task ---
levels_num:24, last_level_size:27, engine_size:23
no compaction triggered
--- Statistics ---
Write Amplification: 89/50=1.780x
Maximum Space Usage: 54/50=1.080x
Read Amplification: 24x

Task 1.2-Triggered by Size Ratio

下一个触发器是大小比触发器。对于所有层,如果有一个层n的所有之前层的大小/此层>= (100 + size_ratio) * 100%,我们将合并所有n层。我们只在要合并的层超过min_merge_width的情况下执行此合并。

使用此触发器,您将在合并模拟器中观察到以下内容:

L207 (1): [207]
L204 (3): [203, 202, 201]
L186 (15): [185, 178, 179, 180, 181, 182, 183, 184, 158, 159, 160, 161, 162, 163, 164]
L114 (31): [113, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56]

将有更少的1-SST层,并且合并算法将保持这些层按大小比从较小到较大的大小。但是,当处于LSM状态的SST更多时,仍然会有超过num_tiers层的情况。为了限制层数,我们需要另一个触发器。

generate_compaction_task函数新增触发条件

// Task 1.2-Triggered by Size Ratio
let mut previous_tiers_size = 0;
for (index, level) in _snapshot.levels.iter().enumerate() {if (index + 1) < self.options.min_merge_width {previous_tiers_size += level.1.len();continue;}let this_tier = level.1.len();if previous_tiers_size as f64 / this_tier as f64 >= (self.options.size_ratio as f64 + 100.0f64) / 100.0f64 {println!("previous_tiers_size:{}, this_tier:{}, n:{}", previous_tiers_size, this_tier, index + 1);return Some(TieredCompactionTask {tiers: _snapshot.levels.iter().take(index + 1).cloned().collect::<Vec<_>>(),bottom_tier_included: index == levels_num - 1,});}previous_tiers_size += level.1.len();
}

实现第二个触发器后,除了往最底层合并外,也能触发前n层进行合并:

Task 1.3:-Reduce Sorted Runs

如果前面的触发器都没有产生合并任务,那么我们将进行一次合并以减少层数。我们将简单地将前面几层压缩为一层,以便最终状态将具有正好num_tiers层(如果在压缩期间没有刷新SST)。

启用此压缩后,您将看到:

L427 (1): [427]
L409 (18): [408, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407]
L208 (31): [207, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72]

所有的合并结果都不会超过num_tiers层。

注意:这部分我们不提供细粒度的单元测试。您可以运行合并模拟器,并与参考解决方案的
输出进行比较,以查看您的实现是否正确。

generate_compaction_task函数新增触发条件,就是在最后返回的时候,判断当前层级个数是否大于num_tiers。如果大于就将前面几个层级进行合并。

if _snapshot.levels.len() == self.options.num_tiers {return None;
}
// Task 1.3:-Reduce Sorted Runs
let nums = _snapshot.levels.len() - self.options.num_tiers + 1;
println!("Reduce Sorted Runs, levels size: {}, num_tiers:{}, nums:{}", _snapshot.levels.len(), self.options.num_tiers, nums);
return Some(TieredCompactionTask {tiers: _snapshot.levels.iter().take(nums).cloned().collect::<Vec<_>>(),bottom_tier_included: false,
});

Task2-Integrate with the Read Path

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

src/compact.rs
src/lsm_storage.rs

由于tiered合并不使用LSM状态的L0级别,所以应该直接将memtable转储到新的层,而不是作为L0 SST。可以通过self.compaction_controller.flush_to_l0()来知道是否要刷新到L0。您可以使用第一个输出SST id作为新排序运行的级别/层ID。您还需要修改您的合并过程,以为tiered合并作业构造合并迭代器。

lsm_storage.rsforce_flush_next_imm_memtable函数中的force_flush_next_imm_memtable修改为:

if self.compaction_controller.flush_to_l0() {// In leveled compaction or no compaction, simply flush to L0snapshot.l0_sstables.insert(0, sst.sst_id());
} else {// In tiered compaction, create a new tiersnapshot.levels.insert(0, (sst.sst_id(), vec![sst.sst_id()]));
}

compact.rs修改compact函数,实现CompactionTask::Tiered分支,因为tired合并中只有排好序的run所以实现比较简单:

CompactionTask::Tiered(TieredCompactionTask { tiers, .. }) => {let mut iters = Vec::with_capacity(tiers.len());for (_, tier_sst_ids) in tiers {let mut ssts = Vec::with_capacity(tier_sst_ids.len());for id in tier_sst_ids.iter() {ssts.push(snapshot.sstables.get(id).unwrap().clone());}iters.push(Box::new(SstConcatIterator::create_and_seek_to_first(ssts)?));}self.compact_generate_sst_from_iter(MergeIterator::create(iters),_task.compact_to_bottom_level(),)
},

相关阅读

Universal Compaction - RocksDB Wiki

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

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

相关文章

人员个体检测、PID行人检测、行人检测算法样本

人员个体检测算法主要用于视频监控、安全防范、人流统计、行为分析等领域&#xff0c;通过图像识别技术来检测和识别视频或图像中的人员个体。这种技术可以帮助管理者实时监控人员活动&#xff0c;确保安全和秩序&#xff0c;提高管理效率。 一、技术实现 人员个体检测算法通常…

Fusion Access

1.FA桌面云需要微软三剑客 2.AD&#xff0c;DNS&#xff0c;DHCP合并部署在一台虚机&#xff0c;内存配置8G 3.FA各个组件 3.1终端接入 3.2接入和访问控制层 3.3虚拟桌面管理层-桌面云规划及部署 3.4安装Linux基础架构虚拟机FA01 3.4.1安装Tools 3.4.2安装FusionAccess组件&am…

【数据结构】基数排序高位优先(MSDF)

基数排序常用写法是低位优先(LSD)&#xff0c;在网上有很多&#xff0c;还有一种写法是高位优先排序(MSDF) 高位优先资料比较少&#xff0c;而且老师布置了一个高位优先的题目&#xff0c;所以也尝试了高位优先的写法&#xff0c;下面来说说吧&#xff0c;程序可以实现功能&…

<Rust>egui学习之部件(十二):如何添加右键菜单?

前言 本专栏是关于Rust的GUI库egui的部件讲解及应用实例分析&#xff0c;主要讲解egui的源代码、部件属性、如何应用。 环境配置 系统&#xff1a;windows 平台&#xff1a;visual studio code 语言&#xff1a;rust 库&#xff1a;egui、eframe 概述 本文是本专栏的第十二篇…

Jenkins使用git和maven编写流水线

1、写git流水线 初识流水线。从git上拉取代码到虚拟机。 【第一步&#xff1a;创建一个新的流水线】 【第二步&#xff1a;定义名字】 点击下方ok&#xff01; 【第三步&#xff1a;添加代码描述】 【第四步&#xff1a;编写流水线代码&#xff0c;如果忘记了&#xff0c;参…

Hadoop安装与配置

一、Hadoop安装与配置 1、解压Hadoop安装包 找到hadoop-2.6.0.tar.gz,将其复到master0节点的”/home/csu”目录内&#xff0c;解压hadoop [csumaster0 ~]$ tar -zxvf ~/hadoop-2.6.0.tar.gz 解压成成功后自动在csu目录下创建hadoop-2.6.0子目录&#xff0c;可以用cd hadoo…

【含文档】基于Springboot+微信小程序 的高校二手商品交易平台(含源码+数据库+lw)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统定…

【数据仓库】数据仓库层次化设计

一、基本概念 **1. RDS&#xff08;RAW DATA STORES&#xff0c;原始数据存储&#xff09;** RDS作为原始数据存储层&#xff0c;用于存储来自各种源头的未经处理的数据。这些数据可能来自企业内部的业务系统、外部数据源或各种传感器等。RDS确保原始数据的完整性和可访问性&…

Python通过Sqlalchemy框架实现增删改查

目录 简介 什么是SQLAlchemy&#xff1f; SQLAlchemy可以分为两个部分&#xff1a;Core和ORM。 一、首先安装sqlalchemy 二、在配置文件中添加数据库连接信息&#xff0c;我这里是Mysql 三、 创建数据库连接类&#xff0c;我这里是动态读取数据库的表字段&#xff0c;自动…

Java数据库连接jdbc

Java数据库连接jdbc 导入java包 1、根目录&#xff0c;新建一个lib目录&#xff08;Dire&#xff09; 2、将jar包放入lib目录下 3、File -> Project Structure&#xff08;项目结构&#xff09; 4、Libraries-> ->java->找到项目的lib目录 5、Apply->OK使用JD…

基于python+django+vue的旅游景点数据分析系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

网安面试会问到的:http的长连接和短连接

《网安面试指南》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484339&idx1&sn356300f169de74e7a778b04bfbbbd0ab&chksmc0e47aeff793f3f9a5f7abcfa57695e8944e52bca2de2c7a3eb1aecb3c1e6b9cb6abe509d51f&scene21#wechat_redirect 《Java代码审…

华为HarmonyOS地图服务 4 - 通过“地图相机“控制地图的可见区域

场景介绍 华为地图的移动是通过模拟相机移动的方式实现的&#xff0c;您可以通过改变相机位置&#xff0c;来控制地图的可见区域&#xff0c;效果如图所示。 本章节将向您介绍相机的各个属性与含义&#xff0c;并移动相机。 相机移动前 …

开源PHP导航网源码/精美简约网址导航收录网站/QQ技术导航程序

源码简介&#xff1a; 一款给力的开源PHP导航网源码&#xff0c;它不仅外观精美简约&#xff0c;还是个网址导航收录网站/QQ技术导航程序哦&#xff01; 在信息爆炸的时代&#xff0c;找网页就像大海捞针一样难。但是有了像PHP 导航网这样的神器&#xff0c;一切都变得简单了…

One API本地开发环境搭建

One API本地开发环境搭建 简介 摘要 &#xff1a; 本文介绍如何在本地搭建 One API 开发环境&#xff0c;包括安装 Go 语言和 GoLand IDE&#xff0c;以及如何新建项目和配置数据库信息。通过简明的步骤说明&#xff0c;帮助开发者快速完成基本的开发环境配置&#xff0c;方…

STM32与51单片机的区别:是否应该直接学习STM32?

STM32与51单片机的区别&#xff1a;是否应该直接学习STM32&#xff1f; 在单片机的世界里&#xff0c;STM32和51单片机都是非常重要的角色。对于初学者来说&#xff0c;是否可以直接跳过51单片机&#xff0c;直接学习STM32&#xff0c;这个问题一直存在争议。让我们深入探讨这…

Vue:默认插槽

目录 一.性质 1.内容分发 2.无名称标识 3.作用域 4.使用方式 二.使用 1.父组件 2.子组件 三.代码 1.父组件代码 2.子组件代码 四.效果 一.性质 1.内容分发 默认插槽允许组件的使用者定义一些内容&#xff0c;这些内容会被插入到组件模板中的特定位置。这有助于实…

TomCat乱码问题

TomCat控制台乱码问题 乱码问题解决&#xff1a; 响应乱码问题 向客户端响应数据&#xff1a; package Servlet;import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servl…

【HTTP】方法(method)以及 GET 和 POST 的区别

文章目录 方法&#xff08;method&#xff09;登录上传GET 和 POST 有什么区别&#xff08;面试&#xff09;区别不准确的说法 方法&#xff08;method&#xff09; 首行中的第一部分。首行是由方法、URL 和版本号组成 方法描述了这次请求想干什么&#xff0c;最主要的是&…

Unity3D入门(一) : 第一个Unity3D项目,实现矩形自动旋转,并导出到Android运行

1. Unity3D介绍 Unity3D是虚拟现实行业中&#xff0c;使用率较高的一款软件。 它有着强大的功能&#xff0c;是让玩家轻松创建三维视频游戏、建筑可视化、实时三维动画等互动内容的多平台、综合型 虚拟现实开发工具。是一个全面整合的专业引擎。 2. Unity安装 官网 : Unity…