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

时间:2017-05-27 01:38:57

标签: database sqlite

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

我想不出有任何合理的理由存储数千个空字节,这会大大增加数据库的大小。

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

1 个答案:

答案 0 :(得分:3)

本页描述了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-la INSERT然后DELETE
  2. 但是,如果您有两个数据库表(具有不同的列)并且需要将它们存储在同一个文件中,该怎么办?一种方法是简单地将每个表的行混合在同一个平面文件中 - 但出于其他原因,这是一个坏主意。因此,在整个*.sqlite文件中,创建“子文件”,它具有已知的固定大小(例如4KiB),只存储单个表的行,直到子文件已满;它们还将指针(如链接列表)存储到包含其余数据的下一个子文件(如果有)。然后,您只需创建新的子文件,因为您需要在文件中有更多空间并设置其下一个文件指针。这些子文件是数据库文件中的“页面”,以及如何在同一父文件系统文件中包含多个读/写数据库表。
  3. 然后除了存储表数据的这些页面之外,还需要存储索引(这使得您可以在不需要扫描整个表或文件的情况下立即找到表行)和其他元数据,例如作为列定义本身 - 通常它们也存储在页面中。关系(表格)数据库文件本身可以被视为文件系统(只是封装在父文件系统中......可以在*.vhd文件中......可以隐藏在varbinary数据库中列...在另一个文件系统中),甚至数据库系统本身也已经与操作系统进行了比较(因为它们提供了运行程序(存储过程)的环境,它们提供IO服务等等 - 如果它们几乎是循环的,你看一下20世纪70年代的旧的基于COBOL的大型机,当时你的所有IO操作都只限于计算机记录管理操作(插入,更新,删除)。