这一块是在助教给的参考代码的基础上进行了一些小范围的修改。
感觉最麻烦的是一个是文件的管理。一开始认定一定要(读一行表示一块),发现这样的话要循环调用getline这个函数,感觉并没有达到好的一个效率。后来查找了蛮多资料然后才熟悉了具体的存储方法……因为写的文件会用txt打开,无法显示的内容都会以为丢失了。
这一块我和写record的同学协商之后打算用强制类型转换的方法来读写,就是,比如int和float都是4个字节,就用四个字节的char来表示,相当于256进制。然后在后期拼合代码的时候……发现如果用这种方法来记录的话,字符串什么的类型都没法用了因为,默认0是字符串的结尾。所以后来扩充了那个类型,为了代码的容易理解最后是,用5个字节来表示int(所以int没法完全表达,比如负数和五位以上的正数)和8个字节表示float(128进制,这样可以避开之前让我们困扰的0的问题而且让大部分落在’可见’也就是能显示的区域)。
有些问题还是集中在个人的水平上,比如,因为内存分配什么的出过很多问题……
还是需要协调……比如说,我的buffer这一块需要建立的文件节点的链表,在队友写的时候是有一个头节点,而我是直接用第一个文件的节点,这样的话在释放的时候可能会有未考虑到的部分。(所以总觉得该用C++啊摔)。而且好像我写的两块独立测试都比较麻烦……而且测试看不出来……
一、BufferManager负责缓冲区的管理,主要功能有:
1. 根据需要,读取指定的数据到系统缓冲区或将缓冲区中的数据写出到文件;
2. 实现缓冲区的替换算法,当缓冲区满时选择合适的页进行替换;
3. 记录缓冲区中各页的状态,如是否被修改过等;
4. 提供缓冲区页的pin功能,及锁定缓冲区的页,不允许替换出去。
为提高磁盘I/O操作的效率,缓冲区与文件系统交互的单位是块,块的大小应为文件系统与磁盘交互单位的整数倍,一般可定为4KB或8KB。
二、模块总体设计思路
记录管理模块(RecordManager)和索引管理模块(IndexManager)向缓冲区管理申请所要的数据,缓冲区管理器首先在缓冲区中查看数据是否存在,若存在,直接返回,否则,从磁盘中将数据读入缓冲区,然后返回。
最近最少使用(LRU)算法:每次访问一个块的时候,如果是重新读取的话把该块的iTime置为1,如果是已经有的话把它的使用次数加一。采用这种方式记录主要是因为对于Index部分,比如根节点的使用率一直保持非常高。
换出脏块时需要写出块。
三、具体实现
1、宏
#defineBLOCK_LEN 4096 // the size of one block
#defineMAX_BLOCK 40 // the max number of the blocks
2、结构体
1)、文件头结构体(structfileInfo) :
包含文件的基本信息:类型(数据文件或索引文件)、文件名、记录数、空块号、记录长度、指向下一个文件的指针、文件所指向的第一个块。
structfileInfo {
int type; //0-> data file, 1 -> index file
CString fileName; // the name of the file
int recordAmount; // the number of record in the file
int freeNum; // the free block number which could be used for thefile
int recordLength; // the length of the record in the file
fileInfo *next; // the pointer points to the next file
blockInfo *firstBlock; // point to the first block within the file
};
2)、块信息结构体(struct blockInfo):
包含块的基本信息:块号、脏位、指向下一个块的指针、块中的字符数、存放信息的字符型数组、年龄(用于LRU算法)、锁。
struct blockInfo {
intblockNum; // the block number of theblock,
// which indicate itwhen it be newed
booldirtyBit; // 0 -> flase, 1 -> indicate dirty, write back
blockInfo*next; //the pointer point to next block
fileInfo*file; // the pointer point to the file, which the block belongs to
intcharNum; // the number of chars in theblock
char*cBlock; // the array space for storingthe records in the block in buffer
intiTime; // it indicate the ageof the block in use
intlock; // prevent the blockfrom replacing
};
3)、主要函数及其功能描述如下:
1. fileInfo *findFileInfo(CStringDB_Name)
返回该数据库对应的头节点。
2.fileInfo* get_file_info(CStringDB_Name,CString fileName, int m_fileType)
在内存中查找该文件,返回该文件头。
3. blockInfo*get_block_inbuffer(const fileInfo *file_temp, const int blockNum)
在该文件节点下所有的块节点中查找特定编号的块,没有返回NULL.
4. blockInfo*remove_block_inbuffer(blockInfo *m_blockInfo)
将块从文件节点下移除。
5. blockInfo *findBlock(CStringDB_Name)
函数实现获取用于替换的内存块。
首先遍历文件节点,判断每个文件节点下是否有dullNode,有的话返回。
其次如果块数未达到MAX_BLOCK,申请内存。
如没有可用块,遍历所有的块节点使用MRU算法找到没有使用的时间最长的块(itime最大),从文件节点下移去,itime置为1;并将该块返回。
6. void replace(fileInfo*m_fileInfo, blockInfo *m_blockInfo)
把block节点连到file节点下
7. blockInfo *get_file_block(CStringDB_Name, CString Table_Name, const int fileType, const int blockNum)
根据文件名,文件类型查找该文件
根据文件的块号,从内存中查询该块;
如该块已经在内存,并且不为空节点,判断是否加锁,未加锁则返回该块的指针,同时调用次数++;
如该块已经在内存,但是为空节点,调用readBlock读取内容,返回该块的指针,调用次数置为1;
如果该快没有存在内存,同样调用readBlock读取所需内容,返回。
8. blockInfo* readBlock(fileInfo*file_temp,CString DB_Name, const intm_blockNum, blockInfo *block_temp)
如果传入参数中块为NULL,则调用findBlock来申请一个可用的块。
申请空间,读取文件。
9. void writeBlock(CString DB_Name,blockInfo * block)
把block指针所指向的块的内容写回磁盘。
10. void closeDatabase(CString DB_Name,bool m_flag)
先调用closeFile释放块节点。
再逐个释放文件节点。
11. void closeFile(CStringDB_Name,struct fileInfo* file_temp)
释放该文件节点下所有的块节点
4)主要操作实现过程
关闭数据库:
调用closeDatabase。函数内部对于每个文件节点,调用closeFile逐个关闭block,判断如果是脏块,调用writeBlock写回文件。再通过一次循环释放掉file节点。
获得某个数据块:
调用get_file_block,先找对应文件get_file_info,再调用get_block_inbuffer找文件下的块。如果不为空块直接返回,如果这个块是空块调用readBlock,找不到也调用readBlock,这个函数会申请缓存,如果申请不到缓存的话,调用findBlock去找一块空的内存。
findBlock的工作机制是先遍历所有的文件,找有没有freeNum的,需要 get_block_inbuffer再判断是不是到了申请的上限,再考虑释放时间最久的块。这里需要用到removeBlockinBuffer这个函数。
初始化:
BlockInfo数据初始化在read里,读取的时候进行初始化。
由其他模块直接作用维持的结构:
当需要其他的模块对该页加锁时,get_file_block将输出错误并且返回NULL指针。
4、内存中维护的结构
1)、单个数据库结构
fileInfo代表一个数据库下所有文件名。在命令use database的时候由Catalog Manager建立。
每个文件记录自己的freeNum对应第几个文件的数据是空的(基本上由数据删除产生),多个freeBlock,每个freeBlock里value指向下一个空的文件。
四、代码参考
////////////////////////////////////////////////////////////
//------------------------------------------------------///
// Module: Index_Manager.cpp
// Produced by: Birdy & C
// Description: buffer
// date:2017/5/22-2017/6/16
//------------------------------------------------------///
///////////////////////////////////////////////////////////
#define MAX_BLOCK 500
//#include"MiniSQL.h"
#include"API_Module.h"
#include"Buffer_Manager.h"
#include"Catalog_Manager.h"
#include"Record_Manager.h"
#include"Index_Manager.h"
#include"Interpreter.h"
using namespace std;
//fileInfo *ptr_to_files;
//返回一个数据库的起始file的地址
fileInfo *findFileInfo(CString DB_Name)
{
if (head_of_files == NULL)
return NULL;
return head_of_files->next;
}
//返回fileInfo
fileInfo *get_file_info(const CString DB_Name, const CString fileName, const int m_fileType)
{
fileInfo* file_temp = findFileInfo(DB_Name);
while (NULL != file_temp) //遍历所有的file节点
{
if (file_temp->fileName == fileName && file_temp->type == m_fileType)
{
return file_temp;
}
file_temp = file_temp->next;
}
//没有找到这个文件
cout << "buffer::get_file_info::can't find the file" << endl;
return NULL;
}
//在内存中查找这个数据块
blockInfo *get_block_inbuffer(const fileInfo *file_temp, const int blockNum)
{
if (NULL == file_temp)
return NULL;
blockInfo *block_temp = file_temp->firstBlock;
//遍历该file节点下所有block
while (NULL != block_temp)
{
if (block_temp->blockNum == blockNum)
{
return block_temp;
}
block_temp = block_temp->next;
}
return NULL;
}
//将块从文件节点下移除
blockInfo *remove_block_inbuffer(blockInfo *m_blockInfo)
{
fileInfo *file_temp = m_blockInfo->fileName;
int blockNum = m_blockInfo->blockNum;
blockInfo *block_last = NULL;
blockInfo *block_temp = file_temp->firstBlock;
while (NULL != block_temp)
{
if (block_temp == m_blockInfo)
{
//find the node wanted
break;
}
block_last = block_temp;
block_temp = block_temp->next;
}
if (NULL != block_temp)
{
if (NULL == block_last)
{
file_temp->firstBlock = block_temp->next;//头节点
}
else
{
block_last->next = block_temp->next;//中间节点&叶节点
}
//返回前一个节点
return block_last;
}
//没找着
return NULL;
}
//返回可用的块
blockInfo *findBlock(CString DB_Name)
{
static int i = 0;//记录现有block数字
//搜索空块
fileInfo *ptr = findFileInfo(DB_Name);
blockInfo *newblock;
while (NULL != ptr)
{
int freeBlock = ptr->freeNum;
if (freeBlock > 0)
{
blockInfo *block_temp = get_block_inbuffer(ptr, freeBlock);
if (NULL != block_temp)
{
blockInfo *block_last = remove_block_inbuffer(block_temp);//从链表中移除
//更新freeNum
CString freeNum_rec(block_temp->cBlock);
ptr->freeNum = _ttoi(freeNum_rec.Left(3));
return block_temp;
}
//file记录了freeNUM 但是找不到这个block
cout << "WANING buffer::findBlock:the freeNum in record may be wrong" << endl;
}
ptr = ptr->next;
}
if (i < MAX_BLOCK)
{
//申请内存
newblock = new blockInfo;
i++;
if (NULL != newblock)
{
newblock->cBlock = new char[BLOCK_SIZE];
if (NULL != newblock->cBlock) return newblock;
}
}
//释放内存
//哇这里写的MRU
int time = -1;
blockInfo *temp = NULL;
ptr = findFileInfo(DB_Name);
while (NULL != ptr)
{
blockInfo *block_ptr = ptr->firstBlock;
while (NULL != block_ptr)
{
if (block_ptr->iTime > time)
{
time = block_ptr->iTime;
temp = block_ptr;
}
block_ptr = block_ptr->next;
}
ptr = ptr->next;
}
temp->iTime = 1;
remove_block_inbuffer(temp);
return temp;
}
//把block连到file下
void replace(fileInfo *m_fileInfo, blockInfo *m_blockInfo)
{
//加在最前面
m_blockInfo->fileName = m_fileInfo;
m_blockInfo->next = m_fileInfo->firstBlock;
m_fileInfo->firstBlock = m_blockInfo;
}
//返回数据
blockInfo *get_file_block(CString DB_Name, CString Table_Name, const int fileType, const int blockNum)
{
blockInfo *block_temp;
fileInfo *file_temp = get_file_info(DB_Name, Table_Name, fileType);
if (NULL != (block_temp = get_block_inbuffer(file_temp, fileType)))
{
if (1 == block_temp->lock)//判断这个块有没有被锁住
cout << "buffer::the block has been locked" << endl;
return block_temp;
}
if (NULL == file_temp)//找不到这个文件
{
cout << "buffer::get_file_block::wrong information" << endl;
return NULL;
}
block_temp = file_temp->firstBlock;
while (NULL != block_temp)
{
if (block_temp->blockNum == blockNum)
{
if (0 != block_temp->charNum) //如果不是空块
{
block_temp->iTime++; //调用次数++
return block_temp;
}
else
{
block_temp = readBlock(file_temp, DB_Name, blockNum, block_temp);
block_temp->iTime = 1; //调用次数初始化
return block_temp;
}
}
block_temp = block_temp->next;
}
cout << "buffer::get_file_block::can't find this block >>> begin read" << endl;
block_temp = readBlock(file_temp, DB_Name, blockNum, NULL);
replace(file_temp, block_temp); //将该block增加到头节点下
block_temp->iTime = 1; //调用次数初始化
return block_temp;
}
//已经判断内存中不存在这一个块、 读取数据
blockInfo* readBlock(fileInfo *file_temp, CString DB_Name, const int m_blockNum, blockInfo *block_temp)
{
//如果没有提供块,则需要自己找一个块
if (block_temp == NULL)
{
block_temp = findBlock(DB_Name);
block_temp->blockNum = m_blockNum;
block_temp->dirtyBit = 0;
}
//读取文件
CString fpath;
if (0 == file_temp->type)
{
fpath = "..\\" + DB_Name + "\\table\\" + file_temp->fileName + ".txt";
}
else
{
fpath = "..\\" + DB_Name + "\\index\\" + file_temp->fileName + ".txt";
}
fstream file(fpath, ios::in | ios::out);
if (!file.is_open()) //检查打开是否成功
{
cout << "buffer::readBlock::can't find the file" + fpath << endl;
return NULL;
}
int offset = (m_blockNum - 1) * BLOCK_SIZE; //计算偏移量
file.seekp(offset, ios::beg); //从起始位置开始
block_temp->cBlock = new char[BLOCK_SIZE]; //申请内存
file.read(block_temp->cBlock, BLOCK_SIZE); //读取文件
file.close();
return block_temp;
}
//把block指针所指向的块的内容写回磁盘。
void writeBlock(CString DB_Name, blockInfo * block)
{
if (!block->dirtyBit) return;
fileInfo *file_temp = block->fileName;
CString m_fileName = block->fileName->fileName;
int m_blockNum = block->blockNum;
CString fpath;
if (0 == file_temp->type)
{
fpath = "..\\" + DB_Name + "\\table\\" + m_fileName + ".txt";
}
else
{
fpath = "..\\" + DB_Name + "\\index\\" + m_fileName + ".txt";
}
fstream file(fpath, ios::in | ios::out);
if (!file.is_open()) //检查打开是否成功
{
cout << "buffer::writeBlock::can't find the file" + fpath << endl;
return;
}
int offset = (m_blockNum - 1) * BLOCK_SIZE;
file.seekp(offset, ios::beg);
CString temp = block->cBlock;
file.write(block->cBlock, temp.GetLength());
file.close();
}
//调用closeFile(DB_Name,filename,fileType,m_flag),逐个关闭文件。
void closeDatabase(CString DB_Name, bool m_flag)
{
//release Block_Info
fileInfo *ptr = findFileInfo(DB_Name);
while (NULL != ptr)
{
closeFile(DB_Name, ptr);
ptr = ptr->next;
}
//release File_info
ptr = findFileInfo(DB_Name);
while (NULL != ptr)
{
fileInfo *ptr_next = ptr->next;
delete ptr;
ptr = ptr_next;
}
}
//关闭文件
void closeFile(CString DB_Name, struct fileInfo* file_temp)
{
if (NULL == file_temp)
{
cout << "buffer::closeFile:can't find the file" << endl;
return;
}
blockInfo* block_temp = file_temp->firstBlock;
blockInfo * block_delete;
//依次删除该file下的block
while (NULL != block_temp)
{
writeBlock(DB_Name, block_temp);
block_delete = block_temp;
block_temp = block_temp->next;//指向下一个节点
delete[] block_delete->cBlock;//释放内存
delete block_delete;//删除节点
}
}