简单数据库实现——Part5 - 持久存储到磁盘

简单数据库实现——Part5 - 持久存储到磁盘目前的数据库在终止程序重新启动后会丢失所有记录,所以我们会通过将整个数据库保存到一个文件来保存记录。我们已经通过将行序列化到页面大小的内存快中,为了增加持久性,我们可以简单的将内存块写入文件并在下次程序启动时读入内存。为了简化这一过程,我们将进行一个称为寻呼机(pager)的抽象。我们向寻呼机询问页码x,寻呼机返回给我们一块内存。它会首先查看他...
摘要由CSDN通过智能技术生成

简单数据库实现——Part5 - 持久存储到磁盘

目前的数据库在终止程序重新启动后会丢失所有记录,所以我们会通过将整个数据库保存到一个文件来保存记录。

我们已经通过将行序列化到页面大小的内存快中,为了增加持久性,我们可以简单的将内存块写入文件并在下次程序启动时读入内存。

为了简化这一过程,我们将进行一个称为寻呼机(pager)的抽象。我们向寻呼机询问页码x,寻呼机返回给我们一块内存。它会首先查看他的缓存(cache),如果缓存中不存在,那么它会从磁盘(disk)中将数据复制到内存中。
architecture

寻呼机(pager)访问页面缓存和文件,表对象通过寻呼机请求页面:

+typedef struct {
   
+  int file_descriptor;
+  uint32_t file_length;
+  void* pages[TABLE_MAX_PAGES];
+} Pager;
+
 typedef struct {
   
-  void* pages[TABLE_MAX_PAGES];
+  Pager* pager;
   uint32_t num_rows;
 } Table;

new_table() 重命名为 db_open() ,因为它现在的作用是连接数据库:

  • 打开数据库文件
  • 初始化寻呼机(pager)数据结构
  • 初始化表(table)数据结构
-Table* new_table() {
   
+Table* db_open(const char* filename) {
   
+  Pager* pager = pager_open(filename);
+  uint32_t num_rows = pager->file_length / ROW_SIZE;
+
   Table* table = malloc(sizeof(Table));
-  table->num_rows = 0;
+  table->pager = pager;
+  table->num_rows = num_rows;

   return table;
 }

db_open() 依次调用 paper_open(),这将打开数据文件并跟踪其大小。它还将页面缓存初始化为 NULL

函数的使用:
open()函数(APUE 3.3和4.5节):

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int oflag,.../*, mode_t mode */);
/* 返回:若成功为文件描述符若出错为-1 */

pathname是要打开或创建的文件的名字。oflag参数可用来说明此函数的多个选择项。

  • O_RDWR 读、写打开
  • O_CREAT 若此文件不存在则创建它。使用此选择项时,需同时说明第三个参数mode,用其说明该新文件的存取许可权位。
  • S_IWUSR 用户-写
  • S_IRUSR 用户-读

lseek()函数(APUE 3.6节):

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int filedes, off_t offset, int whence);
/*返回:若成功为新的文件位移若出错为-1 */

对参数offset的解释与参数whence的值有关。

  • whenceSEEK_END,则将该文件的位移量设置为文件长度加offsetoffset可为正或负。
    lseek成功执行,则返回新的文件位移量。
+Pager* pager_open(const char* filename) {
   
+  int fd = open(filename,
+                O_RDWR |      // Read/Write mode
+                    O_CREAT,  // Create file if it does not exist
+                S_IWUSR |     // User write permission
+                    S_IRUSR   // User read permission
+                );
+
+  if (fd == -1) {
   
+    printf("Unable to open file\n");
+    exit(EXIT_FAILURE);
+  }
+
+  off_t file_length = lseek(fd, 0, SEEK_END);
+
+  Pager* pager = malloc(sizeof(Pager));
+  pager->file_descriptor = fd;
+  pager->file_length = file_length;
+
+  for (uint32_t i = 0; i < TABLE_MAX_PAGES; i++) {
   
+    pager->pages[i] = NULL;
+  }
+
+  return pager;
+}

我们现在将获取页面移动到它自己的方法 get_page() 中。

 void* row_slot(Table* table, uint32_t row_num) {
   
   uint32_t page_num = row_num / ROWS_PER_PAGE;
-  void* page = table->pages[page_num];
-  if (page == NULL) {
   
-    // Allocate memory only when we try to access page
-    page = table->pages[page_num] = malloc(PAGE_SIZE);
-  }
+  void* page = get_page(table->pager, page_num);
   uint32_t row_offset = row_num % ROWS_PER_PAGE;
   uint32_t byte_offset = row_offset * ROW_SIZE;
   return page + byte_offset;
 }

get_page()方法可以处理缓存丢失。我们假设页面一个接一个保存在数据库文件中:第0页位于偏移量0,第1页位于偏移量4096,第2页位于偏移量8192,等等。如果请求的页面位于文件的边界之外,我们知道它应该是空的,所以我们只需分配一些内存并返回它。当我们稍后将缓存更新到磁盘时,该页面将被添加到文件中。

+void* get_page(Pager* pager, uint32_t page_num) {
   
+  if (page_num > TABLE_MAX_PAGES) {
   
+    printf("Tried to fetch page number out of bounds. %d > %d\n", page_num,
+           TABLE_MAX_PAGES);
+    exit(EXIT_FAILURE);
+  }
+
+  if (pager->pages[page_num] == NULL) {
   
+    // Cache miss. Allocate memory and load from file.
+    void* page = malloc(PAGE_SIZE);
+    uint32_t num_pages = pager->file_length / PAGE_SIZE;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值