我们的数据库允许插入记录并读取它们,但前提是您保持程序运行。如果您终止程序并重新启动它,则所有记录都将丢失。下面是我们想要的实现的功能:



it



与sqlite一样,我们将通过将整个数据库保存到一个文件中来保存记录。

我们已经通过将行序列化为页面大小的内存块来实现这一点。为了增加持久性,我们可以简单地将这些内存块写入文件,并在下次程序启动时将它们读入内存。

为了简化这一过程,我们将进行一个称为pager的抽象。我们向pager询问页码x,pager会返回内存块。它首先查看缓存。在缓存丢失时,它将数据从磁盘复制到内存(通过读取数据库文件)。




Android实现保存文件到文件夹中 android保存数据到文件_数据库


我们的函数与SQLite相应模块的对应关系

pager访问页面缓存和文件。Table(表)对象通过pager请求页面,下面是这两者的结构:


+


我将new_table()重命名为db_open(),因为它现在具有打开到数据库的连接的效果。打开一个连接,我的db_open()函数用处是:

  • 打开数据库文件
  • 初始化分页程序数据结构
  • 初始化表数据结构
-


db_open()反过来调用pager_open(), pager_open()打开数据库文件并跟踪其大小。它还将页面缓存初始化为所有null。


+


按照我们新的抽象,我们将获取页面的逻辑移动在它自己的方法中实现:


void


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


+


现在,我们将一直等待缓存放入到磁盘,直到用户关闭数据库的连接。当用户退出时,我们将调用一个名为db_close()的新方法 ,它能

  • 将页缓存刷新到磁盘
  • 关闭数据库文件
  • 释放分页器和表数据结构的内存
+


在我们当前的设计中,文件的长度编码对应数据库中有多少行,因此我们需要在文件的末尾编写部分页面。这就是为什么pager_flush()同时接受页码和大小。它不是最好的设计,但是当我们开始实现b树时,它会被摒弃。


+


最后,我们需要接受文件名作为命令行参数。别忘了在do_meta_command中添加额外的参数:


int


通过这些更改,我们可以关闭然后重新打开数据库,而我们的记录仍然在那里。实现了持久化存储。


~ ./db mydb.db
db > insert 1 agh hlt@qq.com
Executed.
db > insert 2 cc cc@example.com
Executed.
db > .exit
~
~ ./db mydb.db
db > select
(1, agh, hlt@qq.com)
(2, cc, cc@example.com)
Executed.
db > .exit
~


同时你也可以自己查看mydb.db是如何存储的,直接使用vim打开查看即可。