未验证 提交 ac87eefd 编写于 作者: 羽飞's avatar 羽飞 提交者: GitHub

Record manager doc (#185)

### What problem were solved in this pull request?

Issue Number: ref #165 

Problem:
record_manager 没有比较详细的注释

### What is changed and how it works?
加一些注释

### Other information
上级 8587eb0f
......@@ -27,34 +27,28 @@ See the Mulan PSL v2 for more details. */
class Field;
/**
* @brief 标识一个记录的位置
* 一个记录是放在某个文件的某个页面的某个槽位。这里不记录文件信息,记录页面和槽位信息
*/
struct RID
{
PageNum page_num; // record's page number
SlotNum slot_num; // record's slot number
// bool valid; // true means a valid record
RID() = default;
RID(const PageNum _page_num, const SlotNum _slot_num) : page_num(_page_num), slot_num(_slot_num)
{}
RID(const PageNum _page_num, const SlotNum _slot_num) : page_num(_page_num), slot_num(_slot_num) {}
const std::string to_string() const
{
std::stringstream ss;
ss << "PageNum:" << page_num << ", SlotNum:" << slot_num;
return ss.str();
}
bool operator==(const RID &other) const
{
return page_num == other.page_num && slot_num == other.slot_num;
}
bool operator==(const RID &other) const { return page_num == other.page_num && slot_num == other.slot_num; }
bool operator!=(const RID &other) const
{
return !(*this == other);
}
bool operator!=(const RID &other) const { return !(*this == other); }
static int compare(const RID *rid1, const RID *rid2)
{
......@@ -76,6 +70,11 @@ struct RID
static RID rid{0, 0};
return &rid;
}
/**
* @brief 返回一个“最大的”RID
* 我们假设page num和slot num都不会使用对应数值类型的最大值
*/
static RID *max()
{
static RID rid{std::numeric_limits<PageNum>::max(), std::numeric_limits<SlotNum>::max()};
......@@ -83,7 +82,13 @@ struct RID
}
};
class Record
/**
* @brief 表示一个记录
* 当前的记录都是连续存放的空间(内存或磁盘上)。
* 为了提高访问的效率,record通常直接记录指向页面上的内存,但是需要保证访问这种数据时,拿着锁资源。
* 为了方便,也提供了复制内存的方法。可以参考set_data_owner
*/
class Record
{
public:
Record() = default;
......@@ -97,9 +102,9 @@ public:
Record(const Record &other)
{
rid_ = other.rid_;
data_ = other.data_;
len_ = other.len_;
rid_ = other.rid_;
data_ = other.data_;
len_ = other.len_;
owner_ = other.owner_;
if (other.owner_) {
......@@ -110,7 +115,7 @@ public:
}
}
Record &operator =(const Record &other)
Record &operator=(const Record &other)
{
if (this == &other) {
return *this;
......@@ -124,49 +129,34 @@ public:
void set_data(char *data, int len = 0)
{
this->data_ = data;
this->len_ = len;
this->len_ = len;
}
void set_data_owner(char *data, int len)
{
ASSERT(len != 0, "the len of data should not be 0");
this->~Record();
this->data_ = data;
this->len_ = len;
this->data_ = data;
this->len_ = len;
this->owner_ = true;
}
char *data()
{
return this->data_;
}
const char *data() const
{
return this->data_;
}
void set_rid(const RID &rid)
{
this->rid_ = rid;
}
char *data() { return this->data_; }
const char *data() const { return this->data_; }
void set_rid(const RID &rid) { this->rid_ = rid; }
void set_rid(const PageNum page_num, const SlotNum slot_num)
{
this->rid_.page_num = page_num;
this->rid_.slot_num = slot_num;
}
RID &rid()
{
return rid_;
}
const RID &rid() const
{
return rid_;
}
RID &rid() { return rid_; }
const RID &rid() const { return rid_; }
private:
RID rid_;
char *data_ = nullptr;
int len_ = 0;
bool owner_ = false;
char *data_ = nullptr;
int len_ = 0; /// 如果不是record自己来管理内存,这个字段可能是无效的
bool owner_ = false; /// 表示当前是否由record来管理内存
};
......@@ -20,16 +20,19 @@ See the Mulan PSL v2 for more details. */
using namespace common;
int align8(int size)
{
return size / 8 * 8 + ((size % 8 == 0) ? 0 : 8);
}
int page_fix_size()
{
return sizeof(PageHeader);
}
int align8(int size) { return size / 8 * 8 + ((size % 8 == 0) ? 0 : 8); }
/**
* @brief 一个页面有一个页头和bitmap加上N个记录组成。这个函数返回一个页面一定会占用的固定大小
*/
int page_fix_size() { return sizeof(PageHeader); }
/**
* @brief 计算指定大小的页面,可以容纳多少个记录
*
* @param page_size 页面的大小
* @param record_size 记录的大小
*/
int page_record_capacity(int page_size, int record_size)
{
// (record_capacity * record_size) + record_capacity/8 + 1 <= (page_size - fix_size)
......@@ -37,34 +40,36 @@ int page_record_capacity(int page_size, int record_size)
return (int)((page_size - page_fix_size() - 1) / (record_size + 0.125));
}
int page_bitmap_size(int record_capacity)
{
return record_capacity / 8 + ((record_capacity % 8 == 0) ? 0 : 1);
}
/**
* @brief bitmap 记录了某个位置是否有有效的记录数据,这里给定记录个数时需要多少字节来存放bitmap数据
*
* @param record_capacity 想要存放多少记录
*/
int page_bitmap_size(int record_capacity) { return record_capacity / 8 + ((record_capacity % 8 == 0) ? 0 : 1); }
/**
* @brief 页面头固定信息加上bitmap需要的字节
*
* @param record_capacity 想要存放多少记录
*/
int page_header_size(int record_capacity)
{
const int bitmap_size = page_bitmap_size(record_capacity);
return align8(page_fix_size() + bitmap_size);
}
////////////////////////////////////////////////////////////////////////////////
RecordPageIterator::RecordPageIterator()
{}
RecordPageIterator::~RecordPageIterator()
{}
RecordPageIterator::RecordPageIterator() {}
RecordPageIterator::~RecordPageIterator() {}
void RecordPageIterator::init(RecordPageHandler &record_page_handler, SlotNum start_slot_num /*=0*/)
{
record_page_handler_ = &record_page_handler;
page_num_ = record_page_handler.get_page_num();
page_num_ = record_page_handler.get_page_num();
bitmap_.init(record_page_handler.bitmap_, record_page_handler.page_header_->record_capacity);
next_slot_num_ = bitmap_.next_setted_bit(start_slot_num);
}
bool RecordPageIterator::has_next()
{
return -1 != next_slot_num_;
}
bool RecordPageIterator::has_next() { return -1 != next_slot_num_; }
RC RecordPageIterator::next(Record &record)
{
......@@ -79,10 +84,7 @@ RC RecordPageIterator::next(Record &record)
////////////////////////////////////////////////////////////////////////////////
RecordPageHandler::~RecordPageHandler()
{
cleanup();
}
RecordPageHandler::~RecordPageHandler() { cleanup(); }
RC RecordPageHandler::init(DiskBufferPool &buffer_pool, PageNum page_num, bool readonly)
{
......@@ -102,13 +104,13 @@ RC RecordPageHandler::init(DiskBufferPool &buffer_pool, PageNum page_num, bool r
} else {
frame_->write_latch();
}
readonly_ = readonly;
readonly_ = readonly;
char *data = frame_->data();
disk_buffer_pool_ = &buffer_pool;
page_header_ = (PageHeader *)(data);
bitmap_ = data + page_fix_size();
bitmap_ = data + page_fix_size();
LOG_TRACE("Successfully init page_num %d.", page_num);
return ret;
}
......@@ -131,7 +133,7 @@ RC RecordPageHandler::recover_init(DiskBufferPool &buffer_pool, PageNum page_num
disk_buffer_pool_ = &buffer_pool;
page_header_ = (PageHeader *)(data);
bitmap_ = data + page_fix_size();
bitmap_ = data + page_fix_size();
buffer_pool.recover_page(page_num);
......@@ -141,20 +143,20 @@ RC RecordPageHandler::recover_init(DiskBufferPool &buffer_pool, PageNum page_num
RC RecordPageHandler::init_empty_page(DiskBufferPool &buffer_pool, PageNum page_num, int record_size)
{
RC ret = init(buffer_pool, page_num, false/*readonly*/);
RC ret = init(buffer_pool, page_num, false /*readonly*/);
if (ret != RC::SUCCESS) {
LOG_ERROR("Failed to init empty page page_num:record_size %d:%d.", page_num, record_size);
return ret;
}
int page_size = BP_PAGE_DATA_SIZE;
int record_phy_size = align8(record_size);
page_header_->record_num = 0;
page_header_->record_capacity = page_record_capacity(page_size, record_phy_size);
page_header_->record_real_size = record_size;
page_header_->record_size = record_phy_size;
int page_size = BP_PAGE_DATA_SIZE;
int record_phy_size = align8(record_size);
page_header_->record_num = 0;
page_header_->record_capacity = page_record_capacity(page_size, record_phy_size);
page_header_->record_real_size = record_size;
page_header_->record_size = record_phy_size;
page_header_->first_record_offset = page_header_size(page_header_->record_capacity);
bitmap_ = frame_->data() + page_fix_size();
bitmap_ = frame_->data() + page_fix_size();
memset(bitmap_, 0, page_bitmap_size(page_header_->record_capacity));
......@@ -192,7 +194,7 @@ RC RecordPageHandler::insert_record(const char *data, RID *rid)
// 找到空闲位置
Bitmap bitmap(bitmap_, page_header_->record_capacity);
int index = bitmap.next_unsetted_bit(0);
int index = bitmap.next_unsetted_bit(0);
bitmap.set_bit(index);
page_header_->record_num++;
......@@ -288,17 +290,11 @@ PageNum RecordPageHandler::get_page_num() const
return frame_->page_num();
}
bool RecordPageHandler::is_full() const
{
return page_header_->record_num >= page_header_->record_capacity;
}
bool RecordPageHandler::is_full() const { return page_header_->record_num >= page_header_->record_capacity; }
////////////////////////////////////////////////////////////////////////////////
RecordFileHandler::~RecordFileHandler()
{
this->close();
}
RecordFileHandler::~RecordFileHandler() { this->close(); }
RC RecordFileHandler::init(DiskBufferPool *buffer_pool)
{
......@@ -330,13 +326,16 @@ RC RecordFileHandler::init_free_pages()
// NOTE: 由于是初始化时的动作,所以不需要加锁控制并发
RC rc = RC::SUCCESS;
BufferPoolIterator bp_iterator;
bp_iterator.init(*disk_buffer_pool_);
RecordPageHandler record_page_handler;
PageNum current_page_num = 0;
PageNum current_page_num = 0;
while (bp_iterator.has_next()) {
current_page_num = bp_iterator.next();
rc = record_page_handler.init(*disk_buffer_pool_, current_page_num, true/*readonly*/);
rc = record_page_handler.init(*disk_buffer_pool_, current_page_num, true /*readonly*/);
if (rc != RC::SUCCESS) {
LOG_WARN("failed to init record page handler. page num=%d, rc=%d:%s", current_page_num, rc, strrc(rc));
return rc;
......@@ -354,15 +353,19 @@ RC RecordFileHandler::init_free_pages()
RC RecordFileHandler::insert_record(const char *data, int record_size, RID *rid)
{
RC ret = RC::SUCCESS;
// 找到没有填满的页面
RecordPageHandler record_page_handler;
bool page_found = false;
PageNum current_page_num = 0;
bool page_found = false;
PageNum current_page_num = 0;
// 当前要访问free_pages对象,所以需要加锁。在非并发编译模式下,不需要考虑这个锁
lock_.lock();
// 找到没有填满的页面
while (!free_pages_.empty()) {
current_page_num = *free_pages_.begin();
ret = record_page_handler.init(*disk_buffer_pool_, current_page_num, false/*readonly*/);
ret = record_page_handler.init(*disk_buffer_pool_, current_page_num, false /*readonly*/);
if (ret != RC::SUCCESS) {
lock_.unlock();
LOG_WARN("failed to init record page handler. page num=%d, rc=%d:%s", current_page_num, ret, strrc(ret));
......@@ -376,7 +379,7 @@ RC RecordFileHandler::insert_record(const char *data, int record_size, RID *rid)
record_page_handler.cleanup();
free_pages_.erase(free_pages_.begin());
}
lock_.unlock(); // 如果找到了一个有效的页面,那么此时已经拿到了页面的写锁
lock_.unlock(); // 如果找到了一个有效的页面,那么此时已经拿到了页面的写锁
// 找不到就分配一个新的页面
if (!page_found) {
......@@ -387,6 +390,7 @@ RC RecordFileHandler::insert_record(const char *data, int record_size, RID *rid)
}
current_page_num = frame->page_num();
ret = record_page_handler.init_empty_page(*disk_buffer_pool_, current_page_num, record_size);
if (ret != RC::SUCCESS) {
frame->unpin();
......@@ -395,7 +399,8 @@ RC RecordFileHandler::insert_record(const char *data, int record_size, RID *rid)
return ret;
}
frame->unpin(); // frame 在allocate_page的时候,是有一个pin的,在init_empty_page时又会增加一个,所以这里手动释放一个
// frame 在allocate_page的时候,是有一个pin的,在init_empty_page时又会增加一个,所以这里手动释放一个
frame->unpin();
// 这里的加锁顺序看起来与上面是相反的,但是不会出现死锁
// 上面的逻辑是先加lock锁,然后加页面写锁,这里是先加上
......@@ -413,6 +418,7 @@ RC RecordFileHandler::insert_record(const char *data, int record_size, RID *rid)
RC RecordFileHandler::recover_insert_record(const char *data, int record_size, RID *rid)
{
RC ret = RC::SUCCESS;
RecordPageHandler record_page_handler;
ret = record_page_handler.recover_init(*disk_buffer_pool_, rid->page_num);
......@@ -427,16 +433,24 @@ RC RecordFileHandler::recover_insert_record(const char *data, int record_size, R
RC RecordFileHandler::delete_record(const RID *rid)
{
RC rc = RC::SUCCESS;
RecordPageHandler page_handler;
if ((rc = page_handler.init(*disk_buffer_pool_, rid->page_num, false/*readonly*/)) != RC::SUCCESS) {
if ((rc = page_handler.init(*disk_buffer_pool_, rid->page_num, false /*readonly*/)) != RC::SUCCESS) {
LOG_ERROR("Failed to init record page handler.page number=%d. rc=%s", rid->page_num, strrc(rc));
return rc;
}
rc = page_handler.delete_record(rid);
page_handler.cleanup(); // 📢 这里注意要清理掉资源,否则会与insert_record中的加锁顺序冲突而可能出现死锁
if (rc == RC::SUCCESS) {
// 📢 这里注意要清理掉资源,否则会与insert_record中的加锁顺序冲突而可能出现死锁
// delete record的加锁逻辑是拿到页面锁,删除指定记录,然后加上和释放record manager锁
// insert record是加上 record manager锁,然后拿到指定页面锁再释放record manager锁
page_handler.cleanup();
if (OB_SUCC(rc)) {
// 因为这里已经释放了页面锁,并发时,其它线程可能又把该页面填满了,那就不应该再放入 free_pages_
// 中。但是这里可以不关心,因为在查找空闲页面时,会自动过滤掉已经满的页面
lock_.lock();
free_pages_.insert(rid->page_num);
LOG_TRACE("add free page %d to free page list", rid->page_num);
lock_.unlock();
}
return rc;
......@@ -444,13 +458,13 @@ RC RecordFileHandler::delete_record(const RID *rid)
RC RecordFileHandler::get_record(RecordPageHandler &page_handler, const RID *rid, bool readonly, Record *rec)
{
RC ret = RC::SUCCESS;
if (nullptr == rid || nullptr == rec) {
LOG_ERROR("Invalid rid %p or rec %p, one of them is null.", rid, rec);
return RC::INVALID_ARGUMENT;
}
if ((ret != page_handler.init(*disk_buffer_pool_, rid->page_num, readonly)) != RC::SUCCESS) {
RC ret = page_handler.init(*disk_buffer_pool_, rid->page_num, readonly);
if (OB_FAIL(ret)) {
LOG_ERROR("Failed to init record page handler.page number=%d", rid->page_num);
return ret;
}
......@@ -460,17 +474,17 @@ RC RecordFileHandler::get_record(RecordPageHandler &page_handler, const RID *rid
RC RecordFileHandler::visit_record(const RID &rid, bool readonly, std::function<void(Record &)> visitor)
{
RC rc = RC::SUCCESS;
RecordPageHandler page_handler;
if ((rc != page_handler.init(*disk_buffer_pool_, rid.page_num, readonly)) != RC::SUCCESS) {
RC rc = page_handler.init(*disk_buffer_pool_, rid.page_num, readonly);
if (OB_FAIL(rc)) {
LOG_ERROR("Failed to init record page handler.page number=%d", rid.page_num);
return rc;
}
Record record;
rc = page_handler.get_record(&rid, &record);
if (rc != RC::SUCCESS) {
if (OB_FAIL(rc)) {
LOG_WARN("failed to get record from record page handle. rid=%s, rc=%s", rid.to_string().c_str(), strrc(rc));
return rc;
}
......@@ -481,23 +495,17 @@ RC RecordFileHandler::visit_record(const RID &rid, bool readonly, std::function<
////////////////////////////////////////////////////////////////////////////////
RecordFileScanner::~RecordFileScanner()
{
close_scan();
}
RecordFileScanner::~RecordFileScanner() { close_scan(); }
RC RecordFileScanner::open_scan(Table *table,
DiskBufferPool &buffer_pool,
Trx *trx,
bool readonly,
ConditionFilter *condition_filter)
RC RecordFileScanner::open_scan(
Table *table, DiskBufferPool &buffer_pool, Trx *trx, bool readonly, ConditionFilter *condition_filter)
{
close_scan();
table_ = table;
table_ = table;
disk_buffer_pool_ = &buffer_pool;
trx_ = trx;
readonly_ = readonly;
trx_ = trx;
readonly_ = readonly;
RC rc = bp_iterator_.init(buffer_pool);
if (rc != RC::SUCCESS) {
......@@ -513,54 +521,82 @@ RC RecordFileScanner::open_scan(Table *table,
return rc;
}
/**
* @brief 从当前位置开始找到下一条有效的记录
*
* 如果当前页面还有记录没有访问,就遍历当前的页面。
* 当前页面遍历完了,就遍历下一个页面,然后找到有效的记录
*/
RC RecordFileScanner::fetch_next_record()
{
RC rc = RC::SUCCESS;
if (record_page_iterator_.is_valid()) {
// 当前页面还是有效的,尝试看一下是否有有效记录
rc = fetch_next_record_in_page();
if (rc == RC::SUCCESS || rc != RC::RECORD_EOF) {
// 有有效记录:RC::SUCCESS
// 或者出现了错误,rc != (RC::SUCCESS or RC::RECORD_EOF)
// RECORD_EOF 表示当前页面已经遍历完了
return rc;
}
}
// 上个页面遍历完了,或者还没有开始遍历某个页面,那么就从一个新的页面开始遍历查找
while (bp_iterator_.has_next()) {
PageNum page_num = bp_iterator_.next();
record_page_handler_.cleanup();
rc = record_page_handler_.init(*disk_buffer_pool_, page_num, readonly_);
if (rc != RC::SUCCESS) {
LOG_WARN("failed to init record page handler. rc=%d:%s", rc, strrc(rc));
if (OB_FAIL(rc)) {
LOG_WARN("failed to init record page handler. page_num=%d, rc=%s", page_num, strrc(rc));
return rc;
}
record_page_iterator_.init(record_page_handler_);
rc = fetch_next_record_in_page();
if (rc == RC::SUCCESS || rc != RC::RECORD_EOF) {
// 有有效记录:RC::SUCCESS
// 或者出现了错误,rc != (RC::SUCCESS or RC::RECORD_EOF)
// RECORD_EOF 表示当前页面已经遍历完了
return rc;
}
}
// 所有的页面都遍历完了,没有数据了
next_record_.rid().slot_num = -1;
return RC::RECORD_EOF;
}
/**
* @brief 遍历当前页面,尝试找到一条有效的记录
*
* @return RC
*/
RC RecordFileScanner::fetch_next_record_in_page()
{
RC rc = RC::SUCCESS;
while (record_page_iterator_.has_next()) {
rc = record_page_iterator_.next(next_record_);
if (rc != RC::SUCCESS) {
const auto page_num = record_page_handler_.get_page_num();
LOG_TRACE("failed to get next record from page. page_num=%d, rc=%s", page_num, strrc(rc));
return rc;
}
// 如果有过滤条件,就用过滤条件过滤一下
if (condition_filter_ != nullptr && !condition_filter_->filter(next_record_)) {
continue;
}
// 如果是某个事务上遍历数据,还要看看事务访问是否有冲突
if (trx_ == nullptr) {
return rc;
}
// 让当前事务探测一下是否访问冲突,或者需要加锁、等锁等操作,由事务自己决定
rc = trx_->visit_record(table_, next_record_, readonly_);
if (rc == RC::RECORD_INVISIBLE) {
// 可以参考MvccTrx,表示当前记录不可见
// 这种模式仅在 readonly 事务下是有效的
continue;
}
return rc;
......@@ -585,10 +621,7 @@ RC RecordFileScanner::close_scan()
return RC::SUCCESS;
}
bool RecordFileScanner::has_next()
{
return next_record_.rid().slot_num != -1;
}
bool RecordFileScanner::has_next() { return next_record_.rid().slot_num != -1; }
RC RecordFileScanner::next(Record &record)
{
......
......@@ -25,13 +25,39 @@ class RecordPageHandler;
class Trx;
class Table;
/**
* 这里负责管理在一个文件上表记录(行)的组织/管理
*
* 表记录管理的内容包括如何在文件上存放、读取、检索。也就是记录的增删改查。
* 这里的文件都会被拆分成页面,每个页面都有一样的大小。更详细的信息可以参考BufferPool。
* 按照BufferPool的设计,第一个页面用来存放BufferPool本身的元数据,比如当前文件有多少页面、已经分配了多少页面、
* 每个页面的分配状态等。所以第一个页面对RecordManager来说没有作用。RecordManager 本身没有再单独拿一个页面
* 来存放元数据,每一个页面都存放了一个页面头信息,也就是每个页面都有 RecordManager 的元数据信息,可以参考
* PageHeader,这虽然有点浪费但是做起来简单。
*
* 对单个页面来说,最开始是一个页头,然后接着就是一行行记录(会对齐)。
* 如何标识一个记录,或者定位一个记录?
* 使用RID,即record identifier。使用 page num 表示所在的页面,slot num 表示当前在页面中的位置。因为这里的
* 记录都是定长的,所以根据slot num 可以直接计算出记录的起始位置。
* 问题1:那么如果记录不是定长的,还能使用 slot num 吗?
* 问题2:如何更有效地存放不定长数据呢?
* 问题3:如果一个页面不能存放一个记录,那么怎么组织记录存放效果更好呢?
*
* 按照上面的描述,这里提供了几个类,分别是:
* - RecordFileHandler:管理整个文件/表的记录增删改查
* - RecordPageHandler:管理单个页面上记录的增删改查
* - RecordFileScanner:可以用来遍历整个文件上的所有记录
* - RecordPageIterator:可以用来遍历指定页面上的所有记录
* - PageHeader:每个页面上都会记录的页面头信息
*/
/**
* 数据文件,按照页面来组织,每一页都存放一些记录/数据行
* 每一页都有一个这样的页头,虽然看起来浪费,但是现在就简单的这么做
* 从这个页头描述的信息来看,当前仅支持定长行/记录。如果要支持变长记录,
* 或者超长(超出一页)的记录,这么做是不合适的。
*/
struct PageHeader
struct PageHeader
{
int32_t record_num; // 当前页面记录的个数
int32_t record_capacity; // 最大记录个数
......@@ -43,51 +69,103 @@ struct PageHeader
/**
* 遍历一个页面中每条记录的iterator
*/
class RecordPageIterator
class RecordPageIterator
{
public:
RecordPageIterator();
~RecordPageIterator();
/**
* @brief 初始化一个迭代器
*
* @param record_page_handler 负责某个页面上记录增删改查的对象
* @param start_slot_num 从哪个记录开始扫描,默认是0
*/
void init(RecordPageHandler &record_page_handler, SlotNum start_slot_num = 0);
bool has_next();
RC next(Record &record);
RC next(Record &record);
bool is_valid() const
{
return record_page_handler_ != nullptr;
}
bool is_valid() const { return record_page_handler_ != nullptr; }
private:
RecordPageHandler *record_page_handler_ = nullptr;
PageNum page_num_ = BP_INVALID_PAGE_NUM;
common::Bitmap bitmap_;
SlotNum next_slot_num_ = 0;
PageNum page_num_ = BP_INVALID_PAGE_NUM;
common::Bitmap bitmap_; // bitmap 的相关信息可以参考 RecordPageHandler 的说明
SlotNum next_slot_num_ = 0; // 当前遍历到了哪一个slot
};
/**
* 负责处理一个页面中各种操作,比如插入记录、删除记录或者查找记录
*
* 当前定长记录模式下每个页面的组织大概是这样的:
* |-------------------------------------|
* | PageHeader | record allocate bitmap |
* |-------------------------------------|
* | record1 | record2 | ..... | recordN |
* |-------------------------------------|
*/
class RecordPageHandler
class RecordPageHandler
{
public:
RecordPageHandler() = default;
~RecordPageHandler();
/**
* @brief 初始化
*
* @param buffer_pool 关联某个文件时,都通过buffer pool来做读写文件
* @param page_num 当前处理哪个页面
* @param readonly 是否只读。在访问页面时,需要对页面加锁
*/
RC init(DiskBufferPool &buffer_pool, PageNum page_num, bool readonly);
/// 当前所有的recover函数都没有使用,可以忽略
RC recover_init(DiskBufferPool &buffer_pool, PageNum page_num);
/**
* @brief 对一个新的页面做初始化
*
* @param buffer_pool 关联某个文件时,都通过buffer pool来做读写文件
* @param page_num 当前处理哪个页面
* @param record_size 每个记录的大小
*/
RC init_empty_page(DiskBufferPool &buffer_pool, PageNum page_num, int record_size);
/**
* @brief 操作结束后做的清理工作,比如释放页面、解锁
*/
RC cleanup();
/**
* @brief 插入一条记录
*
* @param data 要插入的记录
* @param rid 如果插入成功,通过这个参数返回插入的位置
*/
RC insert_record(const char *data, RID *rid);
RC recover_insert_record(const char *data, RID *rid);
/**
* @brief 删除指定的记录
*
* @param rid 要删除的记录标识
*/
RC delete_record(const RID *rid);
/**
* @brief 获取指定位置的记录数据
*
* @param rid 指定的位置
* @param rec 返回指定的数据。这里不会将数据复制出来,而是使用指针,所以调用者必须保证数据使用期间受到保护
*/
RC get_record(const RID *rid, Record *rec);
PageNum get_page_num() const;
/**
* @brief 当前页面是否已经没有空闲位置插入新的记录
*/
bool is_full() const;
protected:
......@@ -97,30 +175,37 @@ protected:
}
protected:
DiskBufferPool *disk_buffer_pool_ = nullptr;
bool readonly_ = false;
Frame *frame_ = nullptr;
PageHeader *page_header_ = nullptr;
char *bitmap_ = nullptr;
DiskBufferPool *disk_buffer_pool_ = nullptr; // 当前操作的buffer pool(文件)
bool readonly_ = false; // 当前的操作是否都是只读的
Frame *frame_ = nullptr; // 当前操作页面关联的frame(frame的更多概念可以参考buffer pool和frame)
PageHeader *page_header_ = nullptr; // 当前页面上页面头
char *bitmap_ = nullptr; // 当前页面上record分配状态信息bitmap内存起始位置
private:
friend class RecordPageIterator;
};
class RecordFileHandler
/**
* @brief 管理整个文件中记录的增删改查
* 整个文件的组织格式请参考该文件中最前面的注释
*/
class RecordFileHandler
{
public:
RecordFileHandler() = default;
~RecordFileHandler();
/**
* @brief 初始化
*
* @param buffer_pool 当前操作的是哪个文件
*/
RC init(DiskBufferPool *buffer_pool);
void close();
/**
* 更新指定文件中的记录,rec指向的记录结构中的rid字段为要更新的记录的标识符,
* pData字段指向新的记录内容
* @brief 关闭,做一些资源清理的工作
*/
RC update_record(const Record *rec);
void close();
/**
* 从指定文件中删除标识符为rid的记录
......@@ -135,21 +220,42 @@ public:
/**
* 获取指定文件中标识符为rid的记录内容到rec指向的记录结构中
* @param page_handler[in]
* 访问记录时,会拿住一些资源不释放,比如页面锁,使用这个对象保存相关的资源,并在析构时会自动释放
* @param rid 想要获取的记录ID
* @param readonly 获取的记录是只读的还是需要修改的
* @param rec[out] 通过这个参数返回获取到的记录
* @note rec 参数返回的记录并不会复制数据内存。page_handler 对象会拿着相关的资源,比如 pin 住页面和加上页面锁。
* 如果page_handler 释放了,那也不能再访问rec对象了。
*/
RC get_record(RecordPageHandler &page_handler, const RID *rid, bool readonly, Record *rec);
/**
* @brief 与get_record类似,访问某个记录,并提供回调函数来操作相应的记录
*
* @param rid 想要访问的记录ID
* @param readonly 是否会修改记录
* @param visitor 访问记录的回调函数
*/
RC visit_record(const RID &rid, bool readonly, std::function<void(Record &)> visitor);
private:
/**
* @brief 初始化记录当前没有填满记录的页面
*/
RC init_free_pages();
private:
DiskBufferPool *disk_buffer_pool_ = nullptr;
DiskBufferPool *disk_buffer_pool_ = nullptr;
std::unordered_set<PageNum> free_pages_; // 没有填充满的页面集合
common::Mutex lock_; // 当编译时增加-DCONCURRENCY=ON 选项时,才会真正的支持并发
common::Mutex lock_; // 当编译时增加-DCONCURRENCY=ON 选项时,才会真正的支持并发
};
class RecordFileScanner
/**
* @brief 遍历某个文件中所有记录
* 遍历所有的页面,同时访问这些页面中所有的记录
*/
class RecordFileScanner
{
public:
RecordFileScanner() = default;
......@@ -164,11 +270,7 @@ public:
* 删除时也需要遍历找到数据,然后删除,这时就需要加写锁
* @param condition_filter 做一些初步过滤操作
*/
RC open_scan(Table *table,
DiskBufferPool &buffer_pool,
Trx *trx,
bool readonly,
ConditionFilter *condition_filter);
RC open_scan(Table *table, DiskBufferPool &buffer_pool, Trx *trx, bool readonly, ConditionFilter *condition_filter);
/**
* 关闭一个文件扫描,释放相应的资源
......@@ -176,21 +278,21 @@ public:
RC close_scan();
bool has_next();
RC next(Record &record);
RC next(Record &record);
private:
RC fetch_next_record();
RC fetch_next_record_in_page();
private:
Table * table_ = nullptr;
DiskBufferPool * disk_buffer_pool_ = nullptr;
Trx * trx_ = nullptr;
bool readonly_ = false; // 遍历出来的数据,是否可能对它做修改
BufferPoolIterator bp_iterator_; // 遍历buffer pool的所有页面
ConditionFilter * condition_filter_ = nullptr; // 过滤record
RecordPageHandler record_page_handler_;
RecordPageIterator record_page_iterator_; // 遍历某个页面上的所有record
Record next_record_;
Table *table_ = nullptr; // 当前遍历的是哪张表。这个字段仅供事务函数使用,如果设计合适,可以去掉
DiskBufferPool *disk_buffer_pool_ = nullptr; // 当前访问的文件
Trx *trx_ = nullptr; // 当前是哪个事务在遍历
bool readonly_ = false; // 遍历出来的数据,是否可能对它做修改
BufferPoolIterator bp_iterator_; // 遍历buffer pool的所有页面
ConditionFilter *condition_filter_ = nullptr; // 过滤record
RecordPageHandler record_page_handler_;
RecordPageIterator record_page_iterator_; // 遍历某个页面上的所有record
Record next_record_;
};
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册