一、环境搭建

1.课程官网:6.830/6.814: Database Systems

2.Github地址:simple-db-hw-2021

3.安装配置ant

二、实验概览

SimpleDB consists of:

  • Classes that represent fields, tuples, and tuple schemas;(字段、元组(即记录)、模式)
  • Classes that apply predicates and conditions to tuples;(描述元组)
  • One or more access methods (e.g., heap files) that store relations on disk and provide a way to iterate through tuples
    of those relations;(访问元组)
  • A collection of operator classes (e.g., select, join, insert, delete, etc.) that process tuples;(CRUD)
  • A buffer pool that caches active tuples and pages in memory and handles concurrency control and transactions (neither
    of which you need to worry about for this lab); and,(实现buffer pool及并发控制、事务)
  • A catalog that stores information about available tables and their schemas.

整个实验一共有6个lab,通过每一个lab的代码去实现一个简单的数据库,主要有:数据库的组织架构(字段、元组、模式、buffer pool等)、sql boy最爱的CRUD的实现、查询优化、事务与并发控制、崩溃与故障恢复。刚做完第一个lab,记录一下方便后面复习。

lab1主要是实现了整个数据库的整体架构,做完会对数据库是怎样组织我们平时的表会有较多的认识,当然,lab1并没有像MySQL那样去实现以B+树形式的聚簇索引来存储数据,主要还是按顺序存储,数据结构非常的简单。

lab1涉及的主要有这几个部分:Tuple(元组)、TupleDesc(table的metadata)、catalog(该数据库并没有过度区分catalog和schema,可以看成是一个schema)、BufferPool(缓冲池)、HeapPage(数据页)、HeapFile(disk上的文件)、SeqScan(全表顺序扫描),下面是各个部分的介绍与联系:

1.Tuple:元组,数据库上把一个有n列的table称作n元组,一个Tuple有多个字段。通俗来讲,一条记录就是一个元组,在该实验中体现为一个Tuple类的实例。一个Tuple由以下部分组成:a.TupleDesc:该元组的描述信息;b.fields:该记录各个字段的类型与值; c.RecordId:该记录在磁盘的位置。

2.TupleDesc:TupleDesc用来表示一个元组的描述信息,更准确来说应该是一个表的描述信息;

3.Catalog:Catalog是仅次于DataBase的抽象概念,一个DataBase可以有多个Catalog,一个Catalog有多个Schema,一个Schema有多张table,不过该数据库没有太过区分这三个概念,而在MySQL中也是用Schema来表示整个数据库包含的多张table。该lab中实现了一个Table类,并在Catalog类中使用一个HashMap来存放table id与table的映射关系。

4.BufferPool:BufferPool的基本单位是Page,每次从磁盘中(这里表现为DbFile)读取数据页到BufferPool,在数据库上的crud操作都是在Buffer Pool的Page中进行的(所以有脏页、故障恢复等)。该数据库的BufferPool默认是缓冲50个Page,每个Page的默认大小是4096bytes即4kb。lab1主要是实现getPage方法,从BufferPool中获取Page,如果获取不到,就在磁盘中获取,并保存到BufferPool中。当然,当BufferPool满了之后,需要有淘汰策略,后续的lab会实现,应该是使用LRU算法来做的。

5.HeapPage与HeapFile:HeapPage是Page接口的实现,是以顺序逻辑组织数据的一张数据页(后续会用b+树实现来替代),crud操作都是在Page上进行的。HeapPage既是BuffeerPool的基本单位也是HeapFile的基本单位。HeapFile是DbFile接口的实现,与磁盘中的文件交互,该数据中一张表的所有数据就是存到DbFile的File属性中,即一个磁盘的文件就是一张表的所有数据。

6.SeqScan:全表顺序扫描的实现,相当于select * from my_table.

这些组成部分的关系如下图所示:

除了上述信息,该实验还提供了一个DataBase类,可以理解成一个数据库的全局信息,可以说是一个数据库的入口,主要由以下三部分组成:

catalog:存放实际的表格,而每张表格的描述信息应该放在了TupleDesc中;

BufferPool:实际进行crud操作的地方,buffer pool以page为单位从磁盘中读入。

LogFile:日志文件。

三、实验过程

Exercise1

Tuples in SimpleDB are quite basic. They consist of a collection of Field objects, one per field in the Tuple. Field is an interface that different data types (e.g., integer, string) implement. Tuple objects are created by the underlying access methods (e.g., heap files, or B-trees), as described in the next section. Tuples also have a type (or schema), called a tuple descriptor, represented by a TupleDesc object. This object consists of a collection of Type objects, one per field in the tuple, each of which describes the type of the corresponding field.

exercise1主要是实现Tuple与TupleDesc这两个类的构造器和主要方法,较为简单。

Tuple:包含td,rid,fields三个属性,一条记录最基本的信息,其它的就是一些构造器,getter方法和迭代器等,看着注释实现就可以了。

其中Field是字段的实现,点进去看可以看到主要是Type来存储字段类型

而Type是类型的枚举,该数据库支持int类型和String类型,类型信息描述都在里面,后面也可在Type中扩展数据类型:

根据字段的类型,parse方法可以返回具体的Field实现类,即带有类型的Field,里面用value来存储字段值:

TupleDesc:TupleDesc主要是需要实现一个TDItem内部类来表示一个字段的描述信息,包括字段类型,字段名;TupleDesc中用一个TDItem数组来表示多个字段的描述信息,有了这个,后面做起来就简单很多了。

Exercise2

The catalog (class Catalog in SimpleDB) consists of a list of the tables and schemas of the tables that are currently
in the database. You will need to support the ability to add a new table, as well as getting information about a
particular table. Associated with each table is a TupleDesc object that allows operators to determine the types and
number of fields in a table.

The global catalog is a single instance of Catalog that is allocated for the entire SimpleDB process. The global
catalog can be retrieved via the method Database.getCatalog(), and the same goes for the global buffer pool (
using Database.getBufferPool()).

exercise2主要是实现Catalog.java这个类,这里的关键是在Catalog里实现一个Table类,来存放一张表格的信息;然后一个CataLog有多张表格,在Catalog里我们可以用map来存储tableid与table的映射关系;为了方便操作,我还多创建了一个存放table name与 table id映射关系的map:

Exercise3

The buffer pool (class BufferPool in SimpleDB) 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. In later labs, you will implement an eviction policy(淘汰策略). For this lab, you only need to implement the constructor and the BufferPool.getPage() method used by the SeqScan operator. The BufferPool should store up to numPages pages. For this lab, if more than numPages requests are made for different pages, then instead of implementing an eviction policy, you may throw a DbException. In future labs you will be required to implement an eviction policy.

lab1无需实现淘汰策略,主要是实现getPage方法。根据PageId来查找BufferPool中有无该数据页,如果有直接返回,如果没有就从磁盘中获取,存入BufferPool中。其中BufferPool的容器是用map来存Page的,以pid的hashcode与Page建立映射关系。

Exercise4

A HeapFile object is arranged into a set of pages, each of which consists of a fixed number of bytes for storing
tuples, (defined by the constant BufferPool.DEFAULT_PAGE_SIZE), including a header. In SimpleDB, there is
one HeapFile object for each table in the database(一个HeapFile对应一张表). Each page in a HeapFile is arranged as a set of slots, each of
which can hold one tuple (tuples for a given table in SimpleDB are all of the same size). In addition to these slots,
each page has a header that consists of a bitmap with one bit per tuple slot. If the bit corresponding to a particular
tuple is 1, it indicates that the tuple is valid; if it is 0, the tuple is invalid (e.g., has been deleted or was never
initialized.) Pages of HeapFile objects are of type HeapPage which implements the Page interface. Pages are
stored in the buffer pool but are read and written by the HeapFile class.

exercise4主要是实现以下三个类:

  • src/java/simpledb/storage/HeapPageId.java
  • src/java/simpledb/storage/RecordId.java
  • src/java/simpledb/storage/HeapPage.java

关键是HeapPage的实现,HeapPage中的核心是数据是怎样HeapPage中组织的,这里主要是由header和tuples两个部分组成。其中,一条tuple在HeapPage中表示为一个slot,而header以bitmap的形式来表示第i个slot是否被占用,最低位表示第一个slot是否被占用。由于header是一个byte类型数据,所以最后一个字节并不一定都表示一个slot,也可能无意义:

除了基本的属性,关键的方法是计算一个Page能存多少条tuple和header占多少字节,讲义中给出了计算公式:

_tuples per page_ = floor((_page size_ * 8) / (_tuple size_ * 8 + 1))

headerBytes = ceiling(tupsPerPage/8)

代码实现如下:

有了上面这两个方法,就方便对属性进行初始化。当对数据页进行操作时,需要判断某个slot是否被占用,所有需要实现以下方法:

最后就是实现返回一个有效记录的iterator的方法:

Exercise5

exercise5主要是实现heapfile,HeapFile是DbFile的实现,是对磁盘文件操作的入口:

HeapFile要求我们实现一个readPage方法,根据PageId去从磁盘中读取数据。这里主要的难点是计算偏移量然后读取数据,这里需要先计算出offset,然后用seek移动指针,然后再开始读取,具体代码实现如下:

除了实现上述功能,HeapPage还要求我们实现一个迭代器用于遍历所有的tuple,注意这里的实现是先从BufferPool中读取数据,读取不到再从磁盘中读,直接调用readPage会出现OOM。实现如下:

    /*** HeapFile迭代器,用于遍历HeapFile的所有tuple;* 需要使用上BufferPool.getPage(),注意一次不能读出HeapFile的所有tuples,不然会出现OOM*/private static class HeapFileIterator implements DbFileIterator {private final TransactionId tid;private final HeapFile file;private Iterator<Tuple> it;private int pageNo;public HeapFileIterator(TransactionId tid, HeapFile file) {this.tid = tid;this.file = file;}@Overridepublic void open() throws DbException, TransactionAbortedException {pageNo = 0;it = getTupleIterator(pageNo);}/*** 根据pageNo从buffer pool 或者磁盘读出HeapPage并返回其tuple的迭代器* @param pageNo* @return* @throws TransactionAbortedException* @throws DbException*/private Iterator<Tuple> getTupleIterator(int pageNo) throws TransactionAbortedException, DbException{if(pageNo >= 0 && pageNo < file.numPages()) {HeapPageId pid = new HeapPageId(file.getId(), pageNo);HeapPage page = (HeapPage) Database.getBufferPool().getPage(tid, pid, Permissions.READ_ONLY);if(page == null) throw new DbException("get iterator fail! pageNo #" + pageNo + "# is invalid!");return page.iterator();}throw new DbException("get iterator fail!!! pageNo #" + pageNo + "# is invalid!");}@Overridepublic boolean hasNext() throws DbException, TransactionAbortedException {//需要先判断文件有没有打开if(it == null) return false;if(pageNo >= file.numPages()) return false;if(!it.hasNext() && pageNo == file.numPages() - 1) return false;return true;}@Overridepublic Tuple next() throws DbException, TransactionAbortedException, NoSuchElementException {if(it == null) throw new NoSuchElementException("file not open!");if(!it.hasNext()) {if(pageNo < file.numPages() - 1) {pageNo ++;it = getTupleIterator(pageNo);return it.next();}else {return null;}}return it.next();}@Overridepublic void rewind() throws DbException, TransactionAbortedException {close();open();}@Overridepublic void close() {it = null;}}

Exercise6

exercise6注意是做一个全表扫描,主要验证上一个exercise实现的迭代器的正确性,实现SeqScan即可,其中SeqScan给扫描的表支持别名,然后getTupleDesc需要去修改每个字段的名字,加上别名如a.table。实现如下:

四、踩坑记录

1.exercise1中TupleDesc计算所有字段占用的字节数,当时不太理解方法的描述,因为测试用例使用int类型,我简简单单用4*numTuples就过了,后面的erercise的测试用例过不了,仔细结合Type再修改了一下:

2.exercise4中HeapPage获取所有有效字段的迭代器时,没使用bitmap来判断slot是否被占用,导致了测试时空指针。正确的做法是逐个判断slot是否被占用,slot被占用才将tuple加入list中,最后返回list的iterator:

3.exercise6时有个test一直过不了,爆的是数组越界:

一开始以为是迭代器写的有问题,debug无果,后面打日志才发现是前面计算header占用字节数出现的低级错误,337/8=42导致分配的是42bytes的数组,正确的应该用337/8.0再取ceil结果是43:

五、总结

6.830这个实验是在6.824的交流群知道的,当时知道是用Java写的就打算有空来写写,感谢群佬们。做完lab1对数据库数据的组织形式有一定的了解,实验给的讲义也很棒,实验代码的注释也很详细,测试用例也能检测出一些细节上的错误,是个学习数据库知识不错的实验。实验总共花了两个下午一个晚上一个早上来写代码,2个多小时写总结。写完总结这就去做实验2~

实验时间:10.01-10.03

报告撰写时间:10.03 14点到16点

MIT6.830 lab1 SimpleDb 实验报告相关推荐

  1. MIT6.830 lab4 SimpleDB Transactions 实验报告

    一.实验预览 lab4要做的是让SimpleDB支持事务,所以实验前需要对事务的基本概念有了解,并知道ACID的特点.lab4是基于严格两阶段封锁协议去实现原子性和隔离性的,所以开始前也需要了解两阶段 ...

  2. MIT6.830 lab2 SimpleDB Operators 实验报告

    一.实验概览 以下是资料对本实验的介绍 Implement the operators Filter and Join and verify that their corresponding test ...

  3. MIT6.830 lab6 Rollback and Recovery 实验报告

    一.概览 1.steal/no-force策略 lab6要实现的是simpledb的日志系统,以支持回滚和崩溃恢复:在lab4事务中,我们并没有考虑事务执行过程中,如果机器故障或者停电了数据丢失的问题 ...

  4. MIT6.830 lab5 B+ Tree Index 实验报告

    一.实验概览 lab5主要是实现B+树索引,主要有查询.插入.删除等功能,查询主要根据B+树的特性去递归查找即可,插入要考虑节点的分裂(节点tuples满的时候),删除要考虑节点内元素的重新分配(当一 ...

  5. 【软件构造】LAB1实验报告

    2022年春季学期 计算学部<软件构造>课程 Lab 1实验报告 姓名 李** 学号 班号 电子邮件 手机号码 目录 1 实验目标概述... 1 2 实验环境配置... 1 3 实验过程. ...

  6. 操作系统ucore lab1实验报告

    操作系统lab1实验报告 [练习1] 理解通过 make 生成执行文件的过程.(要求在报告中写出对下述问题的回答) 在此练习中,大家需要通过阅读代码来了解: 1. 操作系统镜像文件 ucore.img ...

  7. 《ucore lab1 练习5》实验报告

    [练习5]实现函数调用堆栈跟踪函数 我们需要在lab1中完成kdebug.c中函数print_stackframe的实现,可以通过函数print_stackframe来跟踪函数调用堆栈中记录的返回地址 ...

  8. 《ucore lab1 exercise5》实验报告

    资源 ucore在线实验指导书 我的ucore实验代码 题目:实现函数调用堆栈跟踪函数 我们需要在lab1中完成kdebug.c中函数print_stackframe的实现,可以通过函数print_s ...

  9. 哈工大软件构造Lab1实验报告

    2020年春季学期 计算机学院<软件构造>课程 Lab 1实验报告 目录 1 实验目标概述... 1 2 实验环境配置... 1 3 实验过程... 1 3.1 Magic Squares ...

最新文章

  1. springboot 整合retry(重试机制)
  2. 重温强化学习之基于模型方法:动态规划
  3. csv转json文件
  4. vba与python相比2019_重大改变!Python 或将取代 VBA 成为 Excel 官方脚本语言
  5. 内核并发控制---RCU (来自网易)
  6. 深入学习NSUserDefaults/standardUserDefaults
  7. nginx源码分析——filter模块
  8. 免费的分区软件MiniTool Partition Wizard Free
  9. php简历怎么写比较出彩,如何让简历出彩 简历吸引人有几个方面
  10. python按什么键停止运行_python如何停止运行
  11. SMAA算法详解 - SMAANeighborhoodBlendingPS
  12. CCD相机模数转换芯片时序简介
  13. 400亿票房跟股市有什么关系
  14. 最适合freshman的Java习题集(一)
  15. Pytorch训练速度更快的十七种方法
  16. CCIR601与 CCIR656介绍
  17. 2015十大最具影响力的推广—兄弟连IT教育
  18. Y450 摄像头驱动问题的解决方法“0X00405202指令,或vmstillmnt”
  19. 超宽带(UWB)学习笔记——基于相位的测距方法
  20. python猴子偷桃 #题目:猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第

热门文章

  1. cdn.cache.php,CDN缓存不命中诊断 - 在线工具
  2. windows10杀死本地进程
  3. vi/Vim的快速使用
  4. orange实现逻辑回归_逻辑回归模型
  5. python读取csv第一列_如何使用标头完整的python导入csv文件,其中第一列为非数字...
  6. 液晶屏和计算机组成,液晶显示屏结构是什么 液晶显示屏结构介绍【图文】
  7. python搜索关键词自动提交_python+selenium实现自动化百度搜索关键词
  8. 后序遍历的非递归算法python_Python非递归实现二叉树的后续遍历
  9. geotools绘制椭圆API
  10. HTTP请求中,几种常见的Content-Type类型