为什么SQLite存储数百个空字节?



在我正在创建的数据库中,我很好奇为什么大小比内容大得多,并检查了十六进制代码。在 4 kB 文件(单行作为测试)中,有两个大约为 900 和 1000 字节的主要块,以及几个较小的块,它们都是空字节0x0

我想不出任何合乎逻辑的理由,存储数千个空字节是有利的,从而显着增加数据库的大小。

有人可以向我解释一下吗?我试过搜索,但找不到任何东西。

SQLite 数据库文件 ('*.sqlite) 的结构在此页中描述:

https://www.sqlite.org/fileformat.html

SQLite 文件被分区为长度在 512 到 65536 字节之间的"页面" - 在您的情况下,我想页面大小可能是 1KiB。如果您存储的数据小于 1KiB(就像您的单个测试行一样,我想可能是 100 字节长?),那么还剩下 900 字节 - 并且未使用(解除分配)的空间通常在使用之前(和之后)清零。

这与计算机工作内存(RAM)的工作方式相同 - 因为RAM也使用分页。

我想您希望文件非常紧凑,具有简洁的内部表示形式;某些文件格式就是这种情况 - 例如基于OLE的老式Office文档,但其他文件格式(尤其是数据库文件)需要不同的文件布局,该文件布局同时进行了优化,以便快速访问,快速插入新数据,并且还安排以帮助防止内部碎片 - 这是以浪费空间为代价的。

一个快速的思想实验将演示为什么可变(即非只读)数据库不能使用紧凑的内部文件结构:

  1. 将单个数据库表视为CSV文件(CSV本身足够紧凑,浪费的空间很少)。
    1. 您可以通过追加到文件末尾来INSERT新行。
    2. 您可以通过简单地用零覆盖文件中的行空间来DELETE现有行。请注意,您实际上不能通过"移动"数据(例如在记事本中使用Backspace键)来"删除"空间,因为这意味着复制文件中的所有数据 - 这在很大程度上是一个坏主意。
    3. 您可以通过检查新行的宽度是否适合当前空间(并用零覆盖剩余空间)来UPDATE行,或者如果不适合,则在末尾附加新行并覆盖现有行(a-laINSERT然后DELETE
    4. )
  2. 但是,如果您有两个数据库表(具有不同的列)并且需要将它们存储在同一个文件中,该怎么办?一种方法是简单地将每个表的行混合在同一个平面文件中 - 但出于其他原因,这是一个坏主意。因此,在整个*.sqlite文件中,您可以创建"子文件",这些文件具有已知的固定大小(例如 4KiB),仅存储单个表的行,直到子文件已满;它们还存储指向下一个子文件的指针(如链接列表),该文件包含其余数据(如果有)。然后,您只需创建新的子文件,因为您需要在文件中获得更多空间并设置它们的下一个文件指针。这些子文件就是数据库文件中的"页",也是在同一父文件系统文件中包含多个读/写数据库表的方式。

然后,除了这些页面来存储表数据之外,您还需要存储索引(这允许您近乎即时地找到表行而无需扫描整个表或文件)和其他元数据,例如列定义本身 - 并且通常它们也存储在页面中。关系(表格)数据库文件本身可以被视为文件系统(只是封装在父文件系统中...这可能在*.vhd文件中...可以埋在varbinary数据库列中...在另一个文件系统中),甚至数据库系统本身也被比作操作系统(因为它们为程序(存储过程)提供了一个运行环境,它们提供 IO 服务等等 - 如果你看看 1970 年代基于 COBOL 的旧大型机,它几乎是循环的,当时所有的 IO 操作都仅限于计算机记录管理操作(插入, 更新、删除)。

最新更新