MIT6.830-Lab1

SimpleDB 项目地址

类介绍

  • 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

此处只需注意已提供的FieldType接口中的方法即可。值得注意的是重写类中hashCode()方法一般是:

result = 1
for 对象字段 in 对象字段列表
	result = 31*result + 对象字段.hashCode()
return result

实验2

此处最重要的是想清楚该以哪种数据结构存放数据库中的所有表信息,从预先定义的getPrimaryKeygetDatabaseFilegetTableName三个方法可以看到,都是根据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

HeapPageIdRecordId实现简单,略过。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

HeapFilereadPage()方法要根据参数中的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

posted @   rockdow  阅读(16)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示