数据库系统实现技术实验报告文档格式.docx
《数据库系统实现技术实验报告文档格式.docx》由会员分享,可在线阅读,更多相关《数据库系统实现技术实验报告文档格式.docx(16页珍藏版)》请在冰豆网上搜索。
3.1.4NumFreeFrames()9
3.1.5SelectVictim()9
3.1.6Hash(intpage_id)9
3.1.7RemoveBCB(BCB*ptr,intpage_id)9
3.1.8RemoveLRUEle(intfrid)9
3.1.9SetDirty(intframe_id)9
3.1.10UnsetDirty(intframe_id)9
3.1.11WriteDirtys()9
3.1.12PrintFrame(intframe_id)9
3.2数据存储管理器9
3.2.1OpenFile(stringfilename)9
3.2.2CloseFile()9
3.2.3ReadPage(intpage_id)9
3.2.4WritePage(intframe_id,bFramefrm)10
3.2.5Seek(intoffset,intpos)10
3.2.6GetFile()10
3.2.7IncNumPages()10
3.2.8GetNumPages()10
3.2.9SetUse(intpage_id,intuse_bit)10
3.2.10GetUse(intpage_id)10
3.3LRU管理器10
3.3.1RemoveLRUEle(intfrmid);
10
3.3.2ResetFstNode(intframe_id);
3.3.3Modify(intframe_id);
4核心算法10
4.1替换页面选择伪代码SelectVictim()10
4.2LRU链表更新Modify(intframe_id)11
5测试结果12
6参考文献:
13
1
系统说明
1.1术语说明
1.1.1缓冲区
缓冲区是指系统的主存中的空间。
CPU只能够访问主存中的内容。
缓冲区包含一组帧片。
当一个页面被请求时,就会被装载到缓冲区中。
大部分的商业数据库管理系统设置帧片的大小与系统的页大小一致以确保不会产生额外的碎片。
本实验采用相同的策略。
缓冲区的大小默认的设置为1024.
缓冲区是由被称之为帧片的逻辑分区组成。
这些帧片被存储在全局的定义的结构当中,通常描述为如下形式:
#defineFRAMESIZE4096
structbFrame
{
Charfield[FRAMESIZE];
};
缓冲区将会存储一系列的存有或者装有页面的帧片。
其数组将如下图
#defineDEFBUFSIZE1024
bFramebuf[DEFBUFSIZE];
//或者用户通过参数自定义大小
当页面被请求的时候,这些被分配给缓冲区的空间将会被缓冲区管理器文件等访问。
1.1.2基于结构的目录文件
每一个文件都有一个包含文件中每一页面指针的基础页面。
每一个在基础页面当中的指针都代表一个页面。
数据页面仅仅包含数据而不存储指针。
因此基础页面(也即目录)在文件访问的时候必须被访问。
当记录被查询时候,能够很好地找寻文件是这种基于目录式的文件格式被采用的主要原因。
这种组织方式使得可以快速通过指针访问相应页面而不需要搜索一个长的页面列表。
1.2关键技术
1.2.1内存页面替换策略
采用LRU算法作为替换策略。
通过最近访问时间组织的缓冲区页面构成LRU队列,使用该种策略换出最近最少使用的页面。
总是在LRU的位置选出替换页面。
有一个常系数的时间复杂性是LRU算法的主要优点。
而且,LRU的性能因好的时间局部性而别被人称道。
比如:
当前被访问的页面很可能将不久得将来访问。
对于缓冲区的每一帧片,管理着块的缓冲区必须被保存。
每一个缓冲区控制块(BCB),包含page_id,frame_id,page_latch(页面锁),dirty_bit(脏数据位)。
page_ids作为将page_id作为哈希映射给BCB的关键码。
两个哈希表被需要:
一个是将page_ids映射到frame_id和BCB,一个是将frame_id映射为page_id.
存储帧的转换
采用静态的哈希方法。
在静态的哈希方法种种,桶的容量是固定的。
假如一个桶装满,一个溢出的链表将连接性的额外数据。
使用关键值,哈希函数将其映射为一个桶,同时也是访问当个桶的手段。
在程序运行期间,桶的容量不会发生改变。
静态的哈希方法将采用类似相面的函数
H(k)=(page_id)%buffer_size
每一个缓冲区控制块(BCB),page_id,frame_id,计数,dirty_bit(脏数据位)。
将page_id映射到BCB的哈希表如下BCBhTable[BufferSize].将frame_id映射为page_id的哈希表如下inthTable[BufferSize].
1.2.3文件存储格式
为保证可以快速通过指针访问相应页面而不需要搜索一个长的页面列表,每一个文件都有一个包含文件中每一页面指针的基础页面。
1.3系统需求
✓完成数据的读写更新;
✓能够展示实验结果;
✓实现LRU对缓冲区页面的控制;
✓具有一定的容错能力;
✓实验测试的时间;
✓读写命中率以及缺失率。
2系统设计
2.1Hash链表的设计
在实验当中,两处使用hash链表,其中一条链表将page_id映射为frame_id,在实验当中称为ptof;
另外一条将frame_id映射为page_id,在实验中成为ftop。
Figure1page_id映射frame_id图
Figure2frame_id映射page_id图
2.2系统性能
2.2.1系能测试方法
系统以跟踪驱动文件为主要手段,在该文件由Zipf分布器生成,其中包括500,000个页面,页面号0到49,999。
其形式如“x,###”,其中x是读写标志,0表示读操作,1表示写操作,###表示页面号。
另外假设开始时,内存为空,所有的文件(名为data.dbf)都已经通过目录文件的形式存在物理硬盘。
2.2.2性能指标
i)总I/O次数
ii)缓冲区的命中率
iii)驱动测试文件的运行时间
2.3系统总体架构
2.4系统模块
2.4.1数据存储管理器
classDSMgr{
public:
DSMgr();
intOpenFile(char*filename);
intCloseFile();
intAddPage(intpage_id);
bFrameReadPage(intpage_id);
intWritePage(intpage_id,bFramefrm);
intSeek(intoffset,intpos);
FILE*GetFile();
voidIncNumPages();
voidDecNumPages();
//?
?
intGetNumPages();
voidSetUse(intpage_id,intuse_bit);
intGetUse(intpage_id);
intGetRecOffset(intpage_id);
private:
FILE*currFile;
intnumPages;
intpages[MAXPAGES];
voidIsOk(char*filename,intrtn,intexprtn);
};
2.4.2缓冲区管理器
classBMgr
BMgr();
~BMgr();
//Interfacefunctions
intFixPage(intpage_id,intrwflag);
voidFixNewPage();
intUnfixPage(intpage_id);
intNumFreeFrames();
//InternalFunctions
intSelectVictim();
intHash(intpage_id);
voidRemoveBCB(BCB*ptr,intpage_id);
voidAddBCB(intframe_id,intrwflag);
voidRemoveLRUEle(intfrid);
voidSetDirty(intframe_id);
voidUnsetDirty(intframe_id);
voidWriteDirtys();
voidPrintFrame(intframe_id);
intGetHits();
intGetIO();
//voidChgPgHash(intpage_id,intframe_id,intoutpgid);
intPageIsUsed(intframe_id);
intChgPgHash(intpage_id,intframe_id);
//HashTable
intftop[DEFBUFSIZE];
//frame_idhash
BCB*ptof[DEFBUFSIZE];
//pagehash
LRUMgrlrumgr;
DSMgrdsmgr;
bFramefrms[DEFBUFSIZE];
intwtPgid;
//pagemaxindexwriteondisk
intwrtFlag;
//O
intrdFlag;
//I
inthits;
intsumIn;
intsumOut;
2.4.3LRU管理器
classLRUMgr
LRU*header;
LRU*tail;
intused;
LRUMgr(intlruNum);
~LRUMgr();
//intSelectVictim();
intRemoveLRUEle(intfrmid);
voidResetFstNode(intframe_id);
voidModify(intframe_id);
voidDebugShow();
3模块接口
3.1缓冲区管理器接口
3.1.1FixPage(intpage_id,intprot)
该函数返回值是frame_id,文件和存储管理器将通过在记录record_id中的page_id找到该页面。
当页面在缓冲区中,函数返回相应的frame_id。
否则调入页面,并且是需要的时候会替换页面。
3.1.2FixNewPage()
该函数原型返回page_id,frame_id。
当因为插入,索引分离,对象创建一个新页面被需要的时候就会调用这个函数,被返回的page_id主要用于被分配给记录record_id和元数据。
找到的新页面将会被文件存储管理器存储新的页面。
3.1.3UnfixPage(intpage_id)
该函数原型返回frame_id,这个函数有一次FixPage或者FixNewPage调用。
这个函数会减少帧片上的固定数量。
如果count减少到0,且页面的锁已被移除,帧片会被移除。
page_id被翻译成frame_id,并且可能没有锁以便能够在count为0时被选为一个替换页面。
3.1.4NumFreeFrames()
该函数原型在缓冲区中查询空闲和可用的帧片,这对于N-way排序的查询处理特别有效。
返回一个从0到BUFFERSIZE-1(1023)的整数。
3.1.5SelectVictim()
该函数挑选一个替换的帧片。
假如被选择的帧上面dirty_bit被设置那么就需要被写到硬盘。
3.1.6Hash(intpage_id)
该函数返回frame_id.
3.1.7RemoveBCB(BCB*ptr,intpage_id)
从一个缓冲区控制块数组中移除page_id的页面。
该函数仅仅在SelectVictim()需要替换页面时被调用。
3.1.8RemoveLRUEle(intfrid)
从列表中移除LRU元素。
3.1.9SetDirty(intframe_id)
函数原型设计帧片的脏数据位。
这个脏数据位表示是否有数据写到缓冲区。
当内容被更改,帧就会被写。
这个包含任何的目录页面和数据页面。
假如bit是1,哪儿表示有数据写入;
否则为0。
3.1.10UnsetDirty(intframe_id)
函数原型将相应的frame_id的脏数据置为0,调用这个函数的主要原因是SetDirty被调用时但是页面实际是临时的部分相关。
在这种情况下,这个页面实际上不需要被写。
3.1.11WriteDirtys()
函数在系统关闭时候调用。
主要目的是将buffer中的脏数据帧写到文件。
3.1.12PrintFrame(intframe_id)
打印frame_id里面的内容。
3.2数据存储管理器
3.2.1OpenFile(stringfilename)
函数在文件被打开或者读取的时候调用。
返回值为一个错误返回码。
3.2.2CloseFile()
函数在文件关闭时被调用。
返回值是错误码。
这个函数仅当数据库改变或者程序关闭时候调用。
3.2.3ReadPage(intpage_id)
函数会被缓冲区的FixPage函数调用。
返回的是读取的数据。
这个函数调用fseek()和fread()得到数据。
3.2.4WritePage(intframe_id,bFramefrm)
函数在任何时候从缓冲区中取出数据时调用。
返回被写入的数据字节数。
函数调用fseek()和fwrite()去存储数据。
3.2.5Seek(intoffset,intpos)
函数移动指针
3.2.6GetFile()
函数返回当前文件。
3.2.7IncNumPages()
函数增加页面的计数.
3.2.8GetNumPages()
函数返回文件的计数.
3.2.9SetUse(intpage_id,intuse_bit)
函数找到页数组中的设置位,这个数组跟中被使用的页面。
如果一个页面当中所有的记录都被删除,那么页面就没有实际利用价值并且能够被重新使用。
为了检查页面是否能够被使用,当所有的use_bits被置0。
fixNewPage首先会检查use_bits数组中是否0。
如果有一个0,则可以被重用;
否则重新使用。
3.2.10GetUse(intpage_id)
函数返回相应页面的use_bit位。
3.3LRU管理器
3.3.1RemoveLRUEle(intfrmid);
将帧标识为frmid的LRU单元移除
3.3.2ResetFstNode(intframe_id);
把LRU链表当中第一个元素的frame_id重置为传入的参数值;
将标识为frame_id的LRU单元移到链表首部
4核心算法
4.1替换页面选择伪代码SelectVictim()
当页面不再内存就需要调用替换页,需要根据LRU是否有空闲的单元两种情况进行具体设计。
尤其是当将其他页面换出内存的时候要根据其是否为脏而重写硬盘文件。
……
pLRU=lrumgr.tail;
//指向链表的最后一个元素
priorLRU=lrumgr.tail;
frame_id=pLRU->
frame_id;
//获取最后元素存放的帧ID;
…..
if(frame_id!
=-1){//链表中元素已经全部被用完
while(pLRU!
=lrumgr.header){//寻找未被锁定的帧缓存
flag=PageIsUsed(frame_id);
//判断该帧是否锁定
if(flag==1){
frame_id;
pLRU=pLRU->
prior;
//transfromtailtoheader
}
else{//找到可用的帧
pLRU=lrumgr.header;
//fakewhile
//break;
……
lrumgr.Modify(frame_id);
//将找到的元素放到链表头部,表示最近被访问
else{//链表当中还有元素未被分配
lrumgr.Modify(-1);
//将最后空闲的元素移动到链表首部
if(lrumgr.used<
DEFBUFSIZE){//从0到DEFBUFSIZE-1返回帧ID
lrumgr.used++;
return(lrumgr.used-1);
=-1){/调整被替换的元素BCB链表
pBCB=ptof[ftop[frame_id]%DEFBUFSIZE];
//找到被替换页面的链表的头指针
this->
RemoveBCB(pBCB,ftop[frame_id]);
//移除替换出去页面的元素
returnframe_id;
}
4.2LRU链表更新Modify(intframe_id)
在更新LRU链表过程当中,当要被移动的元素是首元素或者尾元素的时候,需要认真仔细的调整首部或尾部指针,否则会将链表元素丢失
LRU*tmp=this->
tail;
LRU*tmpFirst=NULL,*tmpLast=NULL;
while((tmp!
=NULL)&
&
(!
(tmp->
frame_id==frame_id))){//寻找要移动元素
tmp=tmp->
}……
if(tmp!
=NULL){//要移动的元素存在
tmpFirst=tmp->
tmpLast=tmp->
next;
if(tmpFirst){//被移动的不是首元素
header->
prior=tmp;
tmp->
next=this->
header;
prior=NULL;
//调整链表的头指针
header=tmp;
//
tmpFirst->
next=tmpLast;
if(tmpLast!
=NULL){//被移动的不是尾元素
tmpLast->
prior=tmpFirst;
//modifythenextLRUElement
分情况}
重置尾指针if(tmpLast==NULL){//frame_id==-1?
orlastnodereplaced
…….
tail=tmpFirst;
tail->
next=NULL;
5测试结果
测试环境:
linuxUbuntu10.04(LTS)
驱动测试文件的记录数目:
500000
总I/O次数502821
命中率33.9130%
运行时间16min51sec
Figure3实验测试结果
1DatabaseSystemImplementation(2th),H.Garcia-Molina,J.D.Ullman,J.Widom.机械工业出版社