CMU15-445 Project.3总结

news2025/1/25 9:16:19

在线测试
在这里插入图片描述

Project #3 - Query Execution

以下是Project #3的网址,2022FALL的Project #3是实现一个查询执行,实现一系列算子,用于实现数据库内的SQL计算。项目中的 Query Execution 主要分为三个任务:

  1. Access Method Executors : 需要实现 SeqScan、Insert、Delete、IndexScan 四个算子。
  2. Aggregation and Join Executors : 需要实现 Aggregation、NestedLoopJoin、NestedIndexJoin 三个算子。
  3. Sort + Limit Executors and Top-N Optimization : 需要实现 Sort、Limit、TopN 三个算子,以及实现将 Sort + Limit 优化为 TopN 算子。

我们首先需要了解 SQL 语句如何在 Bustub 中实现相应功能:

  1. 一条 SQL 语句首先通过 Parser 生成一棵抽象语法树 AST 。在 Bustub 中通过调用第三方库libpg_query 将一条 SQL 语句解析为 AST 。
  2. 在获得 AST 之后, Binder 将抽象的词语绑定到数据库中的实体类中,最终获得一个可以被 Bustub 理解的 Bustub AST 。
  3. 获得了绑定的语法树之后,Planner 遍历这棵树,生成相应的查询计划。我们可以将 Planner 理解为树中的每一个节点,需要根据查询计划进行一定的运算。
  4. 在通过 Planner 获得了初步查询计划之后,我们还可以通过 Optimizer 进行优化,提升查询的效率,并生成最终查询计划。
  5. 在确定了最终的查询计划之后,我们需要通过 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 树进行优化时,我们实际上是对当前树进行后续遍历,自底向上使用规则来改写节点。若我们当前的遍历的节点满足我们的改写条件则对当前的树进行优化。

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

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

相关文章

九龙证券|整合大年 钢企迎来盈亏平衡新周期

经历上一年的“至暗时间”后,2023年的钢铁工业正从盈亏平衡的新窗口探出面来。证券时报记者从多家钢企和钢贸商处确认,本年以来钢材价格试探性上涨频现,量价、开工率、库存等指标都呈现向好趋向。 如果说供应侧结构性变革是推动上一轮钢铁工业…

HTML 简介

文章目录HTML 简介实例解析什么是HTML?HTML 标签HTML 元素Web 浏览器HTML 网页结构HTML版本<!DOCTYPE> 声明通用声明HTML5HTML 4.01XHTML 1.0中文编码HTML 简介 HTML 实例 <!DOCTYPE html> <html><head><meta charset"utf-8"><ti…

Spring——数据源对象管理和Spring加载properties文件

前面一直都是在管理自己内部创建的对象&#xff0c;这个是管理外部的对象。 这里先使用阿里巴巴的druid来演示。需要在pom.xml中添加如下的依赖 <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1…

Leetcode.2416 字符串的前缀分数和

题目链接 Leetcode.2416 字符串的前缀分数和 Rating &#xff1a; 1725 题目描述 给你一个长度为 n的数组 words&#xff0c;该数组由 非空 字符串组成。 定义字符串 word的 分数 等于以 word作为 前缀 的 words[i]的数目。 例如&#xff0c;如果 words ["a", &q…

C++STL详解(五)——list的介绍与使用

文章目录list的介绍list的使用list的定义方法list迭代器失效问题list插入和删除inserteraselist迭代器的使用begin&#xff0c;end 和 rbegin&#xff0c;rendlist元素访问front 和 backlist容量控制与数据清理resizeclearlist操作函数spliceremove 和 remove_ifuniquemergerev…

安卓开发之动态设置网络访问地址

之前开发程序联测测接口的时候&#xff0c;因为要和不同的后台人员调接口&#xff0c;所以经常要先把程序里的ip地址改成后台人员给我的。每次都要先修改ip地址&#xff0c;之后编译运行一下&#xff0c;才能测试。但要是换了个后台人员&#xff0c;或者同时和2个后台人员测接口…

Android提词器实现富文本样式

前提前一段时间做了一个程序&#xff0c;提词器APP&#xff0c;结合greendao保存数据。最近新增了一个需求&#xff0c;实现部分文字富文本的展现。师傅找了一个网上的SDK&#xff0c;但是在集成的时候总是出问题&#xff0c;我又不想把项目挪进来&#xff0c;感觉很麻烦&#…

Oracle P6 Professional相比与Microsoft Project的8个优势

目录 引言 1. 自上而下的调度 2. 努力程度 (LOE) 活动 3. 最长路径 4. 多浮动路径分析功能 6.预算材料成本 7. 开始和完成里程碑 8. 工作公式类型 概括 引言 哪种日程安排工具更适合您的情况&#xff0c;Oracle Primavera P6 还是 Microsoft Project(MSP) 经常有一些…

MySQL8.0Linux安装及主从的搭建

MySQL8.0Linux安装教程 下载并安装 需要说明的一点是我使用的是SSH secure shell Client连接linux系统的&#xff0c;它的用法和命令窗口差不多。界面如图&#xff1a;一样的使用Linux命令操作。 话不多说 第一步&#xff1a; 1&#xff09;、切换到 /usr/local下 cd /usr/…

已解决hint : See above for output from the failure.

已解决&#xff08;pip install wxPython安装失败&#xff09;error: legacy-instal1-failure Encountered error while trying to install package.wxPython note: This is an issue with the package mentioned above&#xff0c;not pip. hint : See above for output from …

关于世界坐标系,相机坐标系,图像坐标系,像素坐标系的一些理解

关于世界坐标系&#xff0c;相机坐标系&#xff0c;图像坐标系&#xff0c;像素坐标系的一些理解前言一、各坐标系的含义二、坐标系转换1.世界坐标系与相机坐标系&#xff08;旋转与平移&#xff09;2.相机坐标系与图像坐标系&#xff08;透视&#xff09;3.图像坐标系与像素坐…

【UE4 RTS游戏】02-摄像机运动_完成摄像机在X轴上运动的相关步骤

效果通过控制键盘WS键使得“CameraPawn”进行前后移动步骤将landscape的Z轴位置更改为0删除“PostProcessVolume”将“LightmassImportanceVolume”移入Lighting文件夹内新建一个蓝图类&#xff0c;父类是Pawn&#xff0c;命名为“CameraPawn”将“MyController”重命名为“Cam…

详解JVM

详解JVM 最近学习了&#xff1a;周志明《深入理解高并发编程》&#xff1b;&#xff1b; 特此简要对学习做了部分总结&#xff0c;方便后续对JVM相关知识的完善和巩固&#xff1b; 若想深入了解学习&#xff0c;可阅读上述参考原著&#xff1b; Java内存区域与OOM 运行时数据…

大数据 | (三)centos7图形界面无法执行yum命令

大家好&#xff0c;今天是三八女神节了&#xff01; 你知道吗&#xff1f;世界上第一位电脑程序设计师是名女性&#xff0c;Ada Lovelace (1815-1852)。 她是一位英国数学家兼作家&#xff0c;第一位主张计算机不只可以用来算数的人&#xff0c;也发表了第一段分析机用的演算…

vector中迭代器失效的问题及解决办法

目录 vector常用接口 vector 迭代器失效问题 vector中深浅拷贝问题 vector的数据安排以及操作方式&#xff0c;与array非常相似。两者的唯一差别在于空间的运用的灵活性。array 是静态空间&#xff0c;一旦配置了就不能改变&#xff1b;要换个大(或小) 一点的房子&#x…

CorelDRAW Graphics Suite2023更新内容介绍

懂设计的职场人都知道这款软件&#xff0c;CorelDRAW是一款非常高效的矢量图形设计软件。CorelDRAW操作界面简洁易懂&#xff0c;能够为用户提供精确地创建物体的尺寸和位置的功能&#xff0c;减少点击步骤&#xff0c;提高设计效率&#xff0c;节省设计时间。功能比普通的美图…

简单理解TransFormer

背景:听了李宏毅老师关于transformer的讲解&#xff0c;觉得有必要记录一下&#xff0c;里面的PPT都是李宏毅老师的内容(不喜勿喷)1.self-attention在介绍transformer之前&#xff0c;必须先了解self-attention(1) 先将X输入Embedding(a Wx), 然后a乘相关的权重&#xff0c;生…

Day11-网页布局实战-CSS3动画

文章目录一 CSS3动画1 2D动画案例1-鼠标输入移入DIV 让图片旋转90度案例2-鼠标输入移入DIV 缩放图片案例3-贯穿项目-DIV移动2 animation动画播放器案例1-基础案例案例2-使用百分比关键帧定义动画案例3-旋转的图片案例4-贯穿案例-轮播图3 多余文本省略号...代替案例1-多余文本..…

一 Go环境搭建

1. 下载地址 https://golang.google.cn/dl/ 傻瓜式安装&#xff0c;自动会配置path的变量&#xff0c;安装完成后可以使用go version 查看当前安装的版本 本文使用目前最新的1.20.2版本 2. 配置go环境 cmd控制栏打开输入以下命令&#xff08;如果cmd有问题可以尝试powershe…

340秒语音芯片,轻松实现语音交互,畅享智能生活WTV380语音ic方案

随着智能家居、安防报警、宠物用品 等&#xff0c;智能设备的普及&#xff0c;语音交互技术正在逐渐成为人机交互的主要方式之一。而如何实现稳定高效的语音交互&#xff0c;就需要借助先进的语音芯片技术。今天&#xff0c;我们介绍的是一款高性能的语音芯片——WTV380&#x…