disk_buffer_pool.h 8.7 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
 * @details
 * @code
羽飞's avatar
羽飞 已提交
52 53 54 55
 * TODO 1. 当前的做法,只能分配比较少的页面,你可以扩展一下,支持更多的页面或无限多的页面吗?
 *         可以参考Linux ext(n)和Windows NTFS等文件系统
 *      2. 当前使用bitmap存放页面分配情况,但是这种方法在页面非常多的时候,查找空闲页面的
 *         效率非常低,你有办法优化吗?
羽飞's avatar
羽飞 已提交
56
 * @endcode
羽飞's avatar
羽飞 已提交
57
 */
58 59
struct BPFileHeader 
{
L
Longda Feng 已提交
60 61 62
  int32_t page_count;       //! 当前文件一共有多少个页面
  int32_t allocated_pages;  //! 已经分配了多少个页面
  char bitmap[0];           //! 页面分配位图, 第0个页面(就是当前页面),总是1
羽飞's avatar
羽飞 已提交
63 64 65 66 67

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

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

羽飞's avatar
羽飞 已提交
72 73 74
/**
 * @brief 管理页面Frame
 * @ingroup BufferPool
羽飞's avatar
羽飞 已提交
75 76 77 78
 * @details 管理内存中的页帧。内存是有限的,内存中能够存放的页帧个数也是有限的。
 * 当内存中的页帧不够用时,需要从内存中淘汰一些页帧,以便为新的页帧腾出空间。
 * 这个管理器负责为所有的BufferPool提供页帧管理服务,也就是所有的BufferPool磁盘文件
 * 在访问时都使用这个管理器映射到内存。
羽飞's avatar
羽飞 已提交
79
 */
80 81
class BPFrameManager 
{
羽飞's avatar
羽飞 已提交
82
public:
羽飞's avatar
羽飞 已提交
83
  BPFrameManager(const char *tag);
羽飞's avatar
羽飞 已提交
84

羽飞's avatar
羽飞 已提交
85 86 87
  RC init(int pool_num);
  RC cleanup();

羽飞's avatar
羽飞 已提交
88 89 90 91 92 93 94
  /**
   * @brief 获取指定的页面
   * 
   * @param file_desc 文件描述符,也可以当做buffer pool文件的标识
   * @param page_num  页面号
   * @return Frame* 页帧指针
   */
L
Longda 已提交
95
  Frame *get(int file_desc, PageNum page_num);
羽飞's avatar
羽飞 已提交
96

羽飞's avatar
羽飞 已提交
97 98 99 100 101 102
  /**
   * @brief 列出所有指定文件的页面
   * 
   * @param file_desc 文件描述符
   * @return std::list<Frame *> 页帧列表
   */
L
Longda 已提交
103 104
  std::list<Frame *> find_list(int file_desc);

羽飞's avatar
羽飞 已提交
105 106 107 108 109 110 111
  /**
   * @brief 分配一个新的页面
   * 
   * @param file_desc 文件描述符
   * @param page_num 页面编号
   * @return Frame* 页帧指针
   */
羽飞's avatar
羽飞 已提交
112 113 114 115 116 117 118 119
  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
羽飞 已提交
120 121
  /**
   * 如果不能从空闲链表中分配新的页面,就使用这个接口,
122 123 124 125
   * 尝试从pin count=0的页面中淘汰一些
   * @param count 想要purge多少个页面
   * @param purger 需要在释放frame之前,对页面做些什么操作。当前是刷新脏数据到磁盘
   * @return 返回本次清理了多少个页面
羽飞's avatar
羽飞 已提交
126
   */
127
  int purge_frames(int count, std::function<RC(Frame *frame)> purger);
羽飞's avatar
羽飞 已提交
128

L
Longda Feng 已提交
129 130 131 132
  size_t frame_num() const
  {
    return frames_.count();
  }
羽飞's avatar
羽飞 已提交
133 134 135 136

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

142 143 144 145
private:
  Frame *get_internal(const FrameId &frame_id);
  RC     free_internal(const FrameId &frame_id, Frame *frame);

羽飞's avatar
羽飞 已提交
146 147 148
private:
  class BPFrameIdHasher {
  public:
149
    size_t operator()(const FrameId &frame_id) const
L
Longda Feng 已提交
150
    {
羽飞's avatar
羽飞 已提交
151 152 153
      return frame_id.hash();
    }
  };
154 155

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

  std::mutex lock_;
159
  FrameLruCache  frames_;
羽飞's avatar
羽飞 已提交
160
  FrameAllocator allocator_;
L
Longda 已提交
161
};
羽飞's avatar
羽飞 已提交
162

羽飞's avatar
羽飞 已提交
163 164 165 166
/**
 * @brief 用于遍历BufferPool中的所有页面
 * @ingroup BufferPool
 */
167
class BufferPoolIterator
168
{
羽飞's avatar
羽飞 已提交
169 170 171 172 173 174 175 176
public:
  BufferPoolIterator();
  ~BufferPoolIterator();

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

羽飞's avatar
羽飞 已提交
178
private:
L
Longda Feng 已提交
179 180
  common::Bitmap bitmap_;
  PageNum current_page_num_ = -1;
羽飞's avatar
羽飞 已提交
181 182
};

羽飞's avatar
羽飞 已提交
183 184 185 186
/**
 * @brief BufferPool的实现
 * @ingroup BufferPool
 */
187 188
class DiskBufferPool 
{
L
Longda 已提交
189
public:
羽飞's avatar
羽飞 已提交
190
  DiskBufferPool(BufferPoolManager &bp_manager, BPFrameManager &frame_manager);
L
Longda 已提交
191
  ~DiskBufferPool();
羽飞's avatar
羽飞 已提交
192 193

  /**
L
Longda 已提交
194 195
   * 创建一个名称为指定文件名的分页文件
   */
羽飞's avatar
羽飞 已提交
196 197 198
  RC create_file(const char *file_name);

  /**
羽飞's avatar
羽飞 已提交
199
   * 根据文件名打开一个分页文件
羽飞's avatar
羽飞 已提交
200
   */
羽飞's avatar
羽飞 已提交
201
  RC open_file(const char *file_name);
羽飞's avatar
羽飞 已提交
202 203

  /**
羽飞's avatar
羽飞 已提交
204
   * 关闭分页文件
羽飞's avatar
羽飞 已提交
205
   */
羽飞's avatar
羽飞 已提交
206
  RC close_file();
羽飞's avatar
羽飞 已提交
207 208 209 210

  /**
   * 根据文件ID和页号获取指定页面到缓冲区,返回页面句柄指针。
   */
羽飞's avatar
羽飞 已提交
211
  RC get_this_page(PageNum page_num, Frame **frame);
羽飞's avatar
羽飞 已提交
212 213 214 215 216 217

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

220 221 222 223 224
  /**
   * @brief 释放某个页面,将此页面设置为未分配状态
   * 
   * @param page_num 待释放的页面
   */
羽飞's avatar
羽飞 已提交
225
  RC dispose_page(PageNum page_num);
羽飞's avatar
羽飞 已提交
226 227

  /**
228 229
   * @brief 释放指定文件关联的页的内存
   * 如果已经脏, 则刷到磁盘,除了pinned page
羽飞's avatar
羽飞 已提交
230
   */
羽飞's avatar
羽飞 已提交
231 232
  RC purge_page(PageNum page_num);
  RC purge_all_pages();
羽飞's avatar
羽飞 已提交
233 234

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

羽飞's avatar
羽飞 已提交
243 244 245 246
  /**
   * 检查是否所有页面都是pin count == 0状态(除了第1个页面)
   * 调试使用
   */
羽飞's avatar
羽飞 已提交
247
  RC check_all_pages_unpinned();
羽飞's avatar
羽飞 已提交
248

羽飞's avatar
羽飞 已提交
249
  int file_desc() const;
羽飞's avatar
羽飞 已提交
250

羽飞's avatar
羽飞 已提交
251 252 253 254
  /**
   * 如果页面是脏的,就将数据刷新到磁盘
   */
  RC flush_page(Frame &frame);
羽飞's avatar
羽飞 已提交
255

羽飞's avatar
羽飞 已提交
256 257 258 259 260
  /**
   * 刷新所有页面到磁盘,即使pin count不是0
   */
  RC flush_all_pages();

羽飞's avatar
羽飞 已提交
261 262 263 264
  /**
   * 回放日志时处理page0中已被认定为不存在的page
   */
  RC recover_page(PageNum page_num);
L
Longda Feng 已提交
265

羽飞's avatar
羽飞 已提交
266
protected:
羽飞's avatar
羽飞 已提交
267
  RC allocate_frame(PageNum page_num, Frame **buf);
羽飞's avatar
羽飞 已提交
268 269

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

羽飞's avatar
羽飞 已提交
275 276 277 278
  /**
   * 加载指定页面的数据到内存中
   */
  RC load_page(PageNum page_num, Frame *frame);
羽飞's avatar
羽飞 已提交
279

280 281 282 283 284
  /**
   * 如果页面是脏的,就将数据刷新到磁盘
   */
  RC flush_page_internal(Frame &frame);

羽飞's avatar
羽飞 已提交
285
private:
286 287
  BufferPoolManager &  bp_manager_;
  BPFrameManager &     frame_manager_;
羽飞's avatar
羽飞 已提交
288

289 290 291 292 293 294 295
  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
羽飞 已提交
296 297
private:
  friend class BufferPoolIterator;
羽飞's avatar
羽飞 已提交
298 299
};

羽飞's avatar
羽飞 已提交
300 301 302 303
/**
 * @brief BufferPool的管理类
 * @ingroup BufferPool
 */
304 305
class BufferPoolManager 
{
羽飞's avatar
羽飞 已提交
306
public:
307
  BufferPoolManager(int memory_size = 0);
羽飞's avatar
羽飞 已提交
308 309 310 311 312 313 314 315 316
  ~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:
317
  static void set_instance(BufferPoolManager *bpm); // TODO 优化全局变量的表示方法
羽飞's avatar
羽飞 已提交
318
  static BufferPoolManager &instance();
L
Longda Feng 已提交
319

羽飞's avatar
羽飞 已提交
320 321
private:
  BPFrameManager frame_manager_{"BufPool"};
322 323

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