关注「索引目录」公众号,获取更多干货。
当 SQLite 将一行数据存储到 B+ 树表中时,它并非以某种简单的布局逐列写入,而是构建一个精心设计的字节序列。
表格记录格式
表格记录(行数据)由两个逻辑部分组成:
- 标题
- 记录映像(数据部分)
头部以大小字段开头。该值告诉我们第一个数据项之前有多少字节Data1。重要的是,此大小包含用于编码大小字段本身的字节。
大小以可变长度的 64 位整数形式存储,采用 SQLite 的 varint 编码(一种霍夫曼风格的紧凑表示)。实际上,大小字段充当指向实际数据起始位置的指针。
在大小字段之后,表头包含每个列一个类型字段,顺序与语句中声明的顺序相同CREATE TABLE。SQLite 内部从不重新排列列顺序。
每个Type i字段也以可变长度的无符号整数形式存储。该值编码了相应存储类别和大小Data i。
所以标题本质上是:
[header size][type1][type2][type3]...
接下来是唱片画面:
[data1][data2][data3]...
虚拟机首先读取标头,了解要跳过多少字节才能到达数据部分,并根据列的类型代码了解如何解释每一列。
零长度数据
某些类型代码表示数据部分中占用零字节的值。
例如,类型值:
-
0 -
8 -
9 -
12 -
13
不要在记录图像中存储任何有效载荷。
这是可能的,因为类型代码本身完全决定了其值。例如:
-
NULL 不需要任何数据字节。 -
某些整数常量有特殊的编码方式。
这种设计节省空间,使档案保持紧凑。
为什么列顺序很重要
由于 SQLite 内部不会对列进行重新排序,因此记录中列的物理顺序与模式定义完全一致。
因为记录可能会溢出到其他页面,所以放置方式如下:
-
更小 -
经常访问
模式中较早出现的列可以减少追踪溢出链的需要。
这是一个非常实用的存储优化方法,但很多开发人员却忽略了它。
表键格式
SQLite 中的每个 B+ 树都必须有一个唯一的键。
关系理论认为表中不应包含重复行。但实际上,除非存在唯一约束,否则用户可能会插入相同的行。
SQLite 内部仍然需要对它们进行区分。
为此,每个表都有一个唯一的主键:
-
或由模式明确定义 -
或者由 SQLite 自动创建
如果未指定,SQLite 会分配一个名为rowid 的隐藏的 64 位有符号整数键。
rowid 保证了 B+-树中每一行的唯一性。
因此,即使两行包含相同的列值,它们的 rowid 也会不同——因此它们在 B+ 树中的键也会不同。
这个 rowid 不仅仅是一个逻辑标识符,它在物理上被用作表的 B+ 树结构中的键。
接下来会发生什么?
我们现在已经看到:
-
表记录的结构 -
标头如何编码类型和大小 -
为什么可变长度编码能保持文件紧凑 -
为什么每个表都必须有唯一的键
- 关注「索引目录」公众号
,获取更多干货。

