CMU15-445 Project.3总结
在线测试
Project #3 - Query Execution
以下是Project #3的网址,2022FALL的Project #3是实现一个查询执行,实现一系列算子,用于实现数据库内的SQL计算。项目中的 Query Execution 主要分为三个任务:
- Access Method Executors : 需要实现 SeqScan、Insert、Delete、IndexScan 四个算子。
- Aggregation and Join Executors : 需要实现 Aggregation、NestedLoopJoin、NestedIndexJoin 三个算子。
- Sort + Limit Executors and Top-N Optimization : 需要实现 Sort、Limit、TopN 三个算子,以及实现将 Sort + Limit 优化为 TopN 算子。
我们首先需要了解 SQL 语句如何在 Bustub 中实现相应功能:
- 一条 SQL 语句首先通过 Parser 生成一棵抽象语法树 AST 。在 Bustub 中通过调用第三方库libpg_query 将一条 SQL 语句解析为 AST 。
- 在获得 AST 之后, Binder 将抽象的词语绑定到数据库中的实体类中,最终获得一个可以被 Bustub 理解的 Bustub AST 。
- 获得了绑定的语法树之后,Planner 遍历这棵树,生成相应的查询计划。我们可以将 Planner 理解为树中的每一个节点,需要根据查询计划进行一定的运算。
- 在通过 Planner 获得了初步查询计划之后,我们还可以通过 Optimizer 进行优化,提升查询的效率,并生成最终查询计划。
- 在确定了最终的查询计划之后,我们需要通过 Executor 实现最终的算子,利用算子来真正实现我们的查询计划,将原先的 PlanNode 更换成我们实现的 Executor ,这也是我们本部分的主要内容。
值得注意的是,在 Bustub 中,对于实现 Optimizer ,我们在遍历 Planner 生成的初步查询计划时,利用预先设定的规则实现对 PlanNode 的优化,如将 Limit + Sort 合并为 TopN ,这需要我们提前设定好规则;对于实现 Executor ,我们采用 Iterator Model (火山模型),其中每个算子内部的 Init()
实现算子的初始化,Next()
用于向子节点请求数据,其中,火山模型的优点在于占用内存较小,一次只使用一条数据,但这也导致我们会频繁调用函数,特别是虚函数,带来较大开销,而我们在执行查询时,也是自上向下请求数据。最终,当根节点获得了所有需要查询的数据时,就实现了 SQL 语句的功能。
在 Bustub 中,有一个 Catalog 用于保存 table 的一系列信息,其中需要维护从 table id 和 table name 到 table info 的映射关系,从而可以通过相应键进行查询。
在 table info 中保存了一张表的模式、名字、id 和指向 table heap 的指针。
在 table heap 中有着一系列的 table page ,构成了双向链表的结构,能够遍历整张表获取相应的信息,而 table heap 仅保存其中第一个 table page 的 page id 。
在 table page 内部,他是 page 的一个子类,其中,真正储存了数据的 tuple 从高地址开始向 table page 尾部进行插入。
储存数据的 tuple 对应着表中的一行数据,每个 tuple 都有 RID 唯一对应,其中 RID 由
page id + slot num 构成,而其中的 value 则是每个字段的具体值,由 table info 中的模式来决定。
任务 #1 - 访问方法执行程序
对于任务一,我们需要实现四个算子:SeqScanExecutor 、InsertExecutor 、DeleteExecutor 、IndexScanExecutor 。
SeqScanExecutor
SeqScanExecutor 的功能是需要实现读取给定 table 中的所有 tuple 。在构造函数SeqScanExecutor
中,我们利用GetCatalog
获得 Bustub 中的 Catalog ,并根据table_oid_
利用GetTable
获得对应的table_info_
,从而获得后续的存放表的 page 。在Init
中,我们将当前 table 的指针指向初始的 begin 位置。在Next
函数中,我们首先判断当前 table 的迭代器是否指向末尾,若是则直接返回 false 。而后我们利用指针直接获得当前迭代器对应的 tuple ,并更新 tuple 相应的 rid ,最终移动迭代器。
InsertExecutor
InsertExecutor 的功能是根据子节点算子的结果向当前表中插入项。在构造函数InsertExecutor
中,我们同样利用GetCatalog
获得 Bustub 中的 Catalog ,并根据table_oid_
利用GetTable
获得对应的table_info_
,从而获得后续的存放表的 page 。值得注意的是,由于此处子算子使用的是 unique_ptr ,因此我们在初始化时不能复制只能使用std::move
。在Init
中,我们首先需要初始化子算子,而后考虑到我们插入项之后可能会影响到当前表中已经存在的索引,我们需要根据当前的 table_info_ 获取相应的索引列表。在Next
函数中,我们首先从子算子中获得我们需要插入的 tuple 和对应的 RID 。而后我们利用InsertTuple
向对应的表中插入 tuple 。若插入成功,我们需要遍历当前表中的所有索引,并将新插入的记录插入索引中。最终,我们将插入的内容和对应的模式储存到 tuple 并返回 true ,其中 tuple 中会包含一个 integer value 用于表示由多少行受到影响。
DeleteExecutor
DeleteExecutor 的功能是根据子节点算子的结果向当前表中删除项。在构造函数DeleteExecutor
中,我们同样利用GetCatalog
获得 Bustub 中的 Catalog ,并根据table_oid_
利用GetTable
获得对应的table_info_
,从而获得后续的存放表的 page 。在Init
中,我们首先需要初始化子算子,而后考虑到我们删除项之后可能会影响到当前表中已经存在的索引,我们需要根据当前的 table_info_ 获取相应的索引列表。在Next
函数中,我们首先从子算子中获得我们需要删除的 tuple 和对应的 RID 。而后我们利用MarkDelete
向对应的表中删除 tuple ,值得注意的是,在 Bustub 中,我们并不是在此时直接删除,而是将 tuple 标记为删除状态,也就是逻辑删除。若删除成功,我们需要遍历当前表中的所有索引,并在索引中删除对应的项。最终,我们将删除的内容和对应的模式储存到 tuple 并返回 true ,其中 tuple 中会包含一个 integer value 用于表示由多少行受到影响。
IndexScanExecutor
IndexScanExecutor 的功能是根据 Project 2 中实现的 B+ 树索引,遍历叶子节点查找我们需要的项。由于我们实现的是非聚簇索引,在叶子节点只能获取到 RID,需要拿着 RID 去 table 查询对应的 tuple。在构造函数IndexScanExecutor
中,我们同样利用GetCatalog
获得 Bustub 中的 Catalog ,并根据table_oid_
利用GetTable
获得对应的table_info_
,从而获得后续的存放表的 page ,同时我们还需要初始化需要使用的 table_info_ 、tree_ 、iter_ 。在Init
中,我们需要初始化的内容都已经在构造函数中得到初始化。在Next
函数中,我们首先判断当前 B+ 树的指针是否指向树的末尾,若是则直接返回 false 。否则我们获得当前指针对应的节点的 RID ,并从 table_ 中获得对应的 tuple ,移动指针并返回结果。
任务 #2 - 聚合和加入执行程序
对于任务二,我们需要实现三个算子:AggregationExecutor、NestedLoopJoinExecutor、NestIndexJoinExecutor 。
AggregationExecutor
AggregationExecutor 的功能是实现一个聚集功能,考虑到我们可能会对某一列上的元素进行一些常见的操作,我们可以利用聚集函数直接获得相应值而不需要一个个去遍历计算。在 Bustub 中,我们利用哈希表来储存需要 group by 的字段的数组和需要 aggregate 的字段的数组。因此,我们需要在Init
中便计算出所有结果并进行暂存,而在Next
中将获得的结果一条条 emit 。在构造函数AggregationExecutor
中,我们对所有使用到的 plan 节点、子算子节点、哈希表、哈希表指针进行初始化。在Init
中,我们根据子算子中 emit 的记录调用InsertCombine
插入我们维护的哈希表中,若最终哈希表为空或只有一列属性则添加初值,最终初始化哈希表的指针。此外,我们还需要实现CombineAggregateValues
,根据我们需要执行的聚集函数,包括 count(*) 、count 、sum 、min 、max ,并将计算出的结果储存到哈希表中。在Next
中,我们首先判断当前哈希表的指针是否指向末尾,若是则直接返回 false 。而后我们输出当前的键值对并附上模式,移动迭代器后返回。
NestedLoopJoinExecutor
NestedLoopJoinExecutor 的功能是实现一个连表功能,其特点在于将外表中的每一条 tuple 与内表中的每一条 tuple 进行比较,在内表循环完成之后读取外表的下一条 tuple ,效率比较低。在构造函数NestedLoopJoinExecutor
中,我们初始化对应的 plan 节点、左子算子和右子算子。在Init
中,我们初始化左右算子并将右子算子的内容全部压入数组中。在Next
中,我们将左子算子此时 emit 的 tuple 与右子算子返回的数组中的每一条利用EvaluateJoin
进行比较,若匹配则将连表后的新 tuple 压入最终结果的数组中。若此时是左连表,则将左表中的内容和右表中的内容或空内容压入最终结果中。值得注意的是,为了避免右子算子的结果被全部使用后只会返回 false 的情况,我们使用数组提前储存右子算子中的所有结果。同时为了避免左子算子的结果可能与右子算子中的多个 tuple 相匹配的情况,我们在算子中暂存左子算子的结果并优先尝试进行匹配,此外我们还记录在右子算子的列表中上一次匹配的结果。若遍历右子算子的列表仍未匹配,再去要求左子算子传来下一个 tuple 。
NestIndexJoinExecutor
NestIndexJoinExecutor的功能是实现一个连表功能,与上一功能的区别在于我们直接使用索引进行查询,能够大大加快速度。若现 JOIN ON 右边的字段上建了 index,则 Optimizer 会将 NestedLoopJoin 优化为 NestedIndexJoin。在构造函数NestedLoopJoinExecutor
中,我们初始化对应的 plan 节点、子算子和索引信息、表信息和 B+ 树。在Next
中,我们首先获得子算子中的 tuple ,而后在对内表进行匹配时,直接使用 tuple 中储存的信息在索引中进行搜索,并返回相应的结果。
任务 #3 - 排序 + 限制执行程序和 top-N 优化
对于任务三,我们需要实现三个算子:SortExecutor、LimitExecutor、TopNExecutor ,同时优化 Optimize ,将连续出现的 limit 和 sort 的 plan 节点直接优化 topn 节点。
SortExecutor
SortExecutor的功能是按 ORDER BY 的字段升序或降序排序。在构造函数SortExecutor
中,我们初始化当前的 plan 节点和子算子。在Init
中,我们首先将子算子 emit 的结果全部压入数组中。而后我们利用std::sort
实现对数组的排序,其中需要我们调用CompareGreaterThan
实现对于元素的比较。在Next
中,我们返回当前的 tuple 与 RID 并移动迭代器。
LimitExecutor
LimitExecutor的功能是利用自己维护的变量 count 记录当前已经 emit 多少个 tuple 。若下层算子为空或 count 抵达上限则不再进行 emit 。在构造函数LimitExecutor
中,我们初始化当前的 plan 节点和子算子。在Init
中,我们初始化子算子与 count_ 。在Next
中,我们判断子算子是否还能 emit 或当前的 count_ 是否超过上限。
TopNExecutor
TopNExecutor的功能是计算当前排序的前 N 个 tuple 。我们只需要将以上两个算子相结合,先排序后输出前 N 个 tuple 即可。
Sort + Limit As TopN
此处我们需要优化 Optimize ,将连续出现的 limit 和 sort 的 plan 节点直接优化 topn 节点。我们需要实现 OptimizeSortLimitAsTopN ,其中我们首先获得当前节点的所有子算子节点并递归调用函数 OptimizeSortLimitAsTopN ,而后我们将优化后的子算子加入数组,并克隆一个新的子算子用于修改。而后我们首先判断当前的子节点是否为 Limit ,而子节点的子节点是否为 Sort ,只有严格按照这个格式我们才能够确定可以使用 TopNPlanNode 代替这两个节点。
值得注意的是,在利用规则对当前的原始 plan 树进行优化时,我们实际上是对当前树进行后续遍历,自底向上使用规则来改写节点。若我们当前的遍历的节点满足我们的改写条件则对当前的树进行优化。
相关文章:

CMU15-445 Project.3总结
在线测试 Project #3 - Query Execution 以下是Project #3的网址,2022FALL的Project #3是实现一个查询执行,实现一系列算子,用于实现数据库内的SQL计算。项目中的 Query Execution 主要分为三个任务: Access Method Executors…...

002+limou+HTML——(2)HTML文档
000、前言 一般来说一个静态网页拥有四种元素:文字、图片、超链接、音频和视频(注意,即使在web网页中植入Javascript语言,也不一定是动态网页,真正的动态网页判断标准:是否和服务器产生交互) …...

红外传感器模块与 Arduino 连接
红外传感器模块与 Arduino 连接 原文地址 Arduino 红外传感器接口 红外**接近传感器或红外传感器它发射红外光以感知周围环境,并可用于检测物体的运动。由于这是一个无源传感器,它只能测量红外辐射。如果您曾经尝试过设计避障机器人或任何其他基于接近…...

NC xml配置文件不能生产java文件
在NC开发过程中,新增、或修改了xml文件,在开发工具eclipse中生成或重新生成Java文件,发现生成不了相对应的Java文件。如下图,选中xml文件后,右键点击SpringXml to Java 这种情况其实一般都是xml配置文件有问题&#…...
华为OD机试 - 五键键盘(C 语言解题)【独家】
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 使用说明本期题目:五键键盘…...

Kubernetes Service简介
Service 之前我们了解了Pod的基本用法,我们也了解到Pod的生命是有限的,死亡过后不会复活了。我们后面学习到的RC和Deployment可以用来动态的创建和销毁Pod。尽管每个Pod都有自己的IP地址,但是如果Pod重新启动了的话那么他的IP很有可能也就变…...

【c++类与对象 】
目录:前言一、基础引入1.类的定义2.类的权限3.类的封装4.类的实例化5.计算类对象的大小结构体内存对齐规则空类的大小二、this指针this引入this指针的特性经典例题三、类的六个默认成员函数1、构造 && 析构构造函数析构函数2、拷贝 && 赋值拷贝构造…...

【C++】内联函数auto范围for循环nullptr
🏖️作者:malloc不出对象 ⛺专栏:C的学习之路 👦个人简介:一名双非本科院校大二在读的科班编程菜鸟,努力编程只为赶上各位大佬的步伐🙈🙈 目录前言一、内联函数1.1 内联函数概念1.2…...

运维效率狂飙,都在告警管理上
随着数字化进程的加速,企业IT设备和系统越来越多,告警和流程中断风险也随之增加。每套系统和工具发出的警报,听起来像是一场喧嚣的聚会,各自谈论不同的话题。更糟糕的是,安全和运维团队正在逐渐丧失对告警的敏感度&…...

【每日随笔】中国当前社会阶层 ( 技术无关 | 随便写写 )
文章目录一、阶层划分根据收入划分的阶层根据分工逻辑划分根据权利划分二、根据社会地位和掌握的资源划分的阶层三、赚钱的方式四、如何进入高阶层看了一个有意思的视频 , 讲的是中国当前的社会阶层 , 感觉好有道理 , 搜索了一些资料 ; 参考资料 : 关于中国的社会阶层社会在分…...

【13种css选择器】学css选择器,这一篇就够了
举例形象让你学会,不搞官方话css所有的选择器相邻兄弟选择器后续兄弟选择器后代选择器子代选择器并集选择器(多重选择器)属性选择器伪类选择器伪元素选择器class选择器(类选择器)id选择器*选择器(通配符选择器)标签选择…...

1-1 微服务架构概述
文章目录微服务架构概述1-1. 系统进化理论概述集中式系统:分布式系统1-2. 系统进化理论背景1-3. 什么是微服务架构1-4. 微服务架构的优缺点1-5. 为什么选择 Spring Cloud 构建微服务认识 Spring Cloud2-1. Spring Cloud 是什么2-2. Spring Cloud 的版本2-3 Spring C…...
uniapp传参
//子传父子页面:sumbit() {console.log(this.formData, 传过去的内容对象)let pages getCurrentPages();let prevPage pages[pages.length - 2]; //上一个页面prevPage.$vm.getParams(this.formData); //重点$vmuni.navigateBack();},父页面接收:metho…...

面试官:说说你对 TypeScript 中函数的理解?与 JavaScript 函数的区别?
一、是什么 函数是 JavaScript 应用程序的基础,帮助我们实现抽象层、模拟类、信息隐藏和模块 在 TypeScript 里,虽然已经支持类、命名空间和模块,但函数仍然是主要定义行为的方式,TypeScript 为 JavaScript 函数添加了额外的功能…...

【测试】HD-G2L-IO评估板测试结果表
1. 测试对象HD-G2L-IOT基于HD-G2L-CORE V2.0工业级核心板设计,双路千兆网口、双路CAN-bus、2路RS-232、2路RS-485、DSI、LCD、4G/5G、WiFi、CSI摄像头接口等,接口丰富,适用于工业现场应用需求,亦方便用户评估核心板及CPU的性能。H…...

[2.2.1]进程管理——调度的概念、层次
文章目录第二章 进程管理调度的概念、层次(一)调度的基本概念(二)调度的三个层次(1)高级调度(2)低级调度(3)中级调度补充知识:进程的挂起态与七状…...

【JavaScript UI库和框架】上海道宁与Webix为您提供用于跨平台Web应用程序开发的JS框架及UI小部件
Webix是Javascript库 一种软件产品 用于加速Web开发的 JavaScript UI库和框架 Webix用于跨平台Web应用程序开发的JS框架,为您提供102个UI小部件和功能丰富的CSS/HTML5 JavaScript控件 开发商介绍 Webix团队由由热衷于创建高质量网络产品的专业人士组成ÿ…...

【微信小程序】-- WXS 脚本(二十九)
💌 所属专栏:【微信小程序开发教程】 😀 作 者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! &…...

案例19-遇见问题的临时解决方案和最终解决方案
目录1、背景介绍2、两种解决方案的概念1、临时解决方案:2、最终解决方案:3、排查问题过程4、总结站在用户的角度思考作为软件开发者5、升华1、背景介绍 首先说明这是系统很早之前的时候的一个功能,当时和学习通还有很强的耦合关系。在学习通…...

自指(Self-reference)
文章目录1. 在逻辑、数学和计算方面2. 在生物学中3. 在艺术4. 在语言中5. 在流行文化中6. 在法律中自我参照(Self-reference)是一个涉及指代自己或自己的属性、特征或行为的概念。它可以发生在语言、逻辑、数学、哲学和其他领域。 在自然语言或形式语言…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

tauri项目,如何在rust端读取电脑环境变量
如果想在前端通过调用来获取环境变量的值,可以通过标准的依赖: std::env::var(name).ok() 想在前端通过调用来获取,可以写一个command函数: #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...

实战设计模式之模板方法模式
概述 模板方法模式定义了一个操作中的算法骨架,并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤。简单来说,就是在一个方法中定义了要执行的步骤顺序或算法框架,但允许子类…...

Matlab实现任意伪彩色图像可视化显示
Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中,如何展示好看的实验结果图像非常重要!!! 1、灰度原始图像 灰度图像每个像素点只有一个数值,代表该点的亮度(或…...
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里 脚本1 #!/bin/bash #定义变量 ip10.1.1 #循环去ping主机的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…...