小。速度很快。可靠。
选择任意三个选项。
数据库文件格式

本文档描述并定义了磁盘数据库文件此后所有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 (232- 2). 最小尺寸SQLite数据库是一个512字节的页面。数据库的最大大小为4294967294页,每个65536字节页面或281474976579584字节(约281TB)。通常SQLite会达到底层文件系统或磁盘的最大文件大小限制早在硬件达到其内部大小限制之前。

在常用情况下,SQLite数据库的大小往往在几千字节之间到几GB,尽管已知存在太字节大小的SQLite数据库生产中。

在任何时候,主数据库中的每个页面都有一个使用以下选项之一:

所有对主数据库文件的读取和写入都从一个页面开始边界和所有写操作都是页面大小的整数。读取次数页面大小通常也是整数,只有一个例外当第一次打开数据库时数据库文件(数据库文件头)被读取为子页面大小单元。

在修改数据库的任何信息共享页之前,该页面的原始未修改内容将写入回滚日志。如果交易中断,需要回滚后,可以使用回滚日志来恢复数据库恢复到其原始状态。自由列表叶子页没有需要在回滚时恢复的信息,因此未在修改前写入日记账,以便减少磁盘I/O。

1.3.数据库标题

数据库文件的前100个字节组成数据库文件收割台。数据库文件头分为多个字段,如下所示下表。数据库文件头中的所有多字节字段都是以最高有效字节优先(大-中)存储。

数据库标题格式
抵消大小描述
016标题字符串:“SQLite format 3\000”
162数据库页面大小(以字节为单位)。必须是512之间的2的幂和32768,或表示65536页大小的值1。
181文件格式写入版本。1代表遗产;2用于WAL(沃尔沃).
191文件格式读取版本。1代表遗产;2用于WAL(沃尔沃).
201每页末尾未使用的“保留”空间字节数。通常为0。
211最大嵌入有效载荷分数。必须为64。
221最小嵌入有效载荷分数。必须是32岁。
231叶有效载荷分数。必须是32岁。
244文件更改计数器。
284数据库文件的大小(以页面为单位)。“头数据库大小”。
324第一个空闲列表主干页的页码。
364空闲列表页面的总数。
404架构cookie。
444模式格式编号。支持的模式格式有1、2、3和4。
484默认页面缓存大小。
524在auto-vacuum或增量-真空模式,否则为零。
564数据库文本编码。值1表示UTF-8。值为2指UTF-16le。值3表示UTF-16be。
604用户版本杂注.
644增量真空模式为True(非零)。否则为False(零)。
684由设置的“应用程序ID”PRAGMA应用程序id.
7220保留用于扩展。必须为零。
924这个版本-编号有效.
964 SQLITE_VERSION_NUMBER(SQLITE_版本号)

1.3.1.魔术头字符串

每个有效的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 endian1,被认为是一个神奇的数字,代表65536页的大小。或者可以将双字节字段视为一个小的端号,然后说它表示页面大小除以256。这两个页面大小字段的解释是等效的。

1.3.3.文件格式版本号

偏移量处的文件格式写入版本和文件格式读取版本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模式下的每个事务上不递增。

1.3.7.表头数据库大小

偏移量为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字节大整数存储总数免费列表上的页数。

模式cookie是偏移量为40的4字节大整数每当数据库模式更改时,该值就会增加。A类根据特定版本的数据库模式。当数据库模式更改时,语句必须重新准备。运行准备好的语句时,它首先检查模式cookie,以确保值与语句时的值相同已准备好,如果架构cookie已更改,则语句自动重新准备并重新运行,或使用SQLITE_SCHEMA公司错误。

1.3.10.架构格式编号

模式格式编号是一个偏移量为44的4字节大整数。模式格式编号与文件格式读写类似偏移量18和19处的版本号,模式格式号除外指的是高级SQL格式,而不是低级b树格式化。目前定义了四个模式格式编号:

  1. 所有版本的SQLite都能理解格式13.0.0版(2004-06-18).
  2. 格式2增加了同一表中行的功能具有不同数量的列,以便支持ALTER表格。。。添加列功能。支持SQLite中增加了读写格式23.1.3版2005年2月20日。
  3. 格式3增加了添加额外列的功能ALTER表格。。。添加列具有非NULL默认值值。此功能是在SQLite中添加的3.1.4版2005年3月11日。
  4. 格式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.3.17.预留用于扩展的页眉空间

数据库文件头的所有其他字节都保留给未来扩展,并且必须设置为零。

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页面按以下顺序划分为多个区域:

  1. 100字节的数据库文件头(仅在第1页上找到)
  2. 8或12字节b树页眉
  3. 单元格指针数组
  4. 未分配的空间
  5. 单元格内容区域
  6. 保留区域。

100字节的数据库文件头只能在第1页上找到,即总是一个表b树页面。数据库文件中的所有其他b-tree页面省略这个100字节的头。

保留区域是每个页面(锁定页面除外),扩展可用于保持每页信息。保留区域的大小由一个字节决定数据库文件头中偏移量为20的无符号整数。保留区域的大小通常为零。

对于叶页面,b-tree页眉的大小为8个字节,而对于叶页面则为12个字节内部页面的字节数。页眉中的所有多字节值都是大发动机。b-tree页头由以下字段组成:

B树页眉格式
抵消大小描述
01偏移量为0的单字节标志,表示b-tree页面类型。
  • 值2(0x02)表示该页是内部索引b树页。
  • 值5(0x05)表示该页是内部表b树页。
  • 值10(0x0a)表示该页是叶索引b-tree页。
  • 值13(0x0d)表示该页是叶表b-tree页。
b-tree页面类型的任何其他值都是错误的。
12偏移量1处的两字节整数给出页面上的第一个自由块,如果没有自由块,则为零。
2偏移量3处的两字节整数表示页面上的单元格数。
52偏移量5处的两字节整数指定单元格内容的开始区域。此整数的零值被解释为65536。
71偏移量7处的单字节整数给出了空闲碎片数单元格内容区域中的字节。
84偏移量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。

下面是对相同计算的另一种描述:

溢出阈值旨在提供最小扇出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字节的大号页码。可识别五种页面类型:

  1. b树根页。这个页码应该为零。
  2. 免费列表页面。页码应为零。
  3. 的第一页单元有效载荷溢出链。页码是b树页面包含内容溢出的单元格。
  4. 溢出链中的页面而不是第一页。页码是溢流链。
  5. 非根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能力。

2.1、。记录格式

表b树叶页的数据和键索引b树页面的特征如上所示作为任意字节序列。前面的讨论提到一个键小于另一个键,但没有定义“小于”的含义。当前部分将介绍这些遗漏。

有效负载,表b树数据或索引b树键,始终为“记录格式”。记录格式定义了对应的值序列表或索引中的列。记录格式指定数字列、每列的数据类型以及每列的内容。

记录格式广泛使用可变长度整数变种上面定义的64位有符号整数的表示。

记录按顺序包含标题和正文。标题以一个变量开始,该变量决定总数标头中的字节数。变量值是中标头的大小字节,包括大小本身。尺寸变化如下一个或多个附加变量,每列一个。这些附加变量被称为“序列号”根据下表确定每列的数据类型:

记录格式的序列类型代码
序列号类型内容大小含义
00值为NULL。
11值是一个8位二进制补码整数。
22值是一个16位双补整数。
值是一个24位双补整数。
44值是一个32位双补整数。
56值是一个48位双补整数。
68值是一个64位双补整数。
78值是一个较大的IEEE 754-2008 64位浮点数。
80值是整数0。(仅适用于模式格式4及以上。)
90值为整数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树中键的顺序由键表示的记录。记录比较进度栏按列显示。记录的列从左到右进行检查。这个第一对不相等的列决定了相对顺序两条记录中的一条。各个列的排序顺序如下跟随:

  1. NULL值(序列类型0)首先排序。
  2. NULL之后的数值排序(序列类型1到9)和数字顺序。
  3. 文本值(奇数序列类型13及更大)在数字后排序值的顺序由列确定排序函数.
  4. 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文本是创建表格,创建虚拟表格,创建索引,创建视图,或创建触发器如果根据该语句进行计算数据库文件,当它是数据库连接将重新创建对象。文本通常是原件的副本用于创建对象但应用了规范化的语句文本符合以下规则:

sqlite_schema.sql列中的文本是原始文本的副本创建对象的CREATE语句文本,但规范化为如上所述,并经后续修订ALTER表格声明。sqlite_schema.sql对于内部索引那是由自动创建独特主钥匙约束。

2.6.1.模式表的替代名称

名称“sqlite_schema”在文件格式中的任何位置都不会出现。该名称只是数据库实现使用的约定。由于历史和运营方面的考虑“sqlite_schema”表有时也可以由以下别名:

  1. sqlite_主机
  2. sqlite模板模式
  3. sqlite模板主控程序

因为架构表的名称不会出现在文件格式,如果应用程序选择通过以下方式之一引用模式表这些替代名称。

2.6.2.内部架构对象

除了由创建的表、索引、视图和触发器之外使用CREATE语句SQL的应用程序和/或开发人员sqlite_schema表可以包含零个或多个以下项内部架构对象由SQLite为其创建的自己内部使用。内部架构对象的名称始终以“sqlite_”和任何表、索引、视图或触发器开头名称以“sqlite”开头的是一个内部模式对象。SQLite禁止应用程序创建名称以开头的对象带有“sqlite_”。

SQLite使用的内部模式对象可能包括以下内容:

新的内部架构对象名称,始终以“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时使用具有启用状态3SQLITE_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尝试打开时数据库文件,回滚日志文件的存在将是检测到日志,将自动播放日志以恢复数据库在不完整事务开始时的状态。

只有在回滚日志存在且包含有效的标头。因此,事务可以在一个三种方式中的一种:

  1. 可以删除回滚日志文件,
  2. 回滚日志文件可以截断为零长度,或者
  3. 回滚日志的标头可以用覆盖无效的标题文本(例如,全部为零)。

这三种提交事务的方式对应于DELETE,的TRUNCATE和PERSIST设置日志模式杂注.

有效的回滚日志以以下格式的标头开头:

回滚日记账标题格式
抵消大小描述
08标题字符串:0xd9、0xd5、0x05、0xf9、0x20、0xa1、0x63、0xd7
84“页面计数”-下一段中的页面数日记账,或-1到表示文件末尾的所有内容
124校验和的随机nonce
164数据库的初始大小(以页面为单位)
204写入此命令的进程假定的磁盘扇区大小日记账。
244此日志中的页面大小。

回滚日志标头用零填充,大小为单个扇区(由偏移量20处的扇区大小整数定义)。收割台本身位于扇区中,因此如果在写入扇区时,标题后面的信息将是(希望)没有损坏。

在页眉和零填充之后是零个或多个页面记录。每个页面记录存储数据库文件中页面内容的副本在它被改变之前。同一页不能出现多次在单个回滚日志中。要回滚不完整的事务、进程只需要从头到尾读取回滚日志,并且将日志中找到的页面写回位于适当的位置。

让数据库页面大小(偏移量24处的整数值在日记账标题中)为N。那么页面记录的格式如下:

回滚日记页记录格式
抵消大小描述
04数据库文件中的页码
4N个交易开始前页面的原始内容
N+4号4校验和

校验和是一个无符号32位整数,计算如下:

  1. 将校验和初始化为偏移量12处的轴颈标题。
  2. 将索引X初始化为N-200(其中N是数据库页面的大小以字节为单位。
  3. 将偏移量X处的字节解释为8位无符号整数并将该整数的值添加到校验和中。
  4. X减去200。
  5. 如果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始终与数据库位于同一目录中文件,与数据库文件同名,但包含字符串"-沃尔”追加。

4.1.WAL文件格式

A类WAL文件由一个标题后跟零个或多个“帧”组成。每个框架记录来自数据库文件。数据库的所有更改都通过写入框架进入WAL。当写入满足以下条件的帧时提交事务包含提交标记。单个WAL可以而且通常会记录多个事务。WAL的内容定期为在名为“检查点”。

单个WAL文件可以重复使用多次。换句话说WAL可以填充框架,然后进行检查,然后新建帧可以覆盖旧的帧。WAL总是从一开始就增长最后。连接到每个帧的校验和和计数器是用于确定WAL中哪些帧是有效的是之前检查点的残留物。

WAL标头的大小为32字节,由以下八个字节组成big-endian 32位无符号整数值:

WAL标题格式
抵消大小描述
04神奇数字。0x377f0682或0x377f 0683
44文件格式版本。目前为3007000。
84数据库页面大小。示例:1024
124检查点序列号
164Salt-1:随每个检查点递增的随机整数
204Salt-2:每个检查点的不同随机数
244校验和-1:标头前24个字节的校验和的第一部分
284校验和2:头的前24个字节上校验和的第二部分

紧跟在wal-header后面的是零帧或更多帧。每个帧由一个24字节的帧头和一个页面大小字节共页数据。帧标头是六个大字符32位无符号整数值,如下所示:

WAL帧标题格式
抵消大小描述
04页码
44对于提交记录,数据库文件的大小(以页为单位)提交后。对于所有其他记录,为零。
84从WAL标题复制的Salt-1
124从WAL标题复制的Salt-2
164Checksum-1:累计校验和(包括本页)
204校验和2:累计校验和的后半部分。

当且仅当下列条件满足时,框架才被视为有效正确:

  1. 帧标头中的salt-1和salt-2值匹配wal-header中的盐值

  2. 帧头最后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的数据结构以加快搜索特定页面的框架。

4.6.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联合技术公司