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
};
L
Longda 已提交
60

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

羽飞's avatar
羽飞 已提交
173
  RC dispose_page(PageNum page_num);
羽飞's avatar
羽飞 已提交
174 175 176 177

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

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

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

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

羽飞's avatar
羽飞 已提交
200
  int file_desc() const;
羽飞's avatar
羽飞 已提交
201

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

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

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

羽飞's avatar
羽飞 已提交
217
protected:
羽飞's avatar
羽飞 已提交
218
  RC allocate_frame(PageNum page_num, Frame **buf);
羽飞's avatar
羽飞 已提交
219 220

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

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

231 232 233 234 235
  /**
   * 如果页面是脏的,就将数据刷新到磁盘
   */
  RC flush_page_internal(Frame &frame);

羽飞's avatar
羽飞 已提交
236
private:
237 238
  BufferPoolManager &  bp_manager_;
  BPFrameManager &     frame_manager_;
羽飞's avatar
羽飞 已提交
239

240 241 242 243 244 245 246
  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
羽飞 已提交
247 248
private:
  friend class BufferPoolIterator;
羽飞's avatar
羽飞 已提交
249 250
};

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

羽飞's avatar
羽飞 已提交
267 268
private:
  BPFrameManager frame_manager_{"BufPool"};
269 270

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