编者注:本文档撰写于 2004 年,旨在指导从 SQLite2 迁移到 SQLite3 的程序员。它保留为 SQLite 历史记录的一部分。现代程序员应参考本网站其他位置提供的关于 SQLite 的更新文档。 |
SQLite 版本 3.0 对库引入了重大更改,包括
本文档简要介绍了 SQLite 3.0 中的更改,适用于已经熟悉 SQLite 版本 2.8 的用户。
SQLite 版本 2.8 将继续得到支持,并在可预见的未来修复错误。为了使 SQLite 版本 2.8 和 SQLite 版本 3.0 可以和平共处,SQLite 版本 3.0 中关键文件和 API 的名称已更改为包含字符“3”。例如,C 程序使用的包含文件已从“sqlite.h”更改为“sqlite3.h”。与数据库交互的 shell 程序名称已从“sqlite.exe”更改为“sqlite3.exe”。通过这些更改,可以在同一系统上同时安装 SQLite 2.8 和 SQLite 3.0。并且,同一个 C 程序可以同时链接到 SQLite 2.8 和 SQLite 3.0,并同时使用这两个库。
SQLite 数据库文件使用的格式已完全修改。旧的 2.1 版本格式和新的 3.0 版本格式彼此不兼容。SQLite 版本 2.8 无法读取 3.0 版本的数据库文件,而 SQLite 3.0 版本也无法读取 2.8 版本的数据库文件。
要将 SQLite 2.8 数据库转换为 SQLite 3.0 数据库,请准备好 2.8 和 3.0 版本的命令行 shell。然后输入类似以下的命令
sqlite OLD.DB .dump | sqlite3 NEW.DB
新的数据库文件格式对表使用 B+树。在 B+树中,所有数据都存储在树的叶子节点中,而不是同时存储在叶子节点和中间分支节点中。对表使用 B+树可以实现更好的可扩展性,并且可以在不使用溢出页的情况下存储更大的数据字段。传统的 B 树仍然用于索引。
新的文件格式还支持 512 到 65536 字节之间的可变页大小。页面大小存储在文件头中,因此理论上,同一个库可以读取具有不同页面大小的数据库,尽管此功能尚未在实践中实现。
新的文件格式省略了其磁盘映像中的未使用字段。例如,索引仅使用 B 树记录的键部分,而不使用数据。因此,对于索引,记录数据长度的字段将被省略。诸如键和数据长度之类的整数值使用可变长度编码存储,因此仅需一个或两个字节即可存储最常见的情况,但如果需要,最多可以编码 64 位信息。整数和浮点数数据以二进制形式存储在磁盘上,而不是像 SQLite 版本 2.8 中那样转换为 ASCII。这些变化综合起来导致数据库文件的大小通常比 SQLite 版本 2.8 中的等效文件小 25% 到 35%。
SQLite 版本 3.0 中使用的低级 B 树格式的详细信息可以在 btreeInt.h 源文件的头注释以及 文件格式 文档中找到。
SQLite 版本 2.8 将在内部处理各种格式的数据,但在写入磁盘或通过其 API 交互时,SQLite 2.8 始终将数据转换为 ASCII 文本。相比之下,SQLite 3.0 将其内部数据表示暴露给用户,并在适当的情况下将二进制表示存储到磁盘。添加非 ASCII 表示的暴露是为了支持 BLOB。
SQLite 版本 2.8 具有以下功能:任何类型的数据都可以存储在任何表列中,无论该列声明的类型是什么。此功能在 3.0 版本中得以保留,不过略有修改。每个表列将存储任何类型的数据,尽管列对其声明的数据类型定义的数据格式具有亲和力。当数据插入列时,该列将尝试将数据格式转换为列的声明类型。所有 SQL 数据库引擎都执行此操作。不同之处在于 SQLite 3.0 即使格式转换不可行,仍将存储数据。
例如,如果您有一个声明为“INTEGER”类型的表列,并且您尝试插入一个字符串,该列将查看文本字符串并查看它是否像一个数字。如果该字符串看起来像一个数字,它将被转换为一个数字,如果该数字没有小数部分,则将其转换为整数,并以这种方式存储。但如果该字符串不是格式良好的数字,它仍然以字符串形式存储。类型为“TEXT”的列尝试将数字转换为 ASCII 文本表示形式后再存储它们。但 BLOB 作为 BLOB 存储在 TEXT 列中,因为您通常无法将 BLOB 转换为文本。
在大多数其他 SQL 数据库引擎中,数据类型与存储数据的表列相关联 - 与数据容器相关联。在 SQLite 3.0 中,数据类型与数据本身相关联,而不是与它的容器相关联。Paul Graham 在他的著作 ANSI Common Lisp 中将此属性称为“显式类型”。其他作者对“显式类型”一词有不同的定义,因此请注意混淆。但无论用什么名字,这就是 SQLite 3.0 支持的数据类型模型。
有关 SQLite 版本 3.0 中数据类型的更多信息,请参阅 此处。
SQLite 3.0 的新 API 包含接受文本作为 UTF-8 和 UTF-16 的例程,分别以主机机的本机字节顺序表示。每个数据库文件都将文本管理为 UTF-8、UTF-16BE(大端)或 UTF-16LE(小端)。在内部和磁盘文件中,无论在何处都使用相同文本表示。如果数据库文件(在文件头中)指定的文本表示与接口例程所需的文本表示不匹配,则文本会即时转换。不断将文本从一种表示转换为另一种表示在计算上可能很昂贵,因此建议程序员选择一种表示并坚持使用它,贯穿整个应用程序。
在 SQLite 的当前实现中,SQL 解析器仅使用 UTF-8 文本。因此,如果提供 UTF-16 文本,它将被转换。这只是一个实现问题,没有任何东西可以阻止 SQLite 的未来版本以原生方式解析 UTF-16 编码的 SQL。
在创建新的用户定义的 SQL 函数和排序规则时,每个函数或排序规则都可以指定它是使用 UTF-8、UTF-16be 还是 UTF-16le。可以为每种编码注册单独的实现。如果需要 SQL 函数或排序规则,但当前文本编码版本不可用,则文本将自动转换。如前所述,这种转换需要计算时间,因此建议程序员选择一种编码并坚持使用它,以最大程度地减少不必要的格式转换。
SQLite 对它接收的文本并不挑剔,它很乐意处理未规范化甚至格式良好的 UTF-8 或 UTF-16 的文本字符串。因此,想要存储 ISO8859 数据的程序员可以使用 UTF-8 接口来实现。只要不尝试使用 UTF-16 排序规则或 SQL 函数,文本的字节序列就不会以任何方式修改。
排序规则只是对文本定义的顺序。当 SQLite 3.0 对文本进行排序(或使用“<”或“>=”之类的比较运算符)时,排序顺序首先由数据类型确定。
排序规则用于比较两个文本字符串。排序规则不会改变 NULL、数字或 BLOB 的排序,只会改变文本的排序。
排序规则的实现是一个函数,它以要比较的两个字符串作为输入,如果第一个字符串小于、等于或大于第二个字符串,则返回负数、零或正数。SQLite 3.0 带有一个名为“BINARY”的内置排序规则,它使用标准 C 库中的 memcmp() 例程实现。BINARY 排序规则适用于英文文本。对于其他语言或区域设置,可能更喜欢其他排序规则。
要使用哪个排序规则的决定由 SQL 中的 COLLATE 子句控制。COLLATE 子句可以出现在表定义中,为表列定义默认排序规则,也可以出现在索引字段中,或者出现在 SELECT 语句的 ORDER BY 子句中。计划对 SQLite 的增强功能包括包含标准 CAST() 语法,以允许定义表达式的排序规则。
表的每一行都有一个唯一的 rowid。如果表定义了一个类型为“INTEGER PRIMARY KEY”的列,则该列将成为 rowid 的别名。但是,无论是否有 INTEGER PRIMARY KEY 列,每一行仍然具有 rowid。
在 SQLite 版本 3.0 中,rowid 是一个 64 位有符号整数。这是 SQLite 版本 2.8 的扩展,它只允许 32 位的 rowid。
为了最大程度地减少存储空间,64 位 rowid 存储为可变长度整数。0 到 127 之间的 rowid 仅使用一个字节。0 到 16383 之间的 rowid 仅使用 2 个字节。高达 2097152 使用三个字节。依此类推。允许使用负 rowid,但它们始终使用九个字节的存储空间,因此不建议使用。当 rowid 由 SQLite 自动生成时,它们始终是非负的。
SQLite 版本 2.8 允许多个同时读取器或单个写入器,但不能同时存在两者。SQLite 版本 3.0 允许一个进程开始写入数据库,而其他进程继续读取。写入器仍必须在短暂的时间内获得数据库的独占锁才能提交其更改,但整个写入操作不再需要独占锁。有关 SQLite 版本 3.0 锁定行为的更详细报告,请参阅 此处。
现在,SQLite 中也提供了有限形式的表级锁定。如果每个表都存储在单独的数据库文件中,则这些单独的文件可以附加到主数据库(使用 ATTACH 命令),并且组合的数据库将作为一个数据库运行。但是,锁仅在需要时才对单个文件进行获取。因此,如果您将“数据库”重新定义为两个或多个数据库文件,那么两个进程完全有可能同时写入同一个数据库。为了进一步支持此功能,涉及两个或多个附加数据库的事务提交现在是原子的。
SQLite 版本 3.0 的实现部分得益于 AOL 开发人员对优秀开源软件的支持和拥抱。
本页最后修改于 2023-10-10 17:29:48 UTC