MIT6.5830 Lab1-GoDB实验记录(五)

news2024/11/29 12:53:51

MIT6.5830 Lab1-GoDB实验记录(五) – WhiteNight's Site

标签:Golang

完成了Exercise 1,还有四个Exercise在等着我,慢慢来吧。

实验准备

了解缓冲池

缓冲池,俗称BP。相关的概念还有数据页和缓存页。页(Pages)的概念和操作系统中“分页”的概念是一样的,指的都是把逻辑地址空间分为若干同等大小的页,并从0开始编号。

而缓冲池(Buffer Pool),简单来说它的作用就是就是读磁盘中的页,再把页写入回磁盘。下列这张图就很好的解释了缓冲池的工作流程。

实验步骤

完成getPage()

在本次实验中,我们只需要补全buffer_pool.go中的getPage()函数,就完成一个函数?还是比较简单的…吧?

先看下实验要求

2.4. BufferPool
The buffer pool (class BufferPool in GoDB) is responsible for caching pages in memory that have been recently read from disk. All operators read and write pages from various files on disk through the buffer pool. It consists of a fixed number of pages, defined by the numPages parameter to the BufferPool constructor NewBufferPool.

For this lab, you only need to implement the constructor and the BufferPool.getPage() method used by the HeapFile iterator. The buffer pool stores structs that implement the Page interface; these pages can be read from underlying database files (such as a heap file) which implement the DBFile interface using the readPage method. The BufferPool should store up to numPages pages. If more than numPages requests are made for different pages, you should evict one of them according to an eviction policy of your choice. Note that you should not evict dirty pages (pages where the Page method isDirty() returns true), for reasons we will explain when we discuss transactions later in the class. You don't need to worry about locking in lab 1.

再来看看练习2给的实验要求注释

// Retrieve the specified page from the specified DBFile (e.g., a HeapFile), on
// behalf of the specified transaction. If a page is not cached in the buffer pool,
// you can read it from disk uing [DBFile.readPage]. If the buffer pool is full (i.e.,
// already stores numPages pages), a page should be evicted.  Should not evict
// pages that are dirty, as this would violate NO STEAL. If the buffer pool is
// full of dirty pages, you should return an error. For lab 1, you do not need to
// implement locking or deadlock detection. [For future labs, before returning the page,
// attempt to lock it with the specified permission. If the lock is
// unavailable, should block until the lock is free. If a deadlock occurs, abort
// one of the transactions in the deadlock]. You will likely want to store a list
// of pages in the BufferPool in a map keyed by the [DBFile.pageKey].

写者注

uing?一眼using错字。是时候让我狠狠的提交pull request了。

好吧,我就知道没这么简单。先总结一下我们要做什么

  • 如果缓冲池没有第x页,从磁盘中读取;如果缓冲池满了,自己写个调度算法驱逐页(但不驱逐脏页)。
  • 如果缓冲池全是脏页(脏页和NO STEAL的概念后续开个文章仔细讲讲),返回一个error。
  • 写一个分布式锁,而且要带特定的perm。同时还要释放死锁占用的其中某个事务。

这还不是最折磨的,更折磨的是这个:

Exercise 2
Implement the getPage() method in:

buffer_pool.go
There is a unit test buffer_pool_test.go, but you will not be able to pass this test until you implement the heap file and heap page methods below. You will also test the functionality of the buffer pool when you implement your heap file iterator.

“完成Exercise 4之前,Exercise 2的test是无法通过的”。即不像Exercise 1那样“所见即所得”,没test连自己写的对不对都不知道。而且在Exercise 4又要调用这里的GetPage,但在Exercise2和4之间还夹着个Exercise 3要完成……三个Exercise缺一不可,令人头大。

So,综合考虑之后,我决定先完成heap_page,再完成heap_file,最后再来完成GetPage()。

实验步骤

琢磨heap_page

打开heap_page.go的那一刻,我就知道——要寄了。

从接口到结构体都要自己补全,这可不是一般的难啊。heap page和heap file的关系我倒是知道,但是具体的数据结构我还真不知道。

那不知道能怎么办呢?找现有的源码例子慢慢看呗。我自行学习了InnoDB的heap page结构后,大致能明白自己该完成什么功能了。

比如这一段heap_page.go中的注释。

In addition, all pages are PageSize bytes.  They begin with a header with a 32
bit integer with the number of slots (tuples), and a second 32 bit integer with
the number of used slots.

这就是在说:每一个page的大小都是固定的,而且都有一个header(标头)。这个Header有两个成员:一个是总共的slot插槽数量,另一个是已用的slot数量。而slot则用于存放元组。

很合理嘛,一个页一共能存多少个元组,存了x个元组后还能存多少个元组?这个页的编号是什么?我们通过什么方式访问该page?(一般都是通过指针的方式访问)这些肯定是要我们自己规定的。那我们可以先把header结构写上。

同时,不要忘了元组Tuple是不带字段的,即我们还需要指明这些记录的数据是属于哪个字段的。而这部分的工作在tupledesc中就已经完成了。

type Header struct {
	TotalSlots int32
	UsedSlots  int32
}
type heapPage struct {
	// TODO: some code goes here
	hdr   Header
	tuple []*Tuple
	desc  *TupleDesc

	file   *HeapFile
	pageNo int
}

写者注

上面这些内容是我在10月份写的,而以下内容是11月写的,中间因为各种事情(实习啊投简历啊这些)耽搁了一段时间。而且在此之前我从未接触过copilot。所以我建议各位自带个copilot会比较好一些。

接下来要初始化heap page。heap page的计算公式在注释中已经给我们了。我们只需要判断字段是int还是string类型,再单独计算大小最后求和即可。

func newHeapPage(desc *TupleDesc, pageNo int, f *HeapFile) *heapPage {
	// TODO: some code goes here
	bytesPerTuple := 0
	for _, field := range desc.Fields {
		if field.Ftype == StringType {
			bytesPerTuple += int(unsafe.Sizeof(byte('a'))) * StringLength
		} else if field.Ftype == IntType {
			bytesPerTuple += int(unsafe.Sizeof(int64(0)))
		}
	}
	remPageSize := PageSize - 8
	numSlots := remPageSize / bytesPerTuple

	return &heapPage{
		Hdr:    Header{TotalSlots: int32(numSlots), UsedSlots: 0},
		Tuple:  make([]*Tuple, numSlots),
		Desc:   desc,
		File:   f,
		PageNo: pageNo,
	}
}

写者注

麻了,我敲几段copilot就给我自动补全了。有一种AIGC的美。再这样下去就要变成离开copilot就活不下去的人了

😭

接下来是getNumSlot,说实话没看懂要补全啥,先写个return看看。

func (h *heapPage) getNumSlots() int {
	// TODO: some code goes here

	return int(h.Hdr.TotalSlots) //replace me
}

写者注

麻了,copilot给的代码太抽象了,直接跑肯定是跑不了的,唯一用得上的就是翻译和代码解释功能。

接下来就是插入tuple。哪个槽是空的就往哪里插入tuple,同时要给Tuple的rid赋值并返回。那我们先来回顾一下rid是什么。

type Tuple struct {
	Desc   TupleDesc
	Fields []DBValue
	Rid    recordID //used to track the page and position this page was read from
}
type recordID interface {
}

用于追踪这个元组在xx页的xx位置?但这里给的是interface,所以我们传个结构体进去也没问题。

type RecordId struct {
	PageNo int
	SlotNo int32
}

插入元组,如果page满了插不进去那就返回err。如果没满,那就遍历page的tuple看看哪个位置的tuple是nil。

找到空位之后除了插入元组,还需要更新该记录所在的页和位置(i)。插入完之后记得直接return,毕竟是只插入单个记录,而不是把空位全填上。

func (h *heapPage) insertTuple(t *Tuple) (recordID, error) {
	// TODO: some code goes here
	rid := RecordID{PageNo: h.PageNo, SlotNo: 0}
	if h.Hdr.UsedSlots == h.Hdr.TotalSlots {
		return rid, errors.New("no free slots")
	}
	for i, tuple := range h.Tuple {
		if tuple == nil {
			h.Tuple[i] = t
			rid.SlotNo = i
			h.Tuple[i].Rid = rid
			h.Hdr.UsedSlots += 1
			return rid, nil
		}
	}
	return rid, errors.New("Can't insert Tuple") //replace me
}

搞定之后向跑下heap_page_test.go试试,结果发现会报错。发现还有一个tupleIter函数没补全。那先补全tupleter之后跑个test看看,不然心里不踏实。

panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x30 pc=0x18541a]

看看tupleter,为什么要遍历page的tuple?你如果常写SQL题就肯定能明白为什么。最经典的例子就是两个NOT EXISTS嵌套循环,最内层的NOT EXISTS就是很明显的逐条匹配。

所以tupleter返回的才是一个函数,我们要在这个函数里遍历tuple中的所有非空记录,并且每次都返回新的非空记录。

有了个思路,那就开写咯。迭代的话不用多说,跳过空插槽,直到最后一个插槽为止。不过还要注意最多有n个插槽≠最多有n个tuple。我一开始没注意老是过不了测试,一直报访问越界的错误。后面仔细看了下才发现i可能会导致数组越界。

这里根据注释还需要给返回的tuple初始化rid,rid直接取p的就好了。

func (p *heapPage) tupleIter() func() (*Tuple, error) {
	// TODO: some code goes here
	i := 0
	return func() (*Tuple, error) {
		for i < int(p.Hdr.TotalSlots) && p.Tuple[i] == nil {
			i++
		}
		if i <= int(p.Hdr.TotalSlots) && i < len(p.Tuple) {
			if p.Tuple[i] != nil {
				tuple := p.Tuple[i]
				tuple.Rid = p.Tuple[i].Rid
				i++
				return tuple, nil
			}
		}
		return nil, nil
	}
}

跑一下TestInsertHeapPage,过。

发现还有一个TestHeapPageInsertTuple可以test一下,而且刚好发现里面有我们前面提到的“不知道是干啥”的getNumSlots。

定睛一看,这不就是返回page还剩多少空插槽的函数嘛。那修正一下前面的getNumSlots

func (h *heapPage) getNumSlots() int {
	// TODO: some code goes here
	free := h.Hdr.TotalSlots - h.Hdr.UsedSlots
	return int(free) //replace me
}

写者注

这里有个…不知道算不算坑的坑。为什么这里偏偏是int,而前面我们定义插槽数也好,已用插槽数也好用的都是int32。如果你把鼠标移到int上面会看到注释说“int是一个大小至少为32的数据类型”。
至少,这就意味着int是区别于int32的。所以这里还是要做个强制类型转换。但我是没搞懂为啥前面定义slot不直接用int。省空间?不至于吧,这也就64bit。
事出有因,但我看不懂。我推测是通过int32限制了page的最大插槽数量。毕竟int64最大可取到9,223,372,036,854,775,807。这对于heap_page来说有点太大了。但这也只是我觉得比较合理的推测,人注释也没说,找个自己能信服的理由就过了吧。

跑一下TestHeapPageInsertTuple,过。

本次记录先到这里,篇幅有点太长了。heap_page的剩余函数我们在下篇文章继续补全。

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

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

相关文章

VueX 模块化和namespace

当我们的项目很大的时候&#xff0c;VueX中的代码会越来越多&#xff0c;会有处理数据的&#xff0c;处理人员列表的&#xff0c;处理订单的... 如果我们将这些东西都写在一个state、actions和mutations中的话&#xff0c;就非常不方便后期的维护。 所以我们引入了VueX的模块…

Linux的指令和用途(持续更新)

1. 基本指令&#xff1a; 概念介绍&#xff1a; 1. 目录&#x1f7f0;文件夹 Linux指令用法说明who查看哪些人登陆我的机器whoami (who am i)查看当前账号是谁 pwd 查看当前我所在的目录clear 清屏 tree 目录名&#xff08;文件夹名&#xff09;以树形结构列出该文件夹下的所有…

深度学习之基于Tensorflow人脸面部表情识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 基于Tensorflow的人脸面部表情识别系统是一种基于深度学习技术的图像处理应用&#xff0c;该系统主要通过人脸图像数…

【Unity实战】最全面的库存系统(五)

文章目录 先来看看最终效果前言配置商店系统数据创建另一个NPC绘制商店UI控制商店开关列出商品添加和删除物品功能添加商品到购物车购买商品购物车删除物品商店预览效果购买和出售切换出售功能保存商店数据快捷栏物品切换和使用完结 先来看看最终效果 前言 本期也是最好一期&a…

最新 vie-vite框架下 jtopo安装使用

官方地址 官方源码 安装下载 1.官方好像都没有给git地址&#xff0c;尝试npm安装报错 2.找到1.0.5之前的版本npm i jtopo2&#xff0c;安装成功后使用报错&#xff0c;应该是版本冲突了 1.本地引入&#xff0c; 点击官方源码下载&#xff0c;需要jtopo_npm文件 2.引入到本…

【wp】2023鹏城杯初赛 Web web1(反序列化漏洞)

考点&#xff1a; 常规的PHP反序列化漏洞双写绕过waf 签到题 源码&#xff1a; <?php show_source(__FILE__); error_reporting(0); class Hacker{private $exp;private $cmd;public function __toString(){call_user_func(system, "cat /flag");} }class A {p…

使用Renesas Flash Programmer(RFP)修改Option Byte及刷写程序

文章目录 前言配置Project修改OPBT程序刷写其他操作总结 前言 瑞萨RH850 P1H-C系列&#xff0c;在之前不知道OPBT对程序影响这么大&#xff0c;导致意外操作了其中的寄存器&#xff0c;板子锁死&#xff0c;不能再次刷写程序。本文记录下使用RFP工具刷写Option Byte需要注意的…

【C语法学习】10 - scanf()函数

文章目录 0 前言1 函数原型2 参数2.1 格式字符串2.1.1 转换说明 2.2 参数列表 3 返回值4 读取机制4.1 基本概念4.2 转换说明4.3 读取过程4.4 读取示例4.5 多参数 6 示例6.1 示例16.2 示例26.3 示例36.4 示例4 0 前言 scanf()函数虽然使用起来较为灵活&#xff0c;但是其读取机…

项目实战:分页功能实战

1、在index.html添加点击事件 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><link rel"stylesheet" href"style/index.css"><script src"scr…

VX-3R APRS发射试验

VX-3R本身是不带APRS功能的&#xff0c;不过可能通过外加TNC实现APRS功能。 有大佬已经用Arduino实现了相应的发射功能&#xff1a; https://github.com/handiko/Arduino-APRS 我要做的&#xff0c;就是简单修改一下代码&#xff0c;做一个转接板。 YEASU官方没有给出VX-3R的音…

基于单片机的智能饮水机系统

收藏和点赞&#xff0c;您的关注是我创作的动力 文章目录 概要 一、系统设计方案分析2.1 设计功能及性能分析2.2设计方案分析 二、系统的硬件设计3.1 系统设计框图系统软件设计4.1 总体介绍原理图 四、 结论 概要 现在很多学校以及家庭使用的饮水机的功能都是比较单一的&#…

【完美世界】石昊拒绝云曦相认,爱而不得,云曦悲伤无助

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料《完美世界云曦篇》最新一集&#xff0c;为了云曦&#xff0c;石昊不远十万里&#xff0c;亲自送她回家&#xff0c;这份感情之真挚&#xff0c;绝对毋庸置疑。然而&#xff0c;令人感到不解的是&…

读书笔记:《图解机械原理与构造》

通用零部件 轴&#xff1a;支撑回转零件 转轴&#xff1a;弯矩和转矩转动轴&#xff1a;转矩心轴&#xff1a;弯矩直轴曲轴软轴 轴承&#xff1a;支撑轴旋转 滑动轴承&#xff1a;承载高速旋转 径向滑动轴承&#xff1a;径向载荷推力滑动轴承&#xff1a;轴向载荷 滚动轴承&am…

故障诊断 | MATLAB实现GRNN广义回归神经网络故障诊断

故障诊断 | MATLAB实现GRNN广义回归神经网络故障诊断 目录 故障诊断 | MATLAB实现GRNN广义回归神经网络故障诊断故障诊断基本介绍模型描述预测过程程序设计参考资料故障诊断 基本介绍 MATLAB实现GRNN广义回归神经网络故障诊断 数据为多特征分类数据,输入12个特征,分3

modesim verilog仿真验证基本流程(新建工程方式)

文章目录 环境搭建一、在modelsim里创建一个新的工程二、新建verilog设计文件及仿真激励文件三、仿真结果本文演示如何使用modelsim新建工程进行功能仿真。 环境搭建 本文中采用的modelsim版本如下: modelsim altera 10.3d一、在modelsim里创建一个新的工程 打开modelsim软…

双链表详解(初始化、插入、删除、遍历)(数据结构与算法)

1. 单链表与双链表的区别 单链表&#xff08;Singly Linked List&#xff09;和双链表&#xff08;Doubly Linked List&#xff09;是两种常见的链表数据结构&#xff0c;它们在节点之间的连接方式上有所区别。 单链表&#xff1a; 单链表的每个节点包含两个部分&#xff1a;数…

利用大语言模型(LLM )提高工作效率

日常工作就是面向 google/ 百度编程&#xff0c;除了给变量命名是手动输入&#xff0c;大多时候就是通过搜索引擎拷贝别人的代码&#xff0c;或者找到旧项目一段代码拷贝过来使用。这无疑是开发人员的真实写照&#xff1b;然而&#xff0c;通过搜索引擎搜索答案&#xff0c;无疑…

【嵌入式开发工具】STM32+Keil实现软件工程搭建与开发调试

本篇文章介绍了使用Keil来对STM32F103C8芯片进行初始工程搭建&#xff0c;以及开发与工程调试的完整过程&#xff0c;帮助读者能够在实战中体会到Keil这个开发环境的使用方法&#xff0c;了解一个嵌入式工程从无到有的过程&#xff0c;并且具备快速搭建一个全新芯片对应最小软件…

CMU/MIT/清华/Umass提出生成式机器人智能体RoboGen

文章目录 导读1. Introduction2. 论文地址3. 项目主页4. 开源地址5. RoboGen Pipeline6. Experimental Results作者介绍Reference 导读 CMU/MIT/清华/Umass提出的全球首个生成式机器人智能体RoboGen&#xff0c;可以无限生成数据&#xff0c;让机器人7*24小时永不停歇地训练。…

CVE-2023-21839 weblogic rce漏洞复现

文章目录 一、漏洞影响版本二、漏洞验证三、启动反弹shell监听切换路径到jdk1.8 四、启动ldap服务五、使用CVE-2023-21839工具来进行攻击测试六、反弹shell 一、漏洞影响版本 CVE-2023-21839是Weblogic产品中的远程代码执行漏洞&#xff0c;由于Weblogic IIOP/T3协议存在缺陷&…