再读simpledb 之 元数据管理(2)

前节

2、(StatMgr)统计信息管理

统计信息管理器主要维护了一个表的三种统计信息:表文件包含Block的个数,包含记录的条数,以及每个字段非重复记录数。

image

图1 统计信息管理

注:

StatInfo中,每个字段的非重复记录数是一个经验估计值,大约是1 + (numRecs / 3)。

如前面所说,统计信息不是持久化到磁盘上,而是在系统启动之初,新建StatMgr对象的时候,自动计算得到。

private void refreshStatistics(Transaction tx)
{
    lock (threadLock)
    {
        tablestats = new Dictionary<string, StatInfo>();
        numcalls = 0;
        TableInfo tcatmd = tblMgr.getTableInfo("tblcat", tx);
        RecordFile tcatfile = new RecordFile(tcatmd, tx);
        while (tcatfile.next())
        {
            string tblname = tcatfile.getString("tblname");
            TableInfo md = tblMgr.getTableInfo(tblname, tx);
            StatInfo si = calcTableStats(md, tx);
            tablestats.Add(tblname, si);
        }
        tcatfile.close();
    }
}

在计算指定表的统计信息的时候,通过扫描记录文件,获取记录的相关参数,进而统计出整个表的统计信息:

a. numRecords通过遍历整个表中的记录,计数可得

b. numBlocks通过扫描最后一条记录的RID中的blockNumber得到。

:Record的存储

record类图见前节中的图1。

上面StatMgr中用到了RecordFile提供的方法,所以这里附上simpledb中表记录的存储细节。

前面描述了simpledb的存储实现,还记得,在buffer下面,有一个PageFormatter的接口,当初提到说,整个系统只有两个接口实现,其中有一个就是RecordPageFormatter。实际上说,数据库中的表,不是简单地按照顺序的形式写在文件中,而是有其自身的格式限制。表文件有着自己的存储格式,新插入记录按照指定的格式写入到表文件中

> RecordPageFormatter。

RecordPageFormatte有一个TableInfo对象,利用这个TableInfo对象包含的表中的字段信息,在表文件被写入真实数据之前,对其进行格式化。

public void format(Page page)
{
    int recsize = ti.recordLength() + Page.INT_SIZE;
    for (int pos = 0; pos + recsize <= Page.BLOCK_SIZE; pos += recsize)
    {
        page.setInt(pos, RecordPage.EMPTY);
        makeDefaultRecord(page, pos);
    }
}

代码比较简单,通过格式化的方式,可以看出,simpledb是以定长的形式保存记录的。

makeDefaultRecord方法根据TableInfo提供的schema信息,给字段设置默认值,并将默认值写入文件。

a. int默认值为0

b. 字符串默认值为""

这里要注意下的,是表记录的保存格式:

是否被使用(IS_USED) 表记录(record)

IS_USED初始为0,当新添加记录时,会将IS_USED置为1。

> RecordPage

联想前存储中提到的Page和Block的对应关系,RecordPage可以看做是一种特殊的Page,管理着对应Block中数据记录的放置和访问。略有不同的是,RecordPage不维护一个contents数组,因为RecordPage只是管理宏观放置和访问,处理的粒度是Record,具体的数据读写,交给Transaction对象来完成。

RecordPage中主要包含了5个对象:

blk,当前正在访问的磁盘文件块;

ti,当前表文件的字段信息;

tx,当前查询操作所在的事务;

slotsize,当前Block中,一条记录实际占用的长度;

currentslot,当前可访问的记录的指针。

主要从放置和访问(读写插删)两个角度来介绍下RecordPage的功能:

a. 记录的放置

我理解就是要插入的新纪录的定位问题:放在那里?

前面说过,被格式化过的表文件,就如下图所示(还是以studens表为例):

image

图2 格式化的空表文件

image

图3 插入过数据得到表文件

关键是一个searchFor(IS_USED)方法,给定参数IS_USED=0时,将currentSlot指针移向可用的位置。

另外moveToId(id),将currentSlot指针指向指定的位置。

b. 记录的访问

首先是定位:记录在整个表中的偏移+字段在记录中的偏移

然后用tx的对应方法读出数据

同样先是定位,然后用tx的方法在指定位置写入数据

首先定位,利用searchFor(0),移动currentSlot到合适的位置

标记已占用,IS_USED为1,然后交给后面的写方法,写入记录的内容

使用了标记删除法,不抹去数据,只是将IS_USED标记位设置为0,表示未使用即可。

> RecordFile

上面的RecordPage只是负责对一个磁盘数据块的内容的访问,RecordFile则是对整个表记录文件的遍历和访问。

首先说RecordFile和RecordPage的关系,RecordFile被访问时,不是一次读进来整个文件,而是分成若干个Block,一个BLock一个Block地读,这样在每次访问一个Block的时候,就需要一个RecordPage来访问Block。

一个RecordFile下面,只维护一个RecordPage对象,指向当前访问的Block。

另外还维护着一个currentBlocknum,作为一个指针,指向当前访问块的块号。

RecordFile中包含多个Block,因此,遍历时用到的next()方法:

首先是检查当前Block是否有next记录

否则检测是否有下一个可用的Block

以此类推,直到找到可用的记录,或者到达文件末尾退出。

public bool next()
{
    while (true)
    {
        if (rp.next())
            return true;
        if (atLastBlock())
            return false;
        moveTo(currentblknum + 1);
    }
}

其他的方法,都是对RecordPage方法的包装,考虑了块的移动造成的影响。

原文地址:https://www.cnblogs.com/YFYkuner/p/2689745.html