本文档描述并定义了磁盘数据库文件 此后所有SQLite版本使用的格式 3.0.0版(2004-06-18)。
1 数据库文件
SQLite数据库的完整状态通常是 包含在磁盘上称为“主数据库文件”的单个文件中。
在事务期间,SQLite存储其他信息 在名为“回滚日志”的第二个文件中,或者如果SQLite位于 WAL模式 ,一个预写日志文件。
1.1。 热门日志
如果应用程序或 主机在事务完成之前崩溃,然后回滚 日志或预写日志包含所需的信息 将主数据库文件恢复到一致状态。 回滚时 日志或预写日志包含恢复所需的信息 数据库的状态,它们被称为“热日志”或“热WAL文件”。 热日志和WAL文件只是错误恢复过程中的一个因素 场景等并不常见,但它们是SQLite状态的一部分 数据库等不能忽略。 本文件定义了格式 回滚日志和预写日志文件,但重点是 在主数据库文件上。
1.2、。 页
主数据库文件由一个或多个页面组成。 a的大小 page是介于512和65536之间的2的幂。 内的所有页面 相同的数据库大小相同。 数据库文件的页面大小 由位于偏移量的2字节整数确定 从数据库文件的开头算起16个字节。
页码从1开始。 最大页数为 4294967294 (2 32 - 2). 最小尺寸 SQLite数据库是一个512字节的页面。 数据库的最大大小为4294967294页,每个65536字节 页面或281474976579584字节(约281TB)。 通常SQLite会 达到底层文件系统或磁盘的最大文件大小限制 早在硬件达到其内部大小限制之前。
在常用情况下,SQLite数据库的大小往往在几千字节之间 到几GB,尽管已知存在太字节大小的SQLite数据库 生产中。
在任何时候,主数据库中的每个页面都有一个 使用以下选项之一:
锁定字节页面 免费列表页面
b树页面
表b-tree内部页面 表b-tree叶页 索引b树内部页面 索引b树叶页
有效负载溢出页 指针映射页
所有对主数据库文件的读取和写入都从一个页面开始 边界和所有写操作都是页面大小的整数。 读取次数 页面大小通常也是整数,只有一个例外 当第一次打开数据库时 数据库文件(数据库文件头)被读取为子页面大小单元。
在修改数据库的任何信息共享页之前, 该页面的原始未修改内容将写入 回滚日志。 如果交易中断,需要 回滚后,可以使用回滚日志来恢复 数据库恢复到其原始状态。 自由列表叶子页没有 需要在回滚时恢复的信息,因此 未在修改前写入日记账,以便 减少磁盘I/O。
数据库文件的前100个字节组成数据库文件 收割台。 数据库文件头分为多个字段,如下所示 下表。 数据库文件头中的所有多字节字段都是 以最高有效字节优先(大-中)存储。
数据库标题格式
抵消 大小 描述 0 16 标题字符串:“SQLite format 3\000” 16 2 数据库页面大小(以字节为单位)。 必须是512之间的2的幂 和32768,或表示65536页大小的值1。 18 1 文件格式写入版本。 1代表遗产; 2用于 WAL(沃尔沃) . 19 1 文件格式读取版本。 1代表遗产; 2用于 WAL(沃尔沃) . 20 1 每页末尾未使用的“保留”空间字节数。 通常为0。 21 1 最大嵌入有效载荷分数。 必须为64。 22 1 最小嵌入有效载荷分数。 必须是32岁。 23 1 叶有效载荷分数。 必须是32岁。 24 4 文件更改计数器。 28 4 数据库文件的大小(以页面为单位)。 “头数据库大小”。 32 4 第一个空闲列表主干页的页码。 36 4 空闲列表页面的总数。 40 4 架构cookie。 44 4 模式格式编号。 支持的模式格式有1、2、3和4。 48 4 默认页面缓存大小。 52 4 在auto-vacuum或 增量-真空模式,否则为零。 56 4 数据库文本编码。 值1表示UTF-8。 值为2 指UTF-16le。 值3表示UTF-16be。 60 4 由 用户版本杂注 . 64 4 增量真空模式为True(非零)。 否则为False(零)。 68 4 由设置的“应用程序ID” PRAGMA应用程序id . 72 20 保留用于扩展。 必须为零。 92 4 这个 版本-编号有效 . 96 4
SQLITE_VERSION_NUMBER(SQLITE_版本号)
每个有效的SQLite数据库文件都以以下16个字节开头 (十六进制):53 51 4c 69 74 65 20 66 f 72 6d 61 74 20 33 00。 这个字节序列 对应于UTF-8字符串“SQLite format 3”,包括nul 结尾处的终止符字符。
1.3.2. 页面大小
从偏移量16开始的双字节值确定 数据库。 对于SQLite版本3.7.0.1(2010-08-04) 更早的时候,这个值是 解释为一个大整数,必须是二的幂 512和32768(含)。 从SQLite开始 版本3.7.1 (2010-08-23),第页 支持65536字节的大小。 值65536不适合 两字节整数,因此要指定65536字节的页面大小 偏移量16为0x00 0x01。 此值可以被解释为big endian 1,被认为是一个神奇的数字,代表65536页的大小。 或者可以将双字节字段视为一个小的端号,然后说 它表示页面大小除以256。 这两个 页面大小字段的解释是等效的。
偏移量处的文件格式写入版本和文件格式读取版本 18和19用于增强文件格式 在SQLite的未来版本中。 在SQLite的当前版本中 这些值是1(用于回滚日志模式)和2(用于 WAL(沃尔沃) 日志模式。 如果SQLite的某个版本编码为当前 文件格式规范遇到一个数据库文件,其中读取 版本为1或2,但写入版本大于2,则数据库 文件必须被视为只读。 如果数据库文件具有读取版本 遇到大于2的值,则无法读取或写入该数据库。
1.3.4. 每页保留字节数
SQLite能够在 供扩展使用的每一页的末尾。 这些额外的字节是 例如,SQLite Encryption Extension使用它来存储nonce 和/或与每个页面关联的加密校验和。 这个 偏移量20处的1字节整数中的“保留空间”大小是数字 在每页末尾保留字节的空间以供扩展。 该值通常为0。 该值可以是奇数。
数据库页面的“可用大小”是由 标头中偏移量16处的2字节整数减去“保留”空间大小 记录在报头偏移量20处的1字节整数中。 可用的 页面的大小可能是一个奇数。 然而,可用大小不是 允许小于480。 换句话说,如果页面大小为512, 那么保留的空间大小不能超过32。
1.3.5. 有效载荷分数
最大和最小嵌入有效载荷分数和叶 有效载荷分数值必须为64、32和32。 这些值是 最初旨在作为可用于 修改b-tree算法的存储格式。 然而 功能不受支持,并且当前没有要添加的计划 未来的支持。 因此,这三个字节固定在 指定的值。
1.3.6. 文件更改计数器
文件更改计数器是一个4字节的大整数,位于 只要数据库文件被解锁,就会增加偏移量24 修改后。 当两个或多个进程读取同一数据库文件时,每个进程 进程可以通过监视从其他进程检测数据库更改 兑换柜台。 当 另一个进程修改了数据库,因为缓存已经过时。 文件更改计数器有助于实现这一点。
在WAL模式下,使用WAL-index检测数据库的更改 因此不需要更改计数器。 因此,更改计数器可能 在WAL模式下的每个事务上不递增。
偏移量为28的4字节大整数进入标头 将数据库文件的大小存储在页面中。 如果此在标题中 数据大小无效(请参阅下一段),则数据库 大小通过查找计算 数据库文件的实际大小。 SQLite的旧版本 忽略了标头中的数据库大小,并使用了实际文件大小 独占。 较新版本的SQLite使用头内数据库 大小(如果可用),但如果 标头中的数据库大小无效。
只有在以下情况下,标头中的数据库大小才被视为有效 它是非零的,如果4字节 更改计数器 偏移量24 与4字节完全匹配 版本-编号有效 偏移量92。 标头中的数据库大小始终有效 仅使用最新版本的SQLite修改数据库时, 版本3.7.0(2010-07-21)及更高版本。 如果SQLite的旧版本写入数据库,则不会 知道更新头内数据库大小,因此知道更新头中数据库大小 数据库大小可能不正确。 但是SQLite的传统版本 也将保持偏移量92处数字的版本有效不变 因此,它与change-counter不匹配。 因此,标题中无效 可以通过观察以下时间来检测(并忽略)数据库大小 更改计数器与对数字有效的版本不匹配。
1.3.8. 可用页面列表
数据库文件中未使用的页面存储在空闲列表中。 这个 偏移量32处的4字节大整数存储 自由列表的第一页,如果自由列表为空,则为零。 偏移量36处的4字节大整数存储总数 免费列表上的页数。
1.3.9. 架构cookie
模式cookie是偏移量为40的4字节大整数 每当数据库模式更改时,该值就会增加。 A类 根据特定版本的 数据库模式。 当数据库模式更改时,语句 必须重新准备。 运行准备好的语句时,它首先检查 模式cookie,以确保值与语句时的值相同 已准备好,如果架构cookie已更改,则语句 自动重新准备并重新运行,或使用 SQLITE_SCHEMA公司 错误。
模式格式编号是一个偏移量为44的4字节大整数。 模式格式编号与文件格式读写类似 偏移量18和19处的版本号,模式格式号除外 指的是高级SQL格式,而不是低级b树 格式化。 目前定义了四个模式格式编号:
所有版本的SQLite都能理解格式1 3.0.0版 (2004-06-18).
格式2增加了同一表中行的功能 具有不同数量的列,以便支持 ALTER表格。。。 添加列 功能。 支持 SQLite中增加了读写格式2 3.1.3版 2005年2月20日。
格式3增加了添加额外列的功能 ALTER表格。。。 添加列 具有非NULL默认值 值。 此功能是在SQLite中添加的 3.1.4版 2005年3月11日。
格式4使SQLite遵守 DESC关键字 在 索引声明。 (在的索引中忽略DESC关键字 格式1、2和3。) 格式4还添加了两个新的布尔记录类型值( 串行类型 8和9)。 SQLite 3.3.0中添加了对格式4的支持 2006-01-10.
默认情况下,SQLite创建的新数据库文件使用格式4。 这个 遗留文件格式杂注 可用于导致SQLite 使用格式1创建新的数据库文件。 可以通过以下方式将格式版本号设置为默认值1而不是4 设置 SQLITE_DEFAULT_FILE_FORMAT文件 编译时=1。
1.3.11. 建议的缓存大小
建议使用偏移量48处的4字节双字节有符号整数 数据库文件的缓存大小(以页为单位)。 价值是一个建议 只有,SQLite没有义务兑现。绝对值 整数的值用作建议的大小。 建议的缓存大小 可以使用 default_cache_size杂注 .
1.3.12. 增量真空设置
使用偏移52和64处的两个4字节大端整数 管理 自动真空 和 增量(_V) 模式。 如果 偏移量52处的整数为零,则指针映射(ptrmap)页面为 从数据库文件中省略,并且既没有autovacution也没有 支持incremental_vacuum。 如果偏移量52处的整数为 非零,则它是 数据库文件,数据库文件将包含ptrmap页面 模式必须是auto_vacuum或incremental_vacua。 在后者中 在这种情况下,对于incremental_vacuum和 对于auto_vacuum为false。 如果偏移量52处的整数为零,则 偏移量64处的整数也必须为零。
1.3.13. 文本编码
偏移量56处的4字节大整数决定编码 用于数据库中存储的所有文本字符串。 值1表示UTF-8。 值2表示UTF-16le。 值3表示UTF-16be。 不允许使用其他值。 sqlite3.h头文件将C预处理器宏SQLITE_UTF8定义为1, SQLITE_UTF16LE作为2,SQLITE_UDF16BE作为3,以代替 文本编码的数字代码。
1.3.14. 用户版本号
偏移量60处的4字节大整数是用户版本 由设置和查询 用户版本杂注 。用户版本为 SQLite未使用。
1.3.15. 应用程序ID
偏移量68处的4字节大整数是一个“应用程序ID” 可以通过 PRAGMA应用程序_id 命令以识别 属于或与特定应用程序关联的数据库。 应用程序ID用于用作 应用程序文件格式 。应用程序ID可由实用程序使用 例如 文件(1) 以确定具体 文件类型,而不仅仅是报告“SQLite3数据库”。 列表 可以通过查询 magic.txt文件 SQLite源代码存储库中的文件。
1.3.16. 写入库版本号和版本有效编号
偏移96处的4字节大端整数存储 SQLITE_VERSION_NUMBER(SQLITE_版本号) SQLite库的最大值 最近修改了数据库文件。 位于的4字节大整数 偏移量92是 更改计数器 当版本号 已存储。 偏移量92处的整数表示哪个事务 版本号对有效,有时称为 “version-valid-for-number”。
数据库文件头的所有其他字节都保留给 未来扩展,并且必须设置为零。
1.4. 锁定字节页面
锁字节页是数据库文件的单页 包含偏移量在1073741824和1073742335之间的字节, 包容的。 小于或等于1073741824字节的数据库文件 大小中不包含锁字节页。 大于的数据库文件 1073741824正好包含一个锁字节页。
锁定字节页被留出供特定操作系统使用 变频调速系统 实现数据库文件锁定原语。 SQLite不使用锁字节页。 SQLite核心 永远不会读或写锁字节页, 尽管操作系统特定 变频调速器 实现可以选择在锁字节上读取或写入字节 页面根据 基础系统的需求和倾向。 unix和win32 变频调速系统 SQLite中内置的实现不会写入 锁定字节页,但第三方VFS实现 其他操作系统可能会。
锁字节页面源于支持Win95的需要 设计此文件格式时的主要操作系统,以及 仅支持强制文件锁定。 所有现代操作系统 我们知道支持顾问文件锁定,因此lock-byte页面是 实际上不再需要,但为了向后兼容而保留。
1.5. 自由列表
数据库文件可能包含一个或多个不在 积极使用。 例如,当信息 已从数据库中删除。 未使用的页面存储在空闲列表中 和在需要其他页面时被重用。
自由列表被组织为自由列表主干页面的链接列表 每个中继页包含零个或多个空闲列表的页码 叶页面。
空闲列表主干页由一个4字节的大整数数组组成。 数组的大小是可用空间中容纳的整数数量 (共页)。 最小可用空间为480字节,因此阵列将始终 长度至少为120条。 空闲列表主干上的第一个整数 page是列表中下一个自由列表主干页的页码或零 如果这是最后一个空闲列表中继页。 空闲列表中的第二个整数 trunkpage是要遵循的叶页指针数。 调用空闲列表主干页L上的第二个整数。 如果L大于零,则数组索引介于2和之间的整数 L+1包含自由列表叶页的页码。
自由列表叶页不包含任何信息。 SQLite避免读取或 写入空闲列表叶页以减少磁盘I/O。
3.6.0(2008-07-16)之前的SQLite版本中的一个错误 导致数据库 如果自由列表主干页面中的最后6个条目中有任何一个条目被报告为损坏 数组包含非零值。 较新版本的SQLite没有 这个问题。 然而,较新版本的SQLite仍然避免使用 freelist主干页数组中的最后六个条目,按照该数据库的顺序排列 较新版本的SQLite创建的文件可以被较旧版本读取 SQLite的。
空闲列表页面的数量存储为4字节的大整数 在距离文件开头36的偏移量处。 数据库标题还存储第一个空闲列表主干的页码 页面为4字节的大整数,从开头偏移32 文件的。
1.6. B树页面
b-tree算法为密钥/数据存储提供了唯一的 面向页面的存储设备上的有序密钥。 有关b-树的背景信息,请参见 克努特, 计算机编程的艺术 ,第3卷“分类 和搜索”,第471-479页。 b树的两个变体由 苏莱特。 “表b树”使用64位有符号整数密钥并存储 叶子中的所有数据。 “索引b树”使用任意键,不存储 数据。
b-tree页面是内部页面或叶页面。 叶页包含键,如果是表b树,则每个键都包含 键具有关联的数据。 内部页面包含 K键和指向子b树页面的K+1指针。 内部b-tree页面中的“指针”只是32位 子页的无符号整数页码。
内部b树页面上的键数K, 几乎总是至少为2,通常远大于2。 唯一的例外是当页面1是内部b-tree页面时。 第1页的可用存储空间减少了100字节, 由于该页面开头存在数据库标头, 因此,有时(很少)如果页面1是内部b-tree页面,它可以 最后只拿着一把钥匙。 在所有其他情况下,K等于或大于2。 K上的上限是页面上容纳的所有键。 大钥匙 关于索引b树被拆分为 溢出页面 这样就没有一把钥匙了 使用页面上四分之一以上的可用存储空间 因此,每个内部页面能够存储至少4个密钥。 表b-树的整数键永远不会大到 需要溢出,因此键溢出仅发生在索引b树上。
定义深度 叶b-树的深度为1,任何内部b-树深度为1 超过其任何子级的最大深度。 以一种良好的方式 数据库中,内部b树的所有子级都具有相同的深度。
在内部b-tree页面中,指针和键在逻辑上是交替的 两端都有指针。 (前一句话需要理解 概念上-键的实际布局和 页面中的指针更复杂,将在中进行描述 续集。) 同一页面中的所有键都是唯一的,并且在逻辑上是 按从左到右的升序组织。 (同样,此排序 是逻辑的,而不是物理的。 页面中键的实际位置 是任意的。) 对于任何键X,指向左侧的指针 X是指所有键都小于或等于X的b-tree页面。 指向X右侧的指针表示所有键所在的页面 大于X。
在内部b-tree页面中,每个键和指向它的指针 最左边的组合成一个称为“cell”的结构。 这个 最右边的指针是分开的。 叶子b树页面没有 指针,但它仍然使用单元格结构来保存 索引b树或表b树的键和内容。 数据也是 包含在单元格中。
每个b树页面最多有一个父b树页面。 没有父级的b-tree页面称为根页面。 根b树页面 与其子级的闭包一起形成一个完整的b树。 有一个完整的b树是可能的(事实上也很常见) 它由一个页面组成,该页面既是叶又是根。 因为有从父母到孩子的指针 如果只有根页面是已知的,则可以找到完整的b树。 因此, b树由其根页码标识。
b树页面是表b树页面或索引b树页面。 每个完整b-tree中的所有页面都属于同一类型:任一表 或索引。 数据库文件中有一个表b树 对于数据库模式中的每个rowid表,包括系统表 例如 sqlite架构 。有一个索引b树 模式中每个索引的数据库文件中,包括隐含索引 由唯一性约束创建。 没有与关联的b-树 虚拟表 。可以使用特定的虚拟表实现 属于 影子表格 用于存储,但这些影子表将有单独的 数据库架构中的条目。 不带ROWID 表使用索引b树 而不是表b树,所以有一个 在数据库文件中为每个 不带ROWID 表。 sqlite_schema表对应的b-tree始终是一个表 b-tree并始终具有1的根页。 sqlite_schema表包含每隔一个的根页码 表和索引。
表b树中的每个条目都包含一个64位有符号整数密钥 以及多达2147483647字节的任意数据。 (表b树的键 对应于 罗伊德 b树实现的SQL表的。) 内部表b树只保存指向子级的键和指针。 所有数据都包含在b-tree leaves表中。
索引b树中的每个条目都由up的任意键组成 长度为2147483647字节,无数据。
将单元格的“有效载荷”定义为任意长度的部分 单元格的。 对于索引b树,键的长度总是任意的 因此,有效载荷是关键。 没有任意长度的元素 在内部表b树页面的单元格中,因此这些单元格没有 有效载荷。 表b-树叶页面包含任意长度的内容和 因此,对于那些页面上的单元格,有效载荷就是内容。
当电池的有效负载大小超过某个阈值(至 稍后定义),然后仅为有效负载的前几个字节 存储在b-tree页面上,余额存储在链接列表中 共个内容溢出页面。
b-tree页面按以下顺序划分为多个区域:
100字节的数据库文件头(仅在第1页上找到) 8或12字节b树页眉 单元格指针数组 未分配的空间 单元格内容区域 保留区域。
100字节的数据库文件头只能在第1页上找到,即 总是一个表b树页面。 数据库文件中的所有其他b-tree页面 省略这个100字节的头。
保留区域是每个 页面(锁定页面除外),扩展可用于保持每页 信息。 保留区域的大小由一个字节决定 数据库文件头中偏移量为20的无符号整数。 保留区域的大小通常为零。
对于叶页面,b-tree页眉的大小为8个字节,而对于叶页面则为12个字节 内部页面的字节数。 页眉中的所有多字节值 都是大发动机。 b-tree页头由以下字段组成:
B树页眉格式
抵消 大小 描述 0 1 偏移量为0的单字节标志,表示b-tree页面类型。
值2(0x02)表示该页是内部索引b树页。 值5(0x05)表示该页是内部表b树页。 值10(0x0a)表示该页是叶索引b-tree页。 值13(0x0d)表示该页是叶表b-tree页。 b-tree页面类型的任何其他值都是错误的。 1 2 偏移量1处的两字节整数给出 页面上的第一个自由块,如果没有自由块,则为零。 三 2 偏移量3处的两字节整数表示页面上的单元格数。 5 2 偏移量5处的两字节整数指定单元格内容的开始 区域。 此整数的零值被解释为65536。 7 1 偏移量7处的单字节整数给出了空闲碎片数 单元格内容区域中的字节。 8 4 偏移量8处的四字节页码是最右边的指针。 这个 值仅出现在内部b-tree页面的标题中,从 所有其他页面。
b树页面的单元格指针数组紧跟在b树之后 页眉。 设K是btree上的单元格数。 单元格指针 数组由到单元格内容的K个2字节整数偏移量组成。 这个 单元格指针按键顺序排列,最左边的单元格(具有 最小键)第一个和最右边的单元格(最大的单元格 键)最后。
单元格内容存储在b树页面的单元格内容区域中。 SQLite努力将单元格尽量放在b树页面末尾 它可以,以便为单元格指针数组的未来增长留出空间。 最后一个单元格指针数组项和的开头之间的区域 第一个单元格是未分配的区域。
如果页面不包含单元格(这仅适用于根页面 不包含任何行的表),然后将偏移量 单元格内容区域将等于页面大小减去保留空间的字节数。 如果数据库使用65536字节的页面大小,并且保留空间为零 (保留空间的常用值),然后是 空页希望为65536。 但是,该整数太大,无法存储在 2字节无符号整数,因此在其位置使用值0。
自由块是用于标识内部未分配空间的结构 b树页面。 自由块被组织成一条链。 的前2个字节 自由块是一个大整数,它是b树页面中的偏移量 链中下一个自由块的值,如果自由块是最后一个,则为零 链条。 每个自由块形式的第三个和第四个字节 一个以字节为单位的自由块大小的大整数,包括 4字节标头。 自由块始终按顺序连接 增加偏移量。 b-tree页面标题的第二个字段是 第一个自由块的偏移,如果上没有自由块,则为零 第页。 在格式良好的b-tree页面中,始终至少有一个单元格 在第一个自由块之前。
自由块至少需要4个字节的空间。 如果存在隔离 单元格内容区域内1、2或3个未使用字节组成的组,这些字节 包含一个片段。 存储所有片段中的总字节数 在b-tree页面标题的第五个字段中。 在形成良好的b树页面中, 片段中的总字节数不能超过60。
b-tree页面上的可用空间总量由大小组成 未分配区域的大小加上所有自由块的总大小加上 碎片可用字节数。 SQLite可能会不时重组 一个b-tree页面,这样就没有空闲块或片段字节 未使用的字节包含在未分配的空间区域中,并且 单元格在页面末尾被紧紧地填满。 这叫做 “碎片整理”b-tree页面。
可变长度整数或“varint”是一种静态哈夫曼编码 64位双补整数,对小正数使用较少的空间 值。 变量的长度在1到9个字节之间。 变种包括 零个或多个字节,其中高阶位集后跟一个字节 高位清零,或九个字节,以较短者为准。 前8个字节中每个字节的低7位和所有的8位 第九个字节用于重建64位双补整数。 变量是大元素的:从变量的前一个字节获取的位 比从后面的字节中提取的位更有意义。
单元格的格式取决于单元格的b树页面的类型 显示在上。下表显示了单元格中的元素 各种b树页面类型的外观顺序。
表B-树叶细胞(页眉0x0d):
有效负载的总字节数,包括任何 溢出 一个变量,即整数键。” 罗伊德 " 有效载荷的初始部分不会溢出 页。 的第一页的4字节大整数页码 溢出页面列表-如果所有有效负载都适合b-tree页面,则省略。
表B-树内部单元(标题0x05):
一个4字节的大-中页码,它是左边的子指针。 整数键的变量
索引B树叶单元(标头0x0a):
一个变量,是密钥有效负载的总字节数,包括任何 溢出 有效载荷的初始部分不会溢出 页。 的第一页的4字节大整数页码 溢出页面列表-如果所有有效负载都适合b-tree页面,则省略。
索引B树内部单元(标题0x02):
一个4字节的大-中页码,它是左边的子指针。 一个变量,是密钥有效负载的总字节数,包括任何 溢出 有效载荷的初始部分不会溢出 页。 的第一页的4字节大整数页码 溢出页面列表-如果所有有效负载都适合b-tree页面,则省略。
上述信息可以重新转换为如下表格格式:
B树单元格格式
资料型态 出现在。。。 描述 表叶(0x0d) 桌子内部(0x05) 索引叶(0x0a) 内部索引(0x02) 4字节整数 ✔ ✔ 左孩子的页码 变种 ✔ ✔ ✔ 有效负载的字节数 变种 ✔ ✔ 行ID 字节数组 ✔ ✔ ✔ 有效载荷 4字节整数 ✔ ✔ ✔ 第一个溢出页的页码
溢出页面的有效负载量还取决于 页面类型。 对于以下计算,让U为可用大小 数据库页面的总页面大小减去 每一页的末尾。 设P为有效载荷大小。 在以下内容中, 符号X表示可以直接存储的最大有效载荷量 在b-tree页面上,不溢出溢出页面和符号M 表示必须存储在btree上的最小有效负载量 允许在溢出之前翻页。
表B-树叶细胞:
设X为U-35。 如果有效载荷大小P小于或等于X,则 整个有效载荷被存储在b-树叶页面上。 设M为((U-12)*32/255)-23,设K为M+((P-M)%(U-4))。 如果P大于X 则表b-tree叶页上存储的字节数为K 如果K小于或等于X或M。 叶页面上存储的字节数决不小于M。
表B-树内部单元:
表b树的内部页面没有有效负载,因此永远不会有 任何可能溢出的有效载荷。
索引B-树叶或内部细胞:
设X为((U-12)*64/255)-23。 如果有效载荷尺寸P小于 或等于X,则整个有效载荷存储在b-tree页面上。 设M为((U-12)*32/255)-23,K为M+((P-M)%(U-4))。 如果P大于X,则数字 如果K小于或,则存储在索引b树页上的字节数为K 否则等于X或M。 索引页上存储的字节数决不小于M。
下面是对相同计算的另一种描述:
X是表B三叶页面的U-35或 (U-12)*64/255)-23用于索引页。 M总是(U-12)*32/255)-23。 设K为M+((P-M)%(U-4))。 如果P<=X,则有效载荷的所有P字节都直接存储在 b没有溢出的树页面。 如果P>X和K<=X,则P的前K字节存储在 btree页和剩余的P-K字节存储在溢出页上。 如果P>X和K>X,则P的前M个字节存储在 btree页和剩余的P-M字节存储在溢出页上。
溢出阈值旨在提供最小扇出 4用于索引b树,并确保有足够的有效载荷 位于通常可以访问记录头的b-tree页面上 不查阅溢出页。 事后来看 SQLite b树逻辑意识到这些阈值 变得简单多了。 然而,计算不能更改 不会导致不兼容的文件格式。 以及当前的计算 工作得很好,即使它们有点复杂。
1.7. 单元格有效负载溢出页面
当b-tree单元的有效负载对于b-tree页面来说太大时, 多余的内容溢出到溢出的页面上。 溢出页面形成链接 列表。 每个溢出页的前四个字节是一个大字节 整数,是链中下一页的页码,或零 链中的最后一页。 第五个字节到最后一个可用字节 字节用于保存溢出内容。
1.8. 指针映射或Ptrmap页面
指针映射或ptrmap页面是插入数据库的额外页面 进行操作 自动真空 和 增量真空 模式 效率更高。 数据库中的其他页面类型通常具有指针 从父级到子级。 例如,内部b-tree页面包含指针 到它的子b树页,溢出链有一个指针 链中从早到晚的环节。 ptrmap页面包含链接 从孩子到父母,信息流向相反的方向。
Ptrmap页必须存在于任何非零的数据库文件中 数据库头中偏移量52处的最大根b树页面值。 如果最大的根b-tree页值为零,则数据库不得 包含ptrmap页面。
在具有ptrmap页面的数据库中,第一个ptrmap页是第2页。 ptrmap页面由一个5字节条目数组组成。 让J成为 可容纳在页面可用空间中的5字节条目数。 (换句话说,J=U/5。)第一个ptrmap页面将包含反向指针 第3页至第J+2页(含)的信息。 第二个指针映射 页面将位于J+3页,ptrmap页面将提供反向指针 第J+4页至第2*J+3页(包括第2*J+3页)的信息。 等等 整个数据库文件。
在使用ptrmap页面的数据库中,标识位置的所有页面 通过上一段中的计算,必须是ptrmap页面,并且没有 其他页面可以是ptrmap页面。 除非,如果字节锁页碰巧 与ptrmap页位于同一页码上,然后移动ptrmap 关于这一案例,请参阅下一页。
ptrmap页面上的每个5字节条目都提供有关以下内容的反向链接信息 紧跟在指针图后面的一页。 如果B页是 ptrmap页面,然后提供关于页面B+1的反向链接信息 指针映射上的第一个条目。 关于第B+2页的信息是 由第二条目提供。 等等。
每个5字节的ptrmap条目由一个字节的“页面类型”信息组成 后面是一个4字节的大号页码。 可识别五种页面类型:
b树根页。 这个 页码应该为零。 免费列表页面。 页码应为 零。 的第一页 单元有效载荷溢出链。 页码是b树页面 包含内容溢出的单元格。 溢出链中的页面 而不是第一页。 页码是 溢流链。 非根b树页面。 这个 页码是父b树页面。
在任何包含ptrmap页面的数据库文件中,所有b-tree根页面 必须位于任何非根b树页、单元格有效负载溢出页之前,或 免费列表页面。 此限制确保根页面永远不会 在自动真空或增量真空期间移动。 自动真空 逻辑不知道如何更新sqlite_schema的rootpage字段 表,因此有必要防止根页被移动 在自动真空过程中,为了保持 sqlite_schema表。 根页面移动到 通过CREATE TABLE、CREATE INDEX、DROP TABLE和 DROP INDEX操作。
2 架构层
上述文本描述了SQLite文件的低级方面 格式。 b-tree机制为 访问大型数据集。 本节将描述 低级b-tree层用于实现高级SQL 能力。
表b树叶页的数据和键 索引b树页面的特征如上所示 作为任意字节序列。 前面的讨论提到一个键小于另一个键,但 没有定义“小于”的含义。 当前部分将介绍 这些遗漏。
有效负载,表b树数据或索引b树键, 始终为“记录格式”。 记录格式定义了对应的值序列 表或索引中的列。 记录格式指定数字 列、每列的数据类型以及每列的内容。
记录格式广泛使用 可变长度整数 或 变种 上面定义的64位有符号整数的表示。
记录按顺序包含标题和正文。 标题以一个变量开始,该变量决定总数 标头中的字节数。 变量值是中标头的大小 字节,包括大小本身。 尺寸变化如下 一个或多个附加变量,每列一个。 这些附加变量 被称为“序列号” 根据下表确定每列的数据类型:
记录格式的序列类型代码
序列号类型 内容大小 含义 0 0 值为NULL。 1 1 值是一个8位二进制补码整数。 2 2 值是一个16位双补整数。 三 三 值是一个24位双补整数。 4 4 值是一个32位双补整数。 5 6 值是一个48位双补整数。 6 8 值是一个64位双补整数。 7 8 值是一个较大的IEEE 754-2008 64位浮点数。 8 0 值是整数0。 (仅适用于 模式格式 4及以上。) 9 0 值为整数1。 (仅适用于 模式格式 4及以上。) 10,11 变量
保留供内部使用。 这些串行类型代码将 从不出现在格式良好的数据库文件中,但它们 可能用于临时数据库文件 SQLite有时会生成供自己使用的。 这些代码的含义可以从一个版本转换过来 SQLite到下一个。
N≥12且偶数 (N-12)/2 值是长度为(N-12)/2字节的BLOB。 N≥13且奇数 (N-13)/2 值是 文本编码 长度为(N-13)/2字节。 未存储nul终止符。
收割台尺寸变化 串行类型变量通常由单个字节组成。 这个 大型字符串和BLOB的串行类型变量可能会扩展到两个或三个 字节变量,但这是一个例外,而不是规则。 变量格式在编码记录头时非常有效。
记录中每列的值紧跟在标题之后。 对于串行类型0、8、9、12和13,值为0字节 长度。 如果所有列都属于这些类型,则 记录为空。
记录的值可能少于 相应的表。 例如,在 ALTER表格。。。 添加列 SQL语句已增加 不修改预先存在的行的表模式中的列数 在表中。 使用 默认值 用于表模式中定义的相应列。
2.2。 记录排序顺序
索引b树中键的顺序由 键表示的记录。 记录比较进度栏 按列显示。 记录的列从左到右进行检查。 这个 第一对不相等的列决定了相对顺序 两条记录中的一条。 各个列的排序顺序如下 跟随:
NULL值(序列类型0)首先排序。 NULL之后的数值排序(序列类型1到9) 和数字顺序。 文本值(奇数序列类型13及更大)在数字后排序 值的顺序由列确定 排序函数 . BLOB值(即使是序列类型12或更大)最后按顺序排序 由memcmp()决定。
A类 排序函数 为了计算 文本字段的顺序。 SQLite定义了三个内置的排序函数:
二元的 内置的BINARY排序规则逐字节比较字符串 使用memcmp()函数 来自标准C库。 无案例 NOCASE排序规则类似于BINARY,但大写字母除外 ASCII字符(“A”到“Z”) 在运行 比较。 只有ASCII字符是大小写折叠的。 无案例 不实现通用unicode无案例比较。 RTRIM公司 RTRIM类似于二进制,除了在任意一个的末尾有额外的空格 字符串不会更改结果。 换句话说,字符串将 只要它们彼此相等 只在末尾的空格数上有所不同。
可以将其他特定于应用程序的排序函数添加到 SQLite使用 sqlite3_create_collation() 接口。
所有字符串的默认排序函数为BINARY。 可以在 创建表格 语句上使用COLLATE子句 列定义 . 为列编制索引时 创建表格 语句用于索引中的列,默认情况下, 虽然可以使用 创建索引 声明。
2.3. SQL表的表示
数据库模式中的每个普通SQL表都在磁盘上表示 通过表b树。 表b-tree中的每个条目对应一行 SQL表的。 这个 罗伊德 SQL表的64位有符号 表b树中每个条目的整数键。
每个SQL表行的内容存储在数据库文件中 首先将各个列中的值组合成一个字节数组 以记录格式,然后将该字节数组作为有效载荷存储在 表b树中的一个条目。 记录中值的顺序为 与SQL表定义中的列顺序相同。 当SQL表包含 整数主键 列(别名为 罗伊德 )那么 列在记录中显示为NULL值。 SQLite将始终使用 引用 集成主键 列。
如果 密切关系 列的是REAL,并且该列包含 可以转换为整数而不丢失信息的值 (如果该值不包含小数部分,并且不太大 表示为整数),则列可以存储在记录中 作为整数。 SQLite会将值转换回浮动 从记录中提取时指向。
2.4. WITH OUT ROWID表格的表示
如果在 CREATE TABLE语句的末尾,则该表是 不带ROWID 表并使用不同的磁盘表示形式。 A不带ROWID 表使用索引b树而不是表b树进行存储。 WITH OUT ROWID b树中每个条目的键是一个由 PRIMARY KEY的列,后跟所有剩余的列 桌子。 主键列的显示顺序如下 在PRIMARY KEY子句中声明,其余列显示在 它们在CREATE TABLE语句中的出现顺序。
因此,WITHOUT ROWID表的内容编码是相同的 作为普通rowid表的内容编码,但 重新排列列的顺序,以便PRIMARY KEY列出现 首先,内容被用作索引b树中的键 而不是表b树中的数据。 具有REAL关联的列的特殊编码规则 适用于with OUT ROWID表,就像适用于ROWID表一样。
2.4.1. 抑制PRIMARY KEY中的冗余列 WITH OUT ROWID表格的
如果WITH OUT ROWID表的PRIMARY KEY使用相同的列 多次使用相同的排序序列,然后使用第二个和 该列在PRIMARY KEY定义中的后续出现是 忽略。 例如,以下CREATE TABLE语句都指定 相同的表,在磁盘上具有完全相同的表示形式:
创建表t1(a,b,c,d,主键(a,c)),但不包含行ID; 创建表t1(a,b,c,d,主键(a,c,a,c)),不带行ID; 创建表t1(a,b,c,d,主键(a,a,a,c)),不带行ID; 创建表t1(a,b,c,d,主键(a,a,a、a,c)),但不包含行ID;
上面的第一个示例是表的首选定义, 当然。 所有的例子都创建了一个with ROWID表 两个PRIMARY KEY列,“a”和“c”,按顺序排列,后跟 两个数据列“b”和“d”,也按此顺序排列。
2.5. SQL索引的表示
每个SQL索引,无论是通过 创建索引 陈述 或由UNIQUE或PRIMARY KEY约束隐含,对应于 索引数据库文件中的b-tree。 索引b树中的每个条目对应于 关联的SQL表。 索引b树的关键是 由正在被索引的列组成的记录,后跟 对应表行的键。 对于普通表,行键为 这个 罗伊德 、和用于 不带ROWID 表中的行键是PRIMARY key。 因为表中的每一行都有一个唯一的行键, 索引中的所有键都是唯一的。
在普通索引中 表以及与该表关联的每个索引中的条目。 然而,在 部分指数 ,索引b树仅包含条目 对应于上的WHERE子句表达式 CREATE INDEX语句为true。 索引树和表b树中的对应行共享相同的rowid 或主键值,并包含所有索引列的相同值。
2.5.1. 抑制WITH OUT ROWID二级索引中的冗余列
在WITHOUT ROWID表的索引中,如果PRIMARY KEY的列 也是索引中的一列,并且具有匹配的排序序列,则 indexed column is not repeated in the table-key suffix on the索引记录的末尾。 例如,考虑以下SQL:
创建表ex25(a,b,c,d,e,主键(d,c,a)),不带rowid; 在ex25(c,e)上创建索引ex25ce; 在ex25(a,c,d,e)上创建索引ex25acde; 在ex25上创建索引ex25ae(聚合nocase,e);
ex25ce索引中的每一行都是一条记录 具有以下列:c、e、d、a。前两列为 被索引的列,c和e。其余的列是主列 对应表行的键。 通常,主键为 列d、c和a,但因为列c已经出现在 索引,它从键后缀中省略。
在索引列覆盖所有列的极端情况下 对于PRIMARY KEY,索引将只包含 索引。 上面的ex25acde示例演示了这一点。 中的每个条目 ex25acde索引仅包含列a、c、d和e,其中 订单。
ex25ae中的每一行包含五列:a、e、d、c、a 列重复,因为第一次出现的“a”具有排序规则 函数nocase和第二个有一个排序序列binary。 如果“a”列不重复,并且表中包含两个或多个 具有相同“e”值且“a”仅大小写不同的条目,则 所有这些表条目都对应于 索引,这将打破表之间的一对一对应关系 和索引。
抑制索引的键后缀中的冗余列 条目仅出现在WITH OUT ROWID表中。 在普通的rowid表中, 索引项始终以rowid结尾,即使 整数主键 列是被索引的列之一。
2.6. SQL数据库架构的存储
数据库文件的第1页是表b树的根页 保存一个名为“ sqlite架构 “.这个b树是已知的 作为“模式表”,因为它存储完整的 数据库模式。 sqlite_schema表的结构如下 如果它是使用以下SQL创建的:
创建表sqlite_schema( 键入文本, 名称文本, tbl_ name文本, 根页整数, sql文本 );
sqlite_schema表包含每个表、索引、视图、, 和触发器(统称为“对象”) 没有sqlite_schema表本身的条目。 sqlite_schema表 包含的条目 内部架构对象 除应用程序外- 和程序员定义的对象。
sqlite_schema.type列为1 “table”、“index”、“view”或“trigger” 根据定义的对象类型。 使用了“table”字符串 对于普通和 虚拟表 .
sqlite_schema.name列将保存对象的名称。 独特 和 主钥匙 表上的约束导致SQLite创建 内部索引 名称格式为“sqlite_autoindex_TABLE_N” 其中,TABLE被包含 constraint和N是一个以1开始并增加1的整数 表定义中的每个约束。 在一个 没有ROWID 表中没有sqlite_schema条目 主键,但保留了“sqlite_autoindex_TABLE_N”名称 就好像sqlite_schema条目确实存在一样。 这个 将影响后续UNIQUE约束的编号。 “sqlite_autoindex_TABLE_N”名称从未分配给 集成主键 ,要么在rowid表中,要么在WITH rowid表中。
sqlite_schema.tbl_name列保存表或视图的名称 对象关联的。 对于表或视图 tbl_name列是name列的副本。 对于索引,tbl_name 被索引的表的名称。 对于触发器,tbl_name 列存储引发触发器的表或视图的名称 开火。
sqlite_schema.rootpage列存储根的页码 表和索引的b-tree页面。 对于定义视图、触发器的行, 和虚拟表,rootpage列为0或NULL。
sqlite_schema.sql列存储描述 对象。 此SQL文本是 创建表格 , 创建虚拟表格 , 创建索引 , 创建视图 ,或 创建触发器 如果根据该语句进行计算 数据库文件,当它是 数据库连接 将重新创建对象。 文本通常是原件的副本 用于创建对象但应用了规范化的语句 文本符合以下规则:
开头的CREATE、TABLE、VIEW、TRIGGER和INDEX关键字 语句转换为所有大写字母。 如果TEMP或TEMPORARY关键字出现在 初始CREATE关键字。 出现在名称之前的任何数据库名称限定符 正在创建的对象被删除。 前导空格被删除。 前两个关键字后面的所有空格都转换为一个空格 空间。
sqlite_schema.sql列中的文本是原始文本的副本 创建对象的CREATE语句文本,但规范化为 如上所述,并经后续修订 ALTER表格 声明。 sqlite_schema.sql对于 内部索引 那是 由自动创建 独特 或 主钥匙 约束。
2.6.1. 模式表的替代名称
名称“sqlite_schema”在文件格式中的任何位置都不会出现。 该名称只是数据库实现使用的约定。 由于历史和运营方面的考虑 “sqlite_schema”表有时也可以由 以下别名:
sqlite_主机 sqlite模板模式 sqlite模板主控程序
因为架构表的名称不会出现在 文件格式,如果 应用程序选择通过以下方式之一引用模式表 这些替代名称。
2.6.2. 内部架构对象
除了由创建的表、索引、视图和触发器之外 使用CREATE语句SQL的应用程序和/或开发人员 sqlite_schema表可以包含零个或多个以下项 内部架构对象 由SQLite为其创建的 自己内部使用。 内部架构对象的名称 始终以“sqlite_”和任何表、索引、视图或触发器开头 名称以“sqlite”开头的是一个内部模式对象。 SQLite禁止应用程序创建名称以开头的对象 带有“sqlite_”。
SQLite使用的内部模式对象可能包括以下内容:
具有“sqlite_autoindex_TABLE_N”格式名称的索引 用于实现 独特 和 主钥匙 上的约束 普通桌子。
一个名为“sqlite_sequence”的表,用于跟踪 历史上最大值的 集成主键 对于一张桌子 使用 自动更正 .
名称格式为“sqlite_statN”的表,其中N是整数。 这些表存储由 分析 命令,并由查询规划器用于帮助确定最佳 用于每个查询的算法。
新的内部架构对象名称,始终以“sqlite_”开头, 可以在未来的版本中添加到SQLite文件格式中。
2.6.3. sqlite_sequence表
sqlite_sequence表是一个内部表,用于帮助实现 自动更正 。sqlite_sequence表是自动创建的 每当任何具有AUTOINCREMENT整数主表的普通表 键已创建。 创建后,sqlite_sequence表存在于 sqlite_schema表永久; 它不能被丢弃。 sqlite_sequence表的模式是:
创建表sqlite_sequence(name,seq);
sqlite_sequence表中有一行用于每个普通 使用AUTOINCREMENT的表。 表的名称(如中所示 sqlite_schema.name)位于sqlite_sequence.name字段中 集成主键 插入该表后 在sqlite_sequence.seq字段中。 为AUTOINCREMENT自动生成的新整数主键 保证表大于的sqlite_sequence.seq字段 那张桌子。 如果AUTOINCREMENT表的sqlite_sequence.seq字段已经位于 然后,最大的整数值(9223372036854775807)尝试添加新的 带有自动生成的整数主表的表行将失败 带有 SQLITE_FULL公司 错误。 当需要时,sqlite_sequence.seq字段会自动更新 将新条目插入到AUTOINCREMENT表中。 AUTOINCREMENT表的sqlite_sequence行会自动删除 当桌子被放下时。 如果AUTOINCREMENT表的sqlite_sequence行不存在 更新AUTOINCREMENT表,然后创建一个新的sqlite_sequence行。 如果AUTOINCREMENT表的sqlite_sequence.seq值是手动的 设置为整数以外的值,然后尝试 插入或更新AUTOINCREMENT表,则行为未定义。
允许应用程序代码修改sqlite_sequence表,以添加 新行、删除行或修改现有行。 然而,应用程序 如果sqlitesequence表尚不存在,则代码无法创建该表。 应用程序代码可以删除sqlite_sequence表中的所有条目, 但是应用程序代码无法删除sqlite_sequence表。
2.6.4. sqlite_stat1表
sqlite_stat1是由 分析 命令 并用于保存有关表和索引的补充信息 查询规划器可以用来帮助它找到执行查询的更好方法。 应用程序可以更新、删除、插入或删除sqlite_stat1 表,但不能创建或更改sqlitestat1表。 sqlite_stat1表的模式如下:
创建表格sqlite_stat1(tbl,idx,stat);
每个索引通常有一行,索引由 sqlitestat1.idx列中的名称。 sqlite_stat1.tbl列是 索引所属的表的名称。 在每一行中, sqlitestat.stat列将是 由整数列表后跟零或多个整数组成的字符串 论据。 此中的第一个整数 list是索引中的大致行数。 (数量 索引中的行与表中的行数相同, 除了 部分索引 .) 第二个整数是索引中的近似行数 在索引的第一列中具有相同值的。 第三个 integer是索引中具有 前两列的值相同。 第N个整数(对于N>1) 是中的估计平均行数 对于第一N-1列具有相同值的索引。 对于 如果是K列索引,则stat列中将有K+1个整数。 如果 索引是唯一的,那么最后一个整数将是1。
可以选择跟随stat列中的整数列表 通过参数,每个参数都是一系列非空格字符。 所有参数前面都有一个空格。 未识别的参数将被默认忽略。
如果存在“无序”参数,那么查询计划器假设 索引是无序的,不会将该索引用于范围查询 或用于排序。
“sz=NNN”参数(其中NNN表示一个1或多个数字的序列) 表示表中所有记录的平均行大小或 索引是每行NNN个字节。 SQLite查询规划器可以使用 “sz=NNN”标记提供的估计行大小信息 帮助它选择需要较少磁盘I/O的较小表和索引。
sqlite_stat1.stat字段中存在“noskipscan”标记 索引的 跳过扫描优化 .
将来可能会在stat列的末尾添加新的文本标记 SQLite的增强功能。 为了兼容性,末尾有无法识别的标记 stat列的。
如果sqlite_stat1.idx列为NULL,则sqlite_sstat1.stat 列包含一个整数,该整数是 由sqlitestat1.tbl标识的表中的行。 如果sqlite_stat1.idx列与sqlite_sstat1.tbl相同 列,则该表是 不带ROWID 表和sqlitestat1.stat 字段包含有关实现 没有ROWID表。
2.6.5. sqlite_stat2表
sqlite_stat2仅在编译sqlite时创建和使用 如果SQLITE_ENABLE_STAT2,并且SQLITE版本号介于 3.6.18(2009-09-11)和3.7.8(2011-09-19)。 sqlite_stat2表既不被任何 3.6.18之前或3.7.8之后的SQLite版本。 sqlite_stat2表包含其他信息 关于索引中键的分布。 sqlite_stat2表的模式如下:
创建表格sqlite_stat2(tbl,idx,sampleno,sample);
每个中的sqlite_stat2.idx列和sqlite_sstat2.tbl列 sqlitestat2表的行标识该行描述的索引。 sqlite_stat2中通常有10行 每个索引的表。
具有sqlite_stat2.sampleno的索引的sqlite_sstat2条目 0到9之间(含0和9)是 在沿索引均匀分布的点处获取的索引。 设C是索引中的行数。 然后通过以下公式给出采样行
行数=(i*C*2+C)/20
前一表达式中的变量i在0和9之间变化。 从概念上讲,索引空间被划分为 10个均匀的桶,样本位于每个桶的中间一行。
此处记录sqlite_stat2的格式以供旧版参考。 SQLite的最新版本不再支持SQLite_stat2和 sqlitestat2表(如果存在)将被忽略。
2.6.6. sqlite_stat3表
sqlite_stat3仅在编译sqlite时使用 具有 启用状态3 或 SQLITE_ENABLE_STAT4数据库 如果SQLite版本号为3.7.9(2011-11-01)或更高。 sqlite_stat3表既不被任何 SQLite 3.7.9之前的版本。 如果 SQLITE_ENABLE_STAT4数据库 使用compile-time选项,并且 SQLite版本号为3.8.1(2013-10-17)或更高版本, 然后可以读取sqlitestat3,但不能写入。 sqlite_stat3表包含其他信息 关于索引中键的分布 查询规划器可以用来设计更好更快的查询算法。 sqlite_stat3表的模式如下:
创建表格sqlite_stat3(tbl,idx,nEq,nLt,nDLt,sample);
对于每个索引,sqlite_stat3表中通常有多个条目。 sqlite_stat3.sample列保存 由sqlitestat3.idx和sqlitestat4.tbl标识的索引。 sqlite_stat3.nEq列包含近似值 索引中最左列完全匹配的条目数 样品。 sqlite_stat3.nLt在 其最左侧列小于样本的索引。 sqlite_stat3.nDLt列包含近似值 索引中不同的最左侧条目数小于 样品。
每个索引可以有任意数量的sqlite_stat3条目。 这个 分析 命令通常会生成sqlitestat3表 包含10到40个样本,分布在 键空间和较大的nEq值。
在格式良好的sqlite_stat3表中,任何单个 索引的出现顺序必须与它们在索引中的出现顺序相同。 换句话说,如果最左侧列S1的条目位于 索引b树比 条目带有最左边的列S2,然后在sqlite_stat3表中, 样本S1的rowid必须小于样本S2。
2.6.7. sqlite_stat4表
sqlite_stat4仅在编译sqlite时创建和使用 具有 SQLITE_ENABLE_STAT4数据库 如果SQLite版本号为 3.8.1(2013-10-17)或更高。 sqlite_stat4表既不被任何 3.8.1之前的SQLite版本。 sqlite_stat4表包含其他信息 关于索引中键的分布或 的主键中的键 没有ROWID 表。 查询规划器有时可以使用 sqlitestat4表设计更好更快的查询算法。 sqlite_stat4表的模式如下:
创建表格sqlite_stat4(tbl,idx,nEq,nLt,nDLt,sample);
sqlite_stat4表中通常有10到40个条目用于 统计数据可用的每个索引,但这些限制是 不是硬边界。 sqlite_stat4表中各列的含义如下:
待定:
sqlite_stat4.tbl列保存拥有 行描述的索引 身份证号码:
sqlite_stat4.idx列包含 行描述,或者在 的sqlite_stat4条目 不带ROWID 表 表本身的名称。 示例:
sqlite_stat4.sample列包含一个BLOB 在中 记录格式 对索引列进行编码,后跟 rowid表的rowid或按主键的列 用于WITH OUT ROWID表格。 WITH OUT ROWID表本身的sqlite_stat4.sample BLOB 只包含主键的列。 让sqlite_stat4.sample blob编码的列数为N。 对于普通rowid表上的索引,N将比数字多1 已编制索引的列(共列)。 对于WITH OUT ROWID表上的索引,N将是列数 索引加上主键中的列数。 对于WITH OUT ROWID表格,N将是 主键。 nEq:
sqlite_stat4.nEq列包含N个整数的列表,其中 第K个整数是索引中条目数的近似值 其最左边的K列与最左边的K列完全匹配 样品的。 无光:
sqlite_stat4.nLt列包含N个整数的列表,其中 第K个整数是 索引,其K个最左侧列的总和小于 K留下了样本的大部分列。 有限责任公司:
sqlite_stat4.nDLt列包含N个整数的列表,其中 第K个整数是近似值 索引中在前K列中不同的条目数,以及 其中,最左边的K列的总和小于最左边的 样本的K列。
sqlite_stat4是sqlite_sstat3表的泛化。 这个 sqlite_stat3表提供了有关 索引,而sqlite_stat4表提供了有关所有列的信息 索引的。
每个索引可以有任意数量的sqlite_stat4条目。 这个 分析 命令通常会生成sqlite_stat4表 包含10到40个样本,分布在 键空间和较大的nEq值。
在格式良好的sqlite_stat4表中,任何单个 索引的出现顺序必须与它们在索引中的出现顺序相同。 换句话说,如果条目S1在索引b树中早于 条目S2,则在sqlite_stat4表中,示例S1必须具有 rowid小于样本S2。
三。 回滚日志
回滚日志是与每个SQLite数据库关联的文件 保存用于将数据库文件还原为其初始文件的信息的文件 交易过程中的状态。 回滚日志文件始终位于同一位置 目录作为数据库 文件,与数据库文件同名,但包含字符串 " -日记账 “追加。只能有一个回滚日志 与给定数据库关联,因此只能有一次写入 一次针对单个数据库打开的事务。
如果事务由于应用程序崩溃而中止 系统崩溃,或硬件电源故障或崩溃,则数据库可能 处于不一致的状态。 下次SQLite尝试打开时 数据库文件,回滚日志文件的存在将是 检测到日志,将自动播放日志以恢复 数据库在不完整事务开始时的状态。
只有在回滚日志存在且 包含有效的标头。 因此,事务可以在一个 三种方式中的一种:
可以删除回滚日志文件, 回滚日志文件可以截断为零长度,或者 回滚日志的标头可以用覆盖 无效的标题文本(例如,全部为零)。
这三种提交事务的方式对应于DELETE, 的TRUNCATE和PERSIST设置 日志模式杂注 .
有效的回滚日志以以下格式的标头开头:
回滚日记账标题格式
抵消 大小 描述 0 8 标题字符串:0xd9、0xd5、0x05、0xf9、0x20、0xa1、0x63、0xd7 8 4 “页面计数”-下一段中的页面数 日记账,或-1到 表示文件末尾的所有内容 12 4 校验和的随机nonce 16 4 数据库的初始大小(以页面为单位) 20 4 写入此命令的进程假定的磁盘扇区大小 日记账。 24 4 此日志中的页面大小。
回滚日志标头用零填充,大小为 单个扇区(由偏移量20处的扇区大小整数定义)。 收割台本身位于扇区中,因此如果在 写入扇区时,标题后面的信息将是 (希望)没有损坏。
在页眉和零填充之后是零个或多个页面记录。 每个 页面记录存储数据库文件中页面内容的副本 在它被改变之前。 同一页不能出现多次 在单个回滚日志中。 要回滚不完整的事务、进程 只需要从头到尾读取回滚日志,并且 将日志中找到的页面写回位于 适当的位置。
让数据库页面大小(偏移量24处的整数值 在日记账标题中)为N。 那么页面记录的格式如下:
回滚日记页记录格式
抵消 大小 描述 0 4 数据库文件中的页码 4 N个 交易开始前页面的原始内容 N+4号 4 校验和
校验和是一个无符号32位整数,计算如下:
将校验和初始化为 偏移量12处的轴颈标题。 将索引X初始化为N-200(其中N是数据库页面的大小 以字节为单位。 将偏移量X处的字节解释为8位无符号整数 并将该整数的值添加到校验和中。 X减去200。 如果X大于或等于零,请返回步骤3。
校验和值用于防止不完整写入 断电后的日志页面记录。 不同的随机现时 每次启动交易时使用,以将风险降至最低 未写入的扇区可能偶然包含来自同一页的数据 这是以前期刊的一部分。 通过更改每个 事务,磁盘上陈旧的数据仍将生成不正确的校验和 并以高概率被检测到。 校验和仅使用稀疏样本 基于性能原因的32位字数据记录-设计研究 在SQLite 3.0.0的规划阶段 对整个页面进行校验和时,性能受到了显著影响。
让日记账标题中偏移量8处的页面计数值为M。 如果M大于零,则在M页记录日志文件后 可以将零填充到下一个扇区大小的倍数,然后再填充另一个 可以插入日记帐标题。 同一范围内的所有日记帐标题 日志必须包含相同的数据库页面大小和扇区大小。
如果M在初始日记帐标题中为-1,则页面记录数 接下来是通过计算可容纳多少页记录来计算的 日志文件其余部分的可用空间。
4 写入头日志
从开始 版本3.7.0 (2010年7月21日), SQLite支持新事务 称为“ 预写日志 “或” WAL(沃尔沃) ”。 当数据库处于WAL模式时,与该数据库的所有连接必须 使用WAL。 特定数据库将使用回滚日志 或WAL,但不能同时两者。 WAL始终与数据库位于同一目录中 文件,与数据库文件同名,但包含字符串 " -沃尔 ”追加。
A类 WAL文件 由一个标题后跟零个或多个“帧”组成。 每个框架记录来自 数据库文件。 数据库的所有更改都通过写入 框架进入WAL。 当写入满足以下条件的帧时提交事务 包含提交标记。 单个WAL可以而且通常会记录 多个事务。 WAL的内容定期为 在名为 “检查点”。
单个WAL文件可以重复使用多次。 换句话说 WAL可以填充框架,然后进行检查,然后新建 帧可以覆盖旧的帧。 WAL总是从一开始就增长 最后。 连接到每个帧的校验和和计数器是 用于确定WAL中哪些帧是有效的 是之前检查点的残留物。
WAL标头的大小为32字节,由以下八个字节组成 big-endian 32位无符号整数值:
WAL标题格式
抵消 大小 描述 0 4 神奇数字。 0x377f0682或0x377f 0683 4 4 文件格式版本。 目前为3007000。 8 4 数据库页面大小。 示例:1024 12 4 检查点序列号 16 4 Salt-1:随每个检查点递增的随机整数 20 4 Salt-2:每个检查点的不同随机数 24 4 校验和-1:标头前24个字节的校验和的第一部分 28 4 校验和2:头的前24个字节上校验和的第二部分
紧跟在wal-header后面的是零帧或更多帧。 每个 帧由一个24字节的帧头和一个 页面大小 字节 共页数据。 帧标头是六个大字符32位无符号 整数值,如下所示:
WAL帧标题格式
抵消 大小 描述 0 4 页码 4 4 对于提交记录,数据库文件的大小(以页为单位) 提交后。 对于所有其他记录,为零。 8 4 从WAL标题复制的Salt-1 12 4 从WAL标题复制的Salt-2 16 4 Checksum-1:累计校验和(包括本页) 20 4 校验和2:累计校验和的后半部分。
当且仅当下列条件满足时,框架才被视为有效 正确:
帧标头中的salt-1和salt-2值匹配 wal-header中的盐值
帧头最后8个字节中的校验和值 与上连续计算的校验和完全匹配 WAL报头的前24个字节和前8个字节,以及 所有帧的内容 直到并包括当前帧。
4.2。 校验和算法
校验和是通过将输入解释为 偶数个无符号32位整数:x(0)到x(N)。 如果 WAL头的前4个字节中的幻数是0x377f0683 如果幻数是0x377f0682,则这些整数是小整数。 校验和值始终存储在 big-endian格式,无论使用哪个字节顺序进行计算 校验和。
校验和算法仅适用于以下内容的倍数 长度为8字节。 换句话说,如果输入是x(0)到x(N) 那么N必须是奇数。 校验和算法如下:
s0=s1=0 对于从0到n-1的i,步骤2: s0+=x(i)+s1; s1+=x(i+1)+s0; 外循环 #结果为s0和s1
输出s0和s1都是使用斐波那契权重的加权校验和 以相反的顺序。 (最大的斐波那契权重出现在第一个元素上 求和的序列。) s1值跨越所有32位整数 序列的项,而s0省略了最后一项。
4.3. 检查点算法
在上 检查点 ,首先使用将WAL刷新到持久存储 的xSync方法 变频调速系统 . 然后将WAL的有效内容传输到数据库文件中。 最后,使用另一个 xSync方法调用。 xSync操作充当写入屏障-所有写入都已启动 在xSync完成之前 xSync开始。
检查点无需运行即可完成。 可能是一些 读者仍在使用包含数据的旧事务 在数据库文件中。 在这种情况下,为更新的 从WAL文件到数据库的事务将删除内容 从仍在使用旧事务的下层读取器中取出。 为了避免这种情况, 只有当所有读取器都使用 WAL中的最后一个事务。
4.4. WAL重置
完成检查点后,如果事务中没有其他连接 使用WAL,然后后续写事务可以 从头开始覆盖WAL文件。 这称为“重置 WAL”。在第一个新的 写入事务,WAL标头salt-1值递增 并且salt-2值被随机化。 盐的这些变化无效 WAL中已被选中但尚未被选中的旧框架 覆盖,并防止它们再次被选中。
可以选择在重置时截断WAL文件,但不必。 如果WAL不被截断,性能通常会更好一些,因为 文件系统覆盖现有文件的速度通常比它们快 将生成一个文件。
4.5. 读卡器算法
要从数据库中读取页面(称为页码P) 首先检查WAL以查看它是否包含页面P。如果是,则 页面P的最后一个有效实例,后跟提交帧 或者提交帧本身成为读取的值。 如果WAL 不包含页面P的有效副本和提交副本 帧或后跟提交帧,然后从中读取页面P 数据库文件。
要启动读取事务,读取器记录值的数量 WAL中的帧称为“mxFrame”。 ( 更多详细信息 ) 读取器使用这个记录的mxFrame值 用于所有后续读取操作。 可以附加新事务 WAL,但只要读取器使用其原始mxFrame值 并忽略随后附加的内容,则读者将看到 来自单个时间点的数据库的一致快照。 此技术允许多个并发读卡器查看不同的 数据库内容的版本。
前面段落中的读者算法工作正常,但 因为页面P的框架可以出现在WAL中的任何位置 读者必须扫描整个WAL,寻找页面P帧。 如果 WAL较大(通常为数兆字节),扫描速度较慢, 并且读取性能受到影响。 为了克服这个问题 维护称为wal-index的数据结构以加快 搜索特定页面的框架。
从概念上讲,wal-index是共享内存,尽管当前 VFS实现将内存映射文件用于操作系统 便携性。 记忆地图 文件与数据库位于同一目录中,并且具有相同的名称 作为带有“ -shm公司 “附加后缀。因为 wal-index是共享内存,SQLite不支持 日志模式=WAL 当客户端位于不同的计算机上时,如 数据库的所有客户端都必须能够共享相同的内存。
wal-index的目的是快速回答这个问题:
给定页码P和最大WAL帧索引M, 返回页面P的最大WAL帧索引,该索引不超过M, 如果页面P没有不超过M的帧,则返回NULL。
这个 M(M) 上一段中的值是“mxFrame”值 定义于 第4.4节 在开头读到的 定义WAL的最大帧 读者将使用。
墙-拱是瞬态的。 撞车后,墙-index 从原始WAL文件重建。 需要VFS 当最后一个 与它的连接关闭。 由于墙-柱是瞬态的,它可以 使用特定于架构的格式; 它不一定是跨平台的。 因此,与存储所有值的数据库和WAL文件格式不同 作为big-endian,wal-index在本机中存储多字节值 主机的字节顺序。
本文档涉及数据库的持久状态 由于wal-index是一个瞬态结构 此处将提供有关wal-index格式的信息。 有关墙式index格式的其他详细信息,请参见 分开的 WAL-index文件格式 文档。
此页面上次修改时间 2024-04-16 17:22:18 联合技术公司