MIT6.830-Lab1
类介绍
-
TupleDesc类
TupleDesc类用来存储表结构,使用静态内部类TDItem封装字段类型和字段名称。
-
Tuple类
Tuple类用来存储具体的数据行,使用Filed接口数组存放不同字段类型的数据,使用TupleDesc成员变量存放与该数据行关联的表结构信息,使用RecordId成员变量存放该数据行的行号和所处的数据页信息。
-
Catalog类
Catalog类用来存放当前数据库中的表信息,使用Map类型成员变量存放表编号和表的对象引用之间的映射、表编号和表的名称之间的映射、表编号和表的主键之间的映射。
-
BufferPool类
BufferPool类用来缓存最近使用的数据页,提升查询效率。默认情况下,数据页占4096字节,缓存最多50个数据页,使用Map类型成员变量存放PageID和Page之间的映射。
-
HeapFile类
HeapFile类实现了DbFile接口,用来从磁盘读取指定页号的数据页到内存中。使用File成员变量存储文件引用,TupleDesc成员变量存储表结构。 -
HeapPageId类
HeapPageId类实现类PageId接口,用来存放表编号和数据页号信息。 -
RecordId类
RecordId类存放数据行的编号,并使用PageId对象引用存放数据行所在的数据页信息。 -
HeapPage类
HeapPage类实现了Page接口,用来存储具体的数据页,使用HeapPageId成员变量存放该数据页的页号和关联的数据表编号,使用TupleDesc成员变量存放数据页所属的数据表的表结构,使用Tuple数组存放具体的数据行,使用字节数组存放数据页头部来表示Tuple是否有效,使用字节数组存放整个数据页的拷贝镜像。 -
SeqScan类
SeqScan类用来对数据表进行全表读取,类似select *
,并支持对表设置别名。
实验部分
实验1
此处只需注意已提供的Field
和Type
接口中的方法即可。值得注意的是重写类中hashCode()
方法一般是:
result = 1
for 对象字段 in 对象字段列表
result = 31*result + 对象字段.hashCode()
return result
实验2
此处最重要的是想清楚该以哪种数据结构存放数据库中的所有表信息,从预先定义的getPrimaryKey
、getDatabaseFile
、getTableName
三个方法可以看到,都是根据int
类型的tableId
取出表数据,所以想到使用Map
类型存放表ID和表主键、表对象、表名称三者的对应关系。
实验3
根据BufferPool.getPage()
方法传入的参数PageId pid
,将缓存同样设计为Map
映射。这里需要重点注意的是,当缓存中没有对应的数据页时,要从磁盘上读取该页并存入BufferPool
中(暂时不考虑缓存驱逐策略),具体实现如下:
Page page = Database.getCatalog().getDatabaseFile(pid.getTableId()).readPage(pid);
map.put(pid,page);
实验4
HeapPageId
和RecordId
实现简单,略过。HeapFile
由多个HeapPage
组成,HeapPage
中又包含了tuples
数组(存放表记录)和header
数组(存放表记录的有效性信息)。根据文档提示,完成getNumTuples()
和getHeaderSize()
方法:
private int getNumTuples() {
int tupleSize = td.getSize();
//向下取整,仅存放完整的tuple
return (int) Math.floor((BufferPool.getPageSize()*8.0) / (tupleSize * 8.0 + 1));
}
private int getHeaderSize() {
// 向上取整,可能会存储未使用的tuple状态信息
return (int) Math.ceil(getNumTuples()/8.0);
}
最重要的是isSlotUsed()
方法,它用来判断tuples[i]
是否有效:
public boolean isSlotUsed(int i) {
int nthHeaderByte = i/8; //应该存放在第几个字节
int nthBit = i%8; //应该存放在该字节的第几位
// lsb(least significant bits)是从每个字节的最右端开始计数
// 不能写成return (header[nthHeaderByte] & (1<<nthBit)) == 1;
// 因为 相与之后 可能是 000000010,和1的二进制 00000001不同
return (header[nthHeaderByte] & (1<<nthBit)) != 0;
}
实验5
HeapFile
的readPage()
方法要根据参数中的pid
中的页号,实现随机读取数据页,最重要的是计算数据页的开始位置,可用页号*页大小
取得。
public Page readPage(PageId pid) {
int tableId = pid.getTableId();
if(tableId != this.getId())
throw new RuntimeException("the PageId is not legal");
int pageNumber = pid.getPageNumber();
int maxPageNum = this.numPages();
if(pageNumber<0 || pageNumber>=maxPageNum)
throw new RuntimeException("the pageNum exceed the limit");
FileInputStream fis = null;
BufferedInputStream bis = null;
try {
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
long skipBytes = pageNumber==0?0:pageNumber*BufferPool.getPageSize();
bis.skip(skipBytes);//到达起始位置
byte[] bytes = new byte[BufferPool.getPageSize()];
bis.read(bytes);
bis.close();
HeapPage heapPage = new HeapPage((HeapPageId) pid, bytes);
return heapPage;
}catch (Exception e){
e.printStackTrace();
System.exit(0);
}
return null;
}
iterator
方法要返回DbFileIterator
对象以对HeapFile
文件做遍历,而且要求不能一次性读入所有的数据页以防内存溢出,这就需要使用页号进行逐页遍历:
public DbFileIterator iterator(TransactionId tid) {
return new DbFileIterator() {
private int pageNo = 0;
private Iterator<Tuple> it = null;
@Override
public void open() throws DbException, TransactionAbortedException {
pageNo = 0;
if(pageNo < numPages()){
HeapPageId heapPageId = new HeapPageId(getId(), pageNo);
HeapPage heapPage = (HeapPage)Database.getBufferPool().getPage(tid,heapPageId,Permissions.READ_ONLY);
it = heapPage.iterator();
} else {
it = null;
}
}
@Override
public boolean hasNext() throws DbException, TransactionAbortedException {
if(it == null)
return false;
else if(it.hasNext()){
return true;
}else{
while(++pageNo<numPages()){
HeapPageId heapPageId = new HeapPageId(getId(), pageNo);
HeapPage heapPage = (HeapPage)Database.getBufferPool().getPage(tid,heapPageId,Permissions.READ_ONLY);
it = heapPage.iterator();
if(it.hasNext())
return true;
}
return false;
}
}
@Override
public Tuple next() throws DbException, TransactionAbortedException, NoSuchElementException {
if(it == null)
throw new NoSuchElementException();
return it.next();
}
@Override
public void rewind() throws DbException, TransactionAbortedException {
open();
}
@Override
public void close() {
pageNo = -1;
it = null;
}
};
}
实验6
SeqScan
相当于对HeapFile.iterator()
方法的又一层封装,遍历表记录的调用路径为SeqScan->HeapFile->HeapPage
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步