disk_buffer_pool.h 7.8 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>
26
#include <functional>
羽飞's avatar
羽飞 已提交
27

羽飞's avatar
羽飞 已提交
28 29
#include "common/rc.h"
#include "common/types.h"
30
#include "common/lang/mutex.h"
L
Longda 已提交
31
#include "common/mm/mem_pool.h"
羽飞's avatar
羽飞 已提交
32
#include "common/lang/lru_cache.h"
羽飞's avatar
羽飞 已提交
33
#include "common/lang/bitmap.h"
34 35
#include "storage/buffer/page.h"
#include "storage/buffer/frame.h"
羽飞's avatar
羽飞 已提交
36

羽飞's avatar
羽飞 已提交
37 38
class BufferPoolManager;
class DiskBufferPool;
羽飞's avatar
羽飞 已提交
39

羽飞's avatar
羽飞 已提交
40 41 42 43 44
/**
 * @brief BufferPool 的实现
 * @defgroup BufferPool
 */

羽飞's avatar
羽飞 已提交
45 46
#define BP_FILE_SUB_HDR_SIZE (sizeof(BPFileSubHeader))

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

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

  std::string to_string() const;
羽飞's avatar
羽飞 已提交
67
};
L
Longda 已提交
68

羽飞's avatar
羽飞 已提交
69 70 71 72
/**
 * @brief 管理页面Frame
 * @ingroup BufferPool
 */
73 74
class BPFrameManager 
{
羽飞's avatar
羽飞 已提交
75
public:
羽飞's avatar
羽飞 已提交
76
  BPFrameManager(const char *tag);
羽飞's avatar
羽飞 已提交
77

羽飞's avatar
羽飞 已提交
78 79 80
  RC init(int pool_num);
  RC cleanup();

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

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

羽飞's avatar
羽飞 已提交
85 86 87 88 89 90 91 92
  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
羽飞 已提交
93 94
  /**
   * 如果不能从空闲链表中分配新的页面,就使用这个接口,
95 96 97 98
   * 尝试从pin count=0的页面中淘汰一些
   * @param count 想要purge多少个页面
   * @param purger 需要在释放frame之前,对页面做些什么操作。当前是刷新脏数据到磁盘
   * @return 返回本次清理了多少个页面
羽飞's avatar
羽飞 已提交
99
   */
100
  int purge_frames(int count, std::function<RC(Frame *frame)> purger);
羽飞's avatar
羽飞 已提交
101

L
Longda Feng 已提交
102 103 104 105
  size_t frame_num() const
  {
    return frames_.count();
  }
羽飞's avatar
羽飞 已提交
106 107 108 109

  /**
   * 测试使用。返回已经从内存申请的个数
   */
L
Longda Feng 已提交
110 111 112 113
  size_t total_frame_num() const
  {
    return allocator_.get_size();
  }
羽飞's avatar
羽飞 已提交
114

115 116 117 118
private:
  Frame *get_internal(const FrameId &frame_id);
  RC     free_internal(const FrameId &frame_id, Frame *frame);

羽飞's avatar
羽飞 已提交
119 120 121
private:
  class BPFrameIdHasher {
  public:
122
    size_t operator()(const FrameId &frame_id) const
L
Longda Feng 已提交
123
    {
羽飞's avatar
羽飞 已提交
124 125 126
      return frame_id.hash();
    }
  };
127 128

  using FrameLruCache = common::LruCache<FrameId, Frame *, BPFrameIdHasher>;
羽飞's avatar
羽飞 已提交
129 130 131
  using FrameAllocator = common::MemPoolSimple<Frame>;

  std::mutex lock_;
132
  FrameLruCache  frames_;
羽飞's avatar
羽飞 已提交
133
  FrameAllocator allocator_;
L
Longda 已提交
134
};
羽飞's avatar
羽飞 已提交
135

羽飞's avatar
羽飞 已提交
136 137 138 139
/**
 * @brief 用于遍历BufferPool中的所有页面
 * @ingroup BufferPool
 */
140
class BufferPoolIterator
141
{
羽飞's avatar
羽飞 已提交
142 143 144 145 146 147 148 149
public:
  BufferPoolIterator();
  ~BufferPoolIterator();

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

羽飞's avatar
羽飞 已提交
151
private:
L
Longda Feng 已提交
152 153
  common::Bitmap bitmap_;
  PageNum current_page_num_ = -1;
羽飞's avatar
羽飞 已提交
154 155
};

羽飞's avatar
羽飞 已提交
156 157 158 159
/**
 * @brief BufferPool的实现
 * @ingroup BufferPool
 */
160 161
class DiskBufferPool 
{
L
Longda 已提交
162
public:
羽飞's avatar
羽飞 已提交
163
  DiskBufferPool(BufferPoolManager &bp_manager, BPFrameManager &frame_manager);
L
Longda 已提交
164
  ~DiskBufferPool();
羽飞's avatar
羽飞 已提交
165 166

  /**
L
Longda 已提交
167 168
   * 创建一个名称为指定文件名的分页文件
   */
羽飞's avatar
羽飞 已提交
169 170 171
  RC create_file(const char *file_name);

  /**
羽飞's avatar
羽飞 已提交
172
   * 根据文件名打开一个分页文件
羽飞's avatar
羽飞 已提交
173
   */
羽飞's avatar
羽飞 已提交
174
  RC open_file(const char *file_name);
羽飞's avatar
羽飞 已提交
175 176

  /**
羽飞's avatar
羽飞 已提交
177
   * 关闭分页文件
羽飞's avatar
羽飞 已提交
178
   */
羽飞's avatar
羽飞 已提交
179
  RC close_file();
羽飞's avatar
羽飞 已提交
180 181 182 183

  /**
   * 根据文件ID和页号获取指定页面到缓冲区,返回页面句柄指针。
   */
羽飞's avatar
羽飞 已提交
184
  RC get_this_page(PageNum page_num, Frame **frame);
羽飞's avatar
羽飞 已提交
185 186 187 188 189 190

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

193 194 195 196 197
  /**
   * @brief 释放某个页面,将此页面设置为未分配状态
   * 
   * @param page_num 待释放的页面
   */
羽飞's avatar
羽飞 已提交
198
  RC dispose_page(PageNum page_num);
羽飞's avatar
羽飞 已提交
199 200

  /**
201 202
   * @brief 释放指定文件关联的页的内存
   * 如果已经脏, 则刷到磁盘,除了pinned page
羽飞's avatar
羽飞 已提交
203
   */
羽飞's avatar
羽飞 已提交
204 205
  RC purge_page(PageNum page_num);
  RC purge_all_pages();
羽飞's avatar
羽飞 已提交
206 207

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

羽飞's avatar
羽飞 已提交
216 217 218 219
  /**
   * 检查是否所有页面都是pin count == 0状态(除了第1个页面)
   * 调试使用
   */
羽飞's avatar
羽飞 已提交
220
  RC check_all_pages_unpinned();
羽飞's avatar
羽飞 已提交
221

羽飞's avatar
羽飞 已提交
222
  int file_desc() const;
羽飞's avatar
羽飞 已提交
223

羽飞's avatar
羽飞 已提交
224 225 226 227
  /**
   * 如果页面是脏的,就将数据刷新到磁盘
   */
  RC flush_page(Frame &frame);
羽飞's avatar
羽飞 已提交
228

羽飞's avatar
羽飞 已提交
229 230 231 232 233
  /**
   * 刷新所有页面到磁盘,即使pin count不是0
   */
  RC flush_all_pages();

羽飞's avatar
羽飞 已提交
234 235 236 237
  /**
   * 回放日志时处理page0中已被认定为不存在的page
   */
  RC recover_page(PageNum page_num);
L
Longda Feng 已提交
238

羽飞's avatar
羽飞 已提交
239
protected:
羽飞's avatar
羽飞 已提交
240
  RC allocate_frame(PageNum page_num, Frame **buf);
羽飞's avatar
羽飞 已提交
241 242

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

羽飞's avatar
羽飞 已提交
248 249 250 251
  /**
   * 加载指定页面的数据到内存中
   */
  RC load_page(PageNum page_num, Frame *frame);
羽飞's avatar
羽飞 已提交
252

253 254 255 256 257
  /**
   * 如果页面是脏的,就将数据刷新到磁盘
   */
  RC flush_page_internal(Frame &frame);

羽飞's avatar
羽飞 已提交
258
private:
259 260
  BufferPoolManager &  bp_manager_;
  BPFrameManager &     frame_manager_;
羽飞's avatar
羽飞 已提交
261

262 263 264 265 266 267 268
  std::string          file_name_;
  int                  file_desc_ = -1;
  Frame *              hdr_frame_ = nullptr;
  BPFileHeader *       file_header_ = nullptr;
  std::set<PageNum>    disposed_pages_;

  common::Mutex        lock_;
羽飞's avatar
羽飞 已提交
269 270
private:
  friend class BufferPoolIterator;
羽飞's avatar
羽飞 已提交
271 272
};

羽飞's avatar
羽飞 已提交
273 274 275 276
/**
 * @brief BufferPool的管理类
 * @ingroup BufferPool
 */
277 278
class BufferPoolManager 
{
羽飞's avatar
羽飞 已提交
279
public:
280
  BufferPoolManager(int memory_size = 0);
羽飞's avatar
羽飞 已提交
281 282 283 284 285 286 287 288 289
  ~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:
290
  static void set_instance(BufferPoolManager *bpm); // TODO 优化全局变量的表示方法
羽飞's avatar
羽飞 已提交
291
  static BufferPoolManager &instance();
L
Longda Feng 已提交
292

羽飞's avatar
羽飞 已提交
293 294
private:
  BPFrameManager frame_manager_{"BufPool"};
295 296

  common::Mutex  lock_;
羽飞's avatar
羽飞 已提交
297 298 299
  std::unordered_map<std::string, DiskBufferPool *> buffer_pools_;
  std::unordered_map<int, DiskBufferPool *> fd_buffer_pools_;
};