编者注: 本文档编写于 2004 年,旨在为从 SQLite2 过渡到 SQLite3 的程序员提供指南。它作为 SQLite 历史记录的一部分保留下来。现代程序员应该参考本网站其他地方提供的有关 SQLite 的最新文档。

SQLite 版本 3 概述

SQLite 3.0 版对库进行了重要更改,包括:

本文档是为已经熟悉 SQLite 2.8 版本的用户快速介绍 SQLite 3.0 的变化。

命名变化

在可预见的未来,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 格式互不兼容。2.8 版的 SQLite 不会读取 3.0 版的数据库文件,3.0 版的 SQLite 也不会读取 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 源文件的标题注释和文件格式文档中找到。

清单类型和 BLOB 支持

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 版中数据类型的其他信息可 单独获得。

支持 UTF-8 和 UTF-16

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() 语法,以允许定义表达式的整理顺序。

64 位 ROWID

表的每一行都有一个唯一的rowid。如果表定义了类型为“INTEGER PRIMARY KEY”的列,那么该列将成为 rowid 的别名。但是无论有没有 INTEGER PRIMARY KEY 列,每一行仍然有一个 rowid。

在 SQLite 3.0 版本中,rowid 是一个 64 位有符号整数。这是 SQLite 2.8 版的扩展,它只允许 32 位的 rowids。

为了最小化存储空间,将 64 位 rowid 存储为可变长度整数。0 到 127 之间的 rowids 只使用一个字节。0 到 16383 之间的 rowids 只使用 2 个字节。最多 2097152 使用三个字节。等等。允许使用负 rowid,但它们总是使用九个字节的存储空间,因此不鼓励使用它们。当 rowid 由 SQLite 自动生成时,它们将始终为非负数。

改进的并发性

SQLite 2.8 版允许多个同时读取器或单个写入器,但不能同时使用两者。SQLite 3.0 版允许一个进程开始写入数据库,而其他进程继续读取。写入者仍必须在短暂的时间间隔内获得数据库的独占锁才能提交其更改,但整个写操作不再需要独占锁。有关 SQLite 3.0 版锁定行为的更详细报告可单独获得。

SQLite 现在也提供了一种有限形式的表级锁定。如果每个表都存储在一个单独的数据库文件中,那么这些单独的文件可以附加到主数据库(使用 ATTACH 命令)并且合并后的数据库将作为一个数据库运行。但是只会根据需要在单个文件上获取锁。因此,如果您将“数据库”重新定义为两个或多个数据库文件,那么两个进程同时写入同一个数据库是完全可能的。为了进一步支持此功能,涉及两个或多个 ATTACHed 数据库的事务提交现在是原子的。

学分

SQLite 3.0 版在一定程度上是由 AOL 开发人员支持和拥抱优秀的开源软件实现的。