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