disk_buffer_pool.h 6.2 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 14 15 16
//
#ifndef __OBSERVER_STORAGE_COMMON_PAGE_MANAGER_H_
#define __OBSERVER_STORAGE_COMMON_PAGE_MANAGER_H_

羽飞's avatar
羽飞 已提交
17 18
#include <sys/types.h>
#include <sys/stat.h>
羽飞's avatar
羽飞 已提交
19 20
#include <fcntl.h>
#include <stdio.h>
L
Longda 已提交
21
#include <stdlib.h>
羽飞's avatar
羽飞 已提交
22 23
#include <string.h>
#include <time.h>
羽飞's avatar
羽飞 已提交
24 25
#include <string>
#include <unordered_map>
羽飞's avatar
羽飞 已提交
26 27

#include "rc.h"
L
Longda 已提交
28
#include "common/mm/mem_pool.h"
羽飞's avatar
羽飞 已提交
29

羽飞's avatar
羽飞 已提交
30 31 32 33
typedef int32_t PageNum;

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

//
#define BP_INVALID_PAGE_NUM (-1)
L
Longda 已提交
37
#define BP_PAGE_SIZE (1 << 13)
羽飞's avatar
羽飞 已提交
38 39
#define BP_PAGE_DATA_SIZE (BP_PAGE_SIZE - sizeof(PageNum))
#define BP_FILE_SUB_HDR_SIZE (sizeof(BPFileSubHeader))
L
Longda 已提交
40
#define BP_BUFFER_SIZE 256
羽飞's avatar
羽飞 已提交
41

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

羽飞's avatar
羽飞 已提交
61 62 63 64
class Frame
{
public:
  void clear_page()
L
Longda 已提交
65
  {
羽飞's avatar
羽飞 已提交
66
    memset(&page_, 0, sizeof(page_));
L
Longda 已提交
67
  }
羽飞's avatar
羽飞 已提交
68

羽飞's avatar
羽飞 已提交
69 70 71 72
  PageNum page_num() const
  {
    return page_.page_num;
  }
L
Longda 已提交
73

羽飞's avatar
羽飞 已提交
74 75 76
  void set_page_num(PageNum page_num)
  {
    page_.page_num = page_num;
羽飞's avatar
羽飞 已提交
77
  }
羽飞's avatar
羽飞 已提交
78 79 80 81 82

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

  char *data() {
羽飞's avatar
羽飞 已提交
88
    return page_.data;
羽飞's avatar
羽飞 已提交
89
  }
羽飞's avatar
羽飞 已提交
90

羽飞's avatar
羽飞 已提交
91 92 93 94
  int file_desc() const
  {
    return file_desc_;
  }
羽飞's avatar
羽飞 已提交
95

羽飞's avatar
羽飞 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
  void set_file_desc(int fd)
  {
    file_desc_ = fd;
  }
  bool can_purge()
  {
    return pin_count_ <= 0;
  }
private:
  friend class DiskBufferPool;

  bool          dirty_     = false;
  unsigned int  pin_count_ = 0;
  unsigned long acc_time_  = 0;
  int           file_desc_ = -1;
  Page          page_;
L
Longda 已提交
112
};
羽飞's avatar
羽飞 已提交
113

羽飞's avatar
羽飞 已提交
114
class BPFrameManager : public common::MemPoolSimple<Frame> {
羽飞's avatar
羽飞 已提交
115
public:
羽飞's avatar
羽飞 已提交
116
  BPFrameManager(const char *tag);
羽飞's avatar
羽飞 已提交
117

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

L
Longda 已提交
120 121 122 123
  std::list<Frame *> find_list(int file_desc);

  Frame *begin_purge();
};
羽飞's avatar
羽飞 已提交
124

L
Longda 已提交
125 126
class DiskBufferPool {
public:
羽飞's avatar
羽飞 已提交
127
  DiskBufferPool(BufferPoolManager &bp_manager, BPFrameManager &frame_manager);
L
Longda 已提交
128
  ~DiskBufferPool();
羽飞's avatar
羽飞 已提交
129 130

  /**
L
Longda 已提交
131 132
   * 创建一个名称为指定文件名的分页文件
   */
羽飞's avatar
羽飞 已提交
133 134 135 136 137 138
  RC create_file(const char *file_name);

  /**
   * 根据文件名打开一个分页文件,返回文件ID
   * @return
   */
羽飞's avatar
羽飞 已提交
139
  RC open_file(const char *file_name);
羽飞's avatar
羽飞 已提交
140 141

  /**
羽飞's avatar
羽飞 已提交
142
   * 关闭分页文件
羽飞's avatar
羽飞 已提交
143
   */
羽飞's avatar
羽飞 已提交
144
  RC close_file();
羽飞's avatar
羽飞 已提交
145 146 147 148 149

  /**
   * 根据文件ID和页号获取指定页面到缓冲区,返回页面句柄指针。
   * @return
   */
羽飞's avatar
羽飞 已提交
150
  RC get_this_page(PageNum page_num, Frame **frame);
羽飞's avatar
羽飞 已提交
151 152 153 154 155 156

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

  /**
L
Longda 已提交
160
   * 比purge_page多一个动作, 在磁盘上将对应的页数据删掉。
羽飞's avatar
羽飞 已提交
161
   */
羽飞's avatar
羽飞 已提交
162
  RC dispose_page(PageNum page_num);
羽飞's avatar
羽飞 已提交
163 164 165 166

  /**
   * 释放指定文件关联的页的内存, 如果已经脏, 则刷到磁盘,除了pinned page
   */
羽飞's avatar
羽飞 已提交
167 168
  RC purge_page(PageNum page_num);
  RC purge_all_pages();
羽飞's avatar
羽飞 已提交
169 170 171 172 173 174 175

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

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

  RC check_all_pages_unpinned();
羽飞's avatar
羽飞 已提交
184

羽飞's avatar
羽飞 已提交
185
  int file_desc() const;
羽飞's avatar
羽飞 已提交
186

羽飞's avatar
羽飞 已提交
187 188 189 190
  /**
   * 如果页面是脏的,就将数据刷新到磁盘
   */
  RC flush_page(Frame &frame);
羽飞's avatar
羽飞 已提交
191

羽飞's avatar
羽飞 已提交
192
protected:
羽飞's avatar
羽飞 已提交
193
  RC allocate_frame(Frame **buf);
羽飞's avatar
羽飞 已提交
194 195

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

羽飞's avatar
羽飞 已提交
201 202 203 204
  /**
   * 加载指定页面的数据到内存中
   */
  RC load_page(PageNum page_num, Frame *frame);
羽飞's avatar
羽飞 已提交
205 206

private:
羽飞's avatar
羽飞 已提交
207 208 209 210 211 212 213
  BufferPoolManager &bp_manager_;
  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
羽飞 已提交
214 215
};

羽飞's avatar
羽飞 已提交
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
class BufferPoolManager
{
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();
  
private:
  BPFrameManager frame_manager_{"BufPool"};
  std::unordered_map<std::string, DiskBufferPool *> buffer_pools_;
  std::unordered_map<int, DiskBufferPool *> fd_buffer_pools_;
};
羽飞's avatar
羽飞 已提交
237

L
Longda 已提交
238
#endif  //__OBSERVER_STORAGE_COMMON_PAGE_MANAGER_H_