disk_buffer_pool.h 7.0 KB
Newer Older
羽飞's avatar
羽飞 已提交
1 2 3 4 5 6 7 8 9 10 11
/* Copyright (c) 2021 Xie Meiyi(xiemeiyi@hust.edu.cn) and OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
         http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

//
12
// Created by Meiyi & Longda on 2021/4/13.
羽飞's avatar
羽飞 已提交
13 14 15 16
//
#ifndef __OBSERVER_STORAGE_COMMON_PAGE_MANAGER_H_
#define __OBSERVER_STORAGE_COMMON_PAGE_MANAGER_H_

羽飞's avatar
羽飞 已提交
17 18
#include <sys/types.h>
#include <sys/stat.h>
羽飞's avatar
羽飞 已提交
19 20
#include <fcntl.h>
#include <stdio.h>
L
Longda 已提交
21
#include <stdlib.h>
羽飞's avatar
羽飞 已提交
22 23
#include <string.h>
#include <time.h>
羽飞's avatar
羽飞 已提交
24 25
#include <string>
#include <unordered_map>
羽飞's avatar
羽飞 已提交
26 27

#include "rc.h"
羽飞's avatar
羽飞 已提交
28
#include "defs.h"
L
Longda 已提交
29
#include "common/mm/mem_pool.h"
羽飞's avatar
羽飞 已提交
30
#include "common/lang/bitmap.h"
羽飞's avatar
羽飞 已提交
31

羽飞's avatar
羽飞 已提交
32 33
class BufferPoolManager;
class DiskBufferPool;
羽飞's avatar
羽飞 已提交
34 35 36

//
#define BP_INVALID_PAGE_NUM (-1)
L
Longda 已提交
37
#define BP_PAGE_SIZE (1 << 13)
羽飞's avatar
羽飞 已提交
38 39
#define BP_PAGE_DATA_SIZE (BP_PAGE_SIZE - sizeof(PageNum))
#define BP_FILE_SUB_HDR_SIZE (sizeof(BPFileSubHeader))
L
Longda 已提交
40
#define BP_BUFFER_SIZE 256
羽飞's avatar
羽飞 已提交
41

羽飞's avatar
羽飞 已提交
42
struct Page {
羽飞's avatar
羽飞 已提交
43 44
  PageNum page_num;
  char data[BP_PAGE_DATA_SIZE];
羽飞's avatar
羽飞 已提交
45
};
羽飞's avatar
羽飞 已提交
46 47
// sizeof(Page) should be equal to BP_PAGE_SIZE

羽飞's avatar
羽飞 已提交
48 49 50 51 52 53 54 55 56 57 58
/**
 * BufferPool的文件第一个页面,存放一些元数据信息,包括了后面每页的分配信息。
 * TODO 1. 当前的做法,只能分配比较少的页面,你可以扩展一下,支持更多的页面或无限多的页面吗?
 *         可以参考Linux ext(n)和Windows NTFS等文件系统
 *      2. 当前使用bitmap存放页面分配情况,但是这种方法在页面非常多的时候,查找空闲页面的
 *         效率非常低,你有办法优化吗?
 */
struct BPFileHeader {
  int32_t page_count;        //! 当前文件一共有多少个页面
  int32_t allocated_pages;   //! 已经分配了多少个页面
  char    bitmap[0];         //! 页面分配位图, 第0个页面(就是当前页面),总是1
羽飞's avatar
羽飞 已提交
59 60 61 62 63

  /**
   * 能够分配的最大的页面个数,即bitmap的字节数 乘以8
   */
  static const int MAX_PAGE_NUM = (BP_PAGE_DATA_SIZE - sizeof(page_count) - sizeof(allocated_pages)) * 8;
羽飞's avatar
羽飞 已提交
64
};
L
Longda 已提交
65

羽飞's avatar
羽飞 已提交
66 67 68 69
class Frame
{
public:
  void clear_page()
L
Longda 已提交
70
  {
羽飞's avatar
羽飞 已提交
71
    memset(&page_, 0, sizeof(page_));
L
Longda 已提交
72
  }
羽飞's avatar
羽飞 已提交
73

羽飞's avatar
羽飞 已提交
74 75 76 77
  PageNum page_num() const
  {
    return page_.page_num;
  }
L
Longda 已提交
78

羽飞's avatar
羽飞 已提交
79 80 81
  void set_page_num(PageNum page_num)
  {
    page_.page_num = page_num;
羽飞's avatar
羽飞 已提交
82
  }
羽飞's avatar
羽飞 已提交
83 84 85 86 87

  /**
   * 标记指定页面为“脏”页。如果修改了页面的内容,则应调用此函数,
   * 以便该页面被淘汰出缓冲区时系统将新的页面数据写入磁盘文件
   */
羽飞's avatar
羽飞 已提交
88
  void mark_dirty() {
羽飞's avatar
羽飞 已提交
89
    dirty_ = true;
羽飞's avatar
羽飞 已提交
90 91 92
  }

  char *data() {
羽飞's avatar
羽飞 已提交
93
    return page_.data;
羽飞's avatar
羽飞 已提交
94
  }
羽飞's avatar
羽飞 已提交
95

羽飞's avatar
羽飞 已提交
96 97 98 99
  int file_desc() const
  {
    return file_desc_;
  }
羽飞's avatar
羽飞 已提交
100

羽飞's avatar
羽飞 已提交
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
  void set_file_desc(int fd)
  {
    file_desc_ = fd;
  }
  bool can_purge()
  {
    return pin_count_ <= 0;
  }
private:
  friend class DiskBufferPool;

  bool          dirty_     = false;
  unsigned int  pin_count_ = 0;
  unsigned long acc_time_  = 0;
  int           file_desc_ = -1;
  Page          page_;
L
Longda 已提交
117
};
羽飞's avatar
羽飞 已提交
118

羽飞's avatar
羽飞 已提交
119 120
class BPFrameManager : public common::MemPoolSimple<Frame>
{
羽飞's avatar
羽飞 已提交
121
public:
羽飞's avatar
羽飞 已提交
122
  BPFrameManager(const char *tag);
羽飞's avatar
羽飞 已提交
123

L
Longda 已提交
124
  Frame *get(int file_desc, PageNum page_num);
羽飞's avatar
羽飞 已提交
125

L
Longda 已提交
126 127
  std::list<Frame *> find_list(int file_desc);

羽飞's avatar
羽飞 已提交
128 129 130 131
  /**
   * 如果不能从空闲链表中分配新的页面,就使用这个接口,
   * 尝试从pin count=0的页面中淘汰一个
   */
L
Longda 已提交
132 133
  Frame *begin_purge();
};
羽飞's avatar
羽飞 已提交
134

羽飞's avatar
羽飞 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
class BufferPoolIterator
{
public:
  BufferPoolIterator();
  ~BufferPoolIterator();

  RC init(DiskBufferPool &bp, PageNum start_page = 0);
  bool has_next();
  PageNum next();
  RC reset();
private:
  common::Bitmap   bitmap_;
  PageNum  current_page_num_ = -1;
};

class DiskBufferPool
{
L
Longda 已提交
152
public:
羽飞's avatar
羽飞 已提交
153
  DiskBufferPool(BufferPoolManager &bp_manager, BPFrameManager &frame_manager);
L
Longda 已提交
154
  ~DiskBufferPool();
羽飞's avatar
羽飞 已提交
155 156

  /**
L
Longda 已提交
157 158
   * 创建一个名称为指定文件名的分页文件
   */
羽飞's avatar
羽飞 已提交
159 160 161
  RC create_file(const char *file_name);

  /**
羽飞's avatar
羽飞 已提交
162
   * 根据文件名打开一个分页文件
羽飞's avatar
羽飞 已提交
163
   */
羽飞's avatar
羽飞 已提交
164
  RC open_file(const char *file_name);
羽飞's avatar
羽飞 已提交
165 166

  /**
羽飞's avatar
羽飞 已提交
167
   * 关闭分页文件
羽飞's avatar
羽飞 已提交
168
   */
羽飞's avatar
羽飞 已提交
169
  RC close_file();
羽飞's avatar
羽飞 已提交
170 171 172 173

  /**
   * 根据文件ID和页号获取指定页面到缓冲区,返回页面句柄指针。
   */
羽飞's avatar
羽飞 已提交
174
  RC get_this_page(PageNum page_num, Frame **frame);
羽飞's avatar
羽飞 已提交
175 176 177 178 179 180

  /**
   * 在指定文件中分配一个新的页面,并将其放入缓冲区,返回页面句柄指针。
   * 分配页面时,如果文件中有空闲页,就直接分配一个空闲页;
   * 如果文件中没有空闲页,则扩展文件规模来增加新的空闲页。
   */
羽飞's avatar
羽飞 已提交
181
  RC allocate_page(Frame **frame);
羽飞's avatar
羽飞 已提交
182 183

  /**
L
Longda 已提交
184
   * 比purge_page多一个动作, 在磁盘上将对应的页数据删掉。
羽飞's avatar
羽飞 已提交
185
   */
羽飞's avatar
羽飞 已提交
186
  RC dispose_page(PageNum page_num);
羽飞's avatar
羽飞 已提交
187 188 189 190

  /**
   * 释放指定文件关联的页的内存, 如果已经脏, 则刷到磁盘,除了pinned page
   */
羽飞's avatar
羽飞 已提交
191 192
  RC purge_page(PageNum page_num);
  RC purge_all_pages();
羽飞's avatar
羽飞 已提交
193 194 195 196 197 198 199

  /**
   * 此函数用于解除pageHandle对应页面的驻留缓冲区限制。
   * 在调用GetThisPage或AllocatePage函数将一个页面读入缓冲区后,
   * 该页面被设置为驻留缓冲区状态,以防止其在处理过程中被置换出去,
   * 因此在该页面使用完之后应调用此函数解除该限制,使得该页面此后可以正常地被淘汰出缓冲区
   */
羽飞's avatar
羽飞 已提交
200
  RC unpin_page(Frame *frame);
羽飞's avatar
羽飞 已提交
201 202 203 204

  /**
   * 获取文件的总页数
   */
羽飞's avatar
羽飞 已提交
205 206
  RC get_page_count(int *page_count);

羽飞's avatar
羽飞 已提交
207 208 209 210
  /**
   * 检查是否所有页面都是pin count == 0状态(除了第1个页面)
   * 调试使用
   */
羽飞's avatar
羽飞 已提交
211
  RC check_all_pages_unpinned();
羽飞's avatar
羽飞 已提交
212

羽飞's avatar
羽飞 已提交
213
  int file_desc() const;
羽飞's avatar
羽飞 已提交
214

羽飞's avatar
羽飞 已提交
215 216 217 218
  /**
   * 如果页面是脏的,就将数据刷新到磁盘
   */
  RC flush_page(Frame &frame);
羽飞's avatar
羽飞 已提交
219

羽飞's avatar
羽飞 已提交
220 221 222 223 224
  /**
   * 刷新所有页面到磁盘,即使pin count不是0
   */
  RC flush_all_pages();

羽飞's avatar
羽飞 已提交
225
protected:
羽飞's avatar
羽飞 已提交
226
  RC allocate_frame(Frame **buf);
羽飞's avatar
羽飞 已提交
227 228

  /**
羽飞's avatar
羽飞 已提交
229
   * 刷新指定页面到磁盘(flush),并且释放关联的Frame
羽飞's avatar
羽飞 已提交
230
   */
羽飞's avatar
羽飞 已提交
231 232
  RC purge_frame(Frame *used_frame);
  RC check_page_num(PageNum page_num);
L
Longda 已提交
233

羽飞's avatar
羽飞 已提交
234 235 236 237
  /**
   * 加载指定页面的数据到内存中
   */
  RC load_page(PageNum page_num, Frame *frame);
羽飞's avatar
羽飞 已提交
238 239

private:
羽飞's avatar
羽飞 已提交
240 241 242 243 244 245 246
  BufferPoolManager &bp_manager_;
  BPFrameManager &   frame_manager_;
  std::string        file_name_;
  int                file_desc_ = -1;
  Frame *            hdr_frame_ = nullptr;
  BPFileHeader *     file_header_ = nullptr;
  std::set<PageNum>  disposed_pages;
羽飞's avatar
羽飞 已提交
247 248 249

private:
  friend class BufferPoolIterator;
羽飞's avatar
羽飞 已提交
250 251
};

羽飞's avatar
羽飞 已提交
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
class BufferPoolManager
{
public:
  BufferPoolManager();
  ~BufferPoolManager();

  RC create_file(const char *file_name);
  RC open_file(const char *file_name, DiskBufferPool *&bp);
  RC close_file(const char *file_name);

  RC flush_page(Frame &frame);

public:
  static void set_instance(BufferPoolManager *bpm);
  static BufferPoolManager &instance();
  
private:
  BPFrameManager frame_manager_{"BufPool"};
  std::unordered_map<std::string, DiskBufferPool *> buffer_pools_;
  std::unordered_map<int, DiskBufferPool *> fd_buffer_pools_;
};
羽飞's avatar
羽飞 已提交
273

L
Longda 已提交
274
#endif  //__OBSERVER_STORAGE_COMMON_PAGE_MANAGER_H_