disk_buffer_pool.h 8.4 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
//
羽飞's avatar
羽飞 已提交
14
#pragma once
羽飞's avatar
羽飞 已提交
15

羽飞's avatar
羽飞 已提交
16 17
#include <sys/types.h>
#include <sys/stat.h>
羽飞's avatar
羽飞 已提交
18 19
#include <fcntl.h>
#include <stdio.h>
L
Longda 已提交
20
#include <stdlib.h>
羽飞's avatar
羽飞 已提交
21 22
#include <string.h>
#include <time.h>
羽飞's avatar
羽飞 已提交
23
#include <string>
羽飞's avatar
羽飞 已提交
24
#include <mutex>
羽飞's avatar
羽飞 已提交
25
#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/lru_cache.h"
羽飞's avatar
羽飞 已提交
31
#include "common/lang/bitmap.h"
羽飞's avatar
羽飞 已提交
32

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

//
#define BP_INVALID_PAGE_NUM (-1)
羽飞's avatar
羽飞 已提交
38
#define BP_PAGE_SIZE (1 << 14)
羽飞's avatar
羽飞 已提交
39 40 41
#define BP_PAGE_DATA_SIZE (BP_PAGE_SIZE - sizeof(PageNum))
#define BP_FILE_SUB_HDR_SIZE (sizeof(BPFileSubHeader))

羽飞'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
/**
 * BufferPool的文件第一个页面,存放一些元数据信息,包括了后面每页的分配信息。
 * TODO 1. 当前的做法,只能分配比较少的页面,你可以扩展一下,支持更多的页面或无限多的页面吗?
 *         可以参考Linux ext(n)和Windows NTFS等文件系统
 *      2. 当前使用bitmap存放页面分配情况,但是这种方法在页面非常多的时候,查找空闲页面的
 *         效率非常低,你有办法优化吗?
 */
struct BPFileHeader {
L
Longda Feng 已提交
56 57 58
  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

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

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

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

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

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

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

羽飞's avatar
羽飞 已提交
102 103 104 105 106 107 108 109
  void set_file_desc(int fd)
  {
    file_desc_ = fd;
  }
  bool can_purge()
  {
    return pin_count_ <= 0;
  }
L
Longda Feng 已提交
110

羽飞's avatar
羽飞 已提交
111 112 113
private:
  friend class DiskBufferPool;

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

L
Longda Feng 已提交
121 122 123
class BPFrameId {
public:
  BPFrameId(int file_desc, PageNum page_num) : file_desc_(file_desc), page_num_(page_num)
羽飞's avatar
羽飞 已提交
124 125 126 127 128 129 130
  {}

  bool equal_to(const BPFrameId &other) const
  {
    return file_desc_ == other.file_desc_ && page_num_ == other.page_num_;
  }

L
Longda Feng 已提交
131
  bool operator==(const BPFrameId &other) const
羽飞's avatar
羽飞 已提交
132 133 134 135 136 137 138 139 140
  {
    return this->equal_to(other);
  }

  size_t hash() const
  {
    return static_cast<size_t>(file_desc_) << 32L | page_num_;
  }

L
Longda Feng 已提交
141 142 143 144 145 146 147 148
  int file_desc() const
  {
    return file_desc_;
  }
  PageNum page_num() const
  {
    return page_num_;
  }
羽飞's avatar
羽飞 已提交
149 150 151 152 153 154

private:
  int file_desc_;
  PageNum page_num_;
};

L
Longda Feng 已提交
155
class BPFrameManager {
羽飞's avatar
羽飞 已提交
156
public:
羽飞's avatar
羽飞 已提交
157
  BPFrameManager(const char *tag);
羽飞's avatar
羽飞 已提交
158

羽飞's avatar
羽飞 已提交
159 160 161
  RC init(int pool_num);
  RC cleanup();

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

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

羽飞's avatar
羽飞 已提交
166 167 168 169 170 171 172 173
  Frame *alloc(int file_desc, PageNum page_num);

  /**
   * 尽管frame中已经包含了file_desc和page_num,但是依然要求
   * 传入,因为frame可能忘记初始化或者没有初始化
   */
  RC free(int file_desc, PageNum page_num, Frame *frame);

羽飞's avatar
羽飞 已提交
174 175 176 177
  /**
   * 如果不能从空闲链表中分配新的页面,就使用这个接口,
   * 尝试从pin count=0的页面中淘汰一个
   */
L
Longda 已提交
178
  Frame *begin_purge();
羽飞's avatar
羽飞 已提交
179

L
Longda Feng 已提交
180 181 182 183
  size_t frame_num() const
  {
    return frames_.count();
  }
羽飞's avatar
羽飞 已提交
184 185 186 187

  /**
   * 测试使用。返回已经从内存申请的个数
   */
L
Longda Feng 已提交
188 189 190 191
  size_t total_frame_num() const
  {
    return allocator_.get_size();
  }
羽飞's avatar
羽飞 已提交
192 193 194 195

private:
  class BPFrameIdHasher {
  public:
L
Longda Feng 已提交
196 197
    size_t operator()(const BPFrameId &frame_id) const
    {
羽飞's avatar
羽飞 已提交
198 199 200 201 202 203 204 205 206
      return frame_id.hash();
    }
  };
  using FrameLruCache = common::LruCache<BPFrameId, Frame *, BPFrameIdHasher>;
  using FrameAllocator = common::MemPoolSimple<Frame>;

  std::mutex lock_;
  FrameLruCache frames_;
  FrameAllocator allocator_;
L
Longda 已提交
207
};
羽飞's avatar
羽飞 已提交
208

L
Longda Feng 已提交
209
class BufferPoolIterator {
羽飞's avatar
羽飞 已提交
210 211 212 213 214 215 216 217
public:
  BufferPoolIterator();
  ~BufferPoolIterator();

  RC init(DiskBufferPool &bp, PageNum start_page = 0);
  bool has_next();
  PageNum next();
  RC reset();
L
Longda Feng 已提交
218

羽飞's avatar
羽飞 已提交
219
private:
L
Longda Feng 已提交
220 221
  common::Bitmap bitmap_;
  PageNum current_page_num_ = -1;
羽飞's avatar
羽飞 已提交
222 223
};

L
Longda Feng 已提交
224
class DiskBufferPool {
L
Longda 已提交
225
public:
羽飞's avatar
羽飞 已提交
226
  DiskBufferPool(BufferPoolManager &bp_manager, BPFrameManager &frame_manager);
L
Longda 已提交
227
  ~DiskBufferPool();
羽飞's avatar
羽飞 已提交
228 229

  /**
L
Longda 已提交
230 231
   * 创建一个名称为指定文件名的分页文件
   */
羽飞's avatar
羽飞 已提交
232 233 234
  RC create_file(const char *file_name);

  /**
羽飞's avatar
羽飞 已提交
235
   * 根据文件名打开一个分页文件
羽飞's avatar
羽飞 已提交
236
   */
羽飞's avatar
羽飞 已提交
237
  RC open_file(const char *file_name);
羽飞's avatar
羽飞 已提交
238 239

  /**
羽飞's avatar
羽飞 已提交
240
   * 关闭分页文件
羽飞's avatar
羽飞 已提交
241
   */
羽飞's avatar
羽飞 已提交
242
  RC close_file();
羽飞's avatar
羽飞 已提交
243 244 245 246

  /**
   * 根据文件ID和页号获取指定页面到缓冲区,返回页面句柄指针。
   */
羽飞's avatar
羽飞 已提交
247
  RC get_this_page(PageNum page_num, Frame **frame);
羽飞's avatar
羽飞 已提交
248 249 250 251 252 253

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

  /**
L
Longda 已提交
257
   * 比purge_page多一个动作, 在磁盘上将对应的页数据删掉。
羽飞's avatar
羽飞 已提交
258
   */
羽飞's avatar
羽飞 已提交
259
  RC dispose_page(PageNum page_num);
羽飞's avatar
羽飞 已提交
260 261 262 263

  /**
   * 释放指定文件关联的页的内存, 如果已经脏, 则刷到磁盘,除了pinned page
   */
羽飞's avatar
羽飞 已提交
264 265
  RC purge_page(PageNum page_num);
  RC purge_all_pages();
羽飞's avatar
羽飞 已提交
266 267 268 269 270 271 272

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

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

羽飞's avatar
羽飞 已提交
280 281 282 283
  /**
   * 检查是否所有页面都是pin count == 0状态(除了第1个页面)
   * 调试使用
   */
羽飞's avatar
羽飞 已提交
284
  RC check_all_pages_unpinned();
羽飞's avatar
羽飞 已提交
285

羽飞's avatar
羽飞 已提交
286
  int file_desc() const;
羽飞's avatar
羽飞 已提交
287

羽飞's avatar
羽飞 已提交
288 289 290 291
  /**
   * 如果页面是脏的,就将数据刷新到磁盘
   */
  RC flush_page(Frame &frame);
羽飞's avatar
羽飞 已提交
292

羽飞's avatar
羽飞 已提交
293 294 295 296 297
  /**
   * 刷新所有页面到磁盘,即使pin count不是0
   */
  RC flush_all_pages();

羽飞's avatar
羽飞 已提交
298 299 300 301
  /**
   * 回放日志时处理page0中已被认定为不存在的page
   */
  RC recover_page(PageNum page_num);
L
Longda Feng 已提交
302

羽飞's avatar
羽飞 已提交
303
protected:
羽飞's avatar
羽飞 已提交
304
protected:
羽飞's avatar
羽飞 已提交
305
  RC allocate_frame(PageNum page_num, Frame **buf);
羽飞's avatar
羽飞 已提交
306 307

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

羽飞's avatar
羽飞 已提交
313 314 315 316
  /**
   * 加载指定页面的数据到内存中
   */
  RC load_page(PageNum page_num, Frame *frame);
羽飞's avatar
羽飞 已提交
317 318

private:
羽飞's avatar
羽飞 已提交
319
  BufferPoolManager &bp_manager_;
L
Longda Feng 已提交
320 321 322 323 324 325
  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
羽飞 已提交
326 327 328

private:
  friend class BufferPoolIterator;
羽飞's avatar
羽飞 已提交
329 330
};

L
Longda Feng 已提交
331
class BufferPoolManager {
羽飞's avatar
羽飞 已提交
332 333 334 335 336 337 338 339 340 341 342 343 344
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();
L
Longda Feng 已提交
345

羽飞's avatar
羽飞 已提交
346 347 348 349 350
private:
  BPFrameManager frame_manager_{"BufPool"};
  std::unordered_map<std::string, DiskBufferPool *> buffer_pools_;
  std::unordered_map<int, DiskBufferPool *> fd_buffer_pools_;
};