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

#include "rc.h"
羽飞's avatar
羽飞 已提交
29
#include "defs.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 40 41

#define BP_FILE_SUB_HDR_SIZE (sizeof(BPFileSubHeader))

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

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

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

63 64
class BPFrameManager 
{
羽飞's avatar
羽飞 已提交
65
public:
羽飞's avatar
羽飞 已提交
66
  BPFrameManager(const char *tag);
羽飞's avatar
羽飞 已提交
67

羽飞's avatar
羽飞 已提交
68 69 70
  RC init(int pool_num);
  RC cleanup();

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

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

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

L
Longda Feng 已提交
92 93 94 95
  size_t frame_num() const
  {
    return frames_.count();
  }
羽飞's avatar
羽飞 已提交
96 97 98 99

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

105 106 107 108
private:
  Frame *get_internal(const FrameId &frame_id);
  RC     free_internal(const FrameId &frame_id, Frame *frame);

羽飞's avatar
羽飞 已提交
109 110 111
private:
  class BPFrameIdHasher {
  public:
112
    size_t operator()(const FrameId &frame_id) const
L
Longda Feng 已提交
113
    {
羽飞's avatar
羽飞 已提交
114 115 116
      return frame_id.hash();
    }
  };
117 118

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

  std::mutex lock_;
122
  FrameLruCache  frames_;
羽飞's avatar
羽飞 已提交
123
  FrameAllocator allocator_;
L
Longda 已提交
124
};
羽飞's avatar
羽飞 已提交
125

126
class BufferPoolIterator
127
{
羽飞's avatar
羽飞 已提交
128 129 130 131 132 133 134 135
public:
  BufferPoolIterator();
  ~BufferPoolIterator();

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

羽飞's avatar
羽飞 已提交
137
private:
L
Longda Feng 已提交
138 139
  common::Bitmap bitmap_;
  PageNum current_page_num_ = -1;
羽飞's avatar
羽飞 已提交
140 141
};

142 143
class DiskBufferPool 
{
L
Longda 已提交
144
public:
羽飞's avatar
羽飞 已提交
145
  DiskBufferPool(BufferPoolManager &bp_manager, BPFrameManager &frame_manager);
L
Longda 已提交
146
  ~DiskBufferPool();
羽飞's avatar
羽飞 已提交
147 148

  /**
L
Longda 已提交
149 150
   * 创建一个名称为指定文件名的分页文件
   */
羽飞's avatar
羽飞 已提交
151 152 153
  RC create_file(const char *file_name);

  /**
羽飞's avatar
羽飞 已提交
154
   * 根据文件名打开一个分页文件
羽飞's avatar
羽飞 已提交
155
   */
羽飞's avatar
羽飞 已提交
156
  RC open_file(const char *file_name);
羽飞's avatar
羽飞 已提交
157 158

  /**
羽飞's avatar
羽飞 已提交
159
   * 关闭分页文件
羽飞's avatar
羽飞 已提交
160
   */
羽飞's avatar
羽飞 已提交
161
  RC close_file();
羽飞's avatar
羽飞 已提交
162 163 164 165

  /**
   * 根据文件ID和页号获取指定页面到缓冲区,返回页面句柄指针。
   */
羽飞's avatar
羽飞 已提交
166
  RC get_this_page(PageNum page_num, Frame **frame);
羽飞's avatar
羽飞 已提交
167 168 169 170 171 172

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

175 176 177 178 179
  /**
   * @brief 释放某个页面,将此页面设置为未分配状态
   * 
   * @param page_num 待释放的页面
   */
羽飞's avatar
羽飞 已提交
180
  RC dispose_page(PageNum page_num);
羽飞's avatar
羽飞 已提交
181 182

  /**
183 184
   * @brief 释放指定文件关联的页的内存
   * 如果已经脏, 则刷到磁盘,除了pinned page
羽飞's avatar
羽飞 已提交
185
   */
羽飞's avatar
羽飞 已提交
186 187
  RC purge_page(PageNum page_num);
  RC purge_all_pages();
羽飞's avatar
羽飞 已提交
188 189

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

羽飞's avatar
羽飞 已提交
198 199 200 201
  /**
   * 检查是否所有页面都是pin count == 0状态(除了第1个页面)
   * 调试使用
   */
羽飞's avatar
羽飞 已提交
202
  RC check_all_pages_unpinned();
羽飞's avatar
羽飞 已提交
203

羽飞's avatar
羽飞 已提交
204
  int file_desc() const;
羽飞's avatar
羽飞 已提交
205

羽飞's avatar
羽飞 已提交
206 207 208 209
  /**
   * 如果页面是脏的,就将数据刷新到磁盘
   */
  RC flush_page(Frame &frame);
羽飞's avatar
羽飞 已提交
210

羽飞's avatar
羽飞 已提交
211 212 213 214 215
  /**
   * 刷新所有页面到磁盘,即使pin count不是0
   */
  RC flush_all_pages();

羽飞's avatar
羽飞 已提交
216 217 218 219
  /**
   * 回放日志时处理page0中已被认定为不存在的page
   */
  RC recover_page(PageNum page_num);
L
Longda Feng 已提交
220

羽飞's avatar
羽飞 已提交
221
protected:
羽飞's avatar
羽飞 已提交
222
  RC allocate_frame(PageNum page_num, Frame **buf);
羽飞's avatar
羽飞 已提交
223 224

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

羽飞's avatar
羽飞 已提交
230 231 232 233
  /**
   * 加载指定页面的数据到内存中
   */
  RC load_page(PageNum page_num, Frame *frame);
羽飞's avatar
羽飞 已提交
234

235 236 237 238 239
  /**
   * 如果页面是脏的,就将数据刷新到磁盘
   */
  RC flush_page_internal(Frame &frame);

羽飞's avatar
羽飞 已提交
240
private:
241 242
  BufferPoolManager &  bp_manager_;
  BPFrameManager &     frame_manager_;
羽飞's avatar
羽飞 已提交
243

244 245 246 247 248 249 250
  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
羽飞 已提交
251 252
private:
  friend class BufferPoolIterator;
羽飞's avatar
羽飞 已提交
253 254
};

255 256
class BufferPoolManager 
{
羽飞's avatar
羽飞 已提交
257
public:
258
  BufferPoolManager(int memory_size = 0);
羽飞's avatar
羽飞 已提交
259 260 261 262 263 264 265 266 267
  ~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:
268
  static void set_instance(BufferPoolManager *bpm); // TODO 优化全局变量的表示方法
羽飞's avatar
羽飞 已提交
269
  static BufferPoolManager &instance();
L
Longda Feng 已提交
270

羽飞's avatar
羽飞 已提交
271 272
private:
  BPFrameManager frame_manager_{"BufPool"};
273 274

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