第1章 SQLite介绍
SQLite是一个开源的、内嵌式的关系型数据库。它最初发布于2000年,在便携性、易用性、紧凑性、有效性和可靠性方面有突出的表现。
内嵌式数据库
SQLite是一个内嵌式的数据库。
数据库服务器就在你的程序中,其好处是不需要网络配置和管理。数据库的服务器和客户端运行在同一个进程中。这样可以减少网络访问的消耗,简化数据库管理,使你的程序部署起来更容易。所有需要你做的都已经和你的程序一起编译好了。
如图1-1所示。一个Perl脚本、一个标准C/C++程序和一个使用PHP编写的Apache进程都使用SQLite。Perl脚本导入DBI::SQLite模板,并通过它来访问C API。PHP采用与C相似的方式访问C API。总之,它们都需要访问C API。尽管它们每个进程中都有独立的数据库服务器,但它们可以操作相同的数据库文件。SQLite利用操作系统功能来完成数据的同步和加锁。
图1-1 内嵌的主进程中的SQLite
目前市场上有多种为内嵌应用所设计的关系型数据库产品,如Sybase SQL Anywhere、InterSystems Caché、Pervasive PSQL和微软的Jet Engine。有些厂家从他们的大型数据库产品翻新出内嵌式的变种,如IBM的DB2 Everyplace、Oracle的10g和微软的SQL Server Desktop Engine。开源的数据库MySQL和Firebird都提供内嵌式的版本。在所有这些产品中,仅有两个是完全开放源代码的且不收许可证费用——Firebird和SQLite。在这两个当中,仅有一个是专门为内嵌式应用设计的——SQLite。
开发者的数据库
SQLite具有多方面的特性。它是一个数据库,一个程序库,一个命令行工具,也是一个学习关系型数据库的很好的工具。确实有很多途径可以使用它——内嵌环境、网站、操作系统服务、脚本语言和应用程序。对于程序员来说,SQLite就象一个数据传送带,提供了一种方便的将应用程序绑定的数据的方法。就象传送带一样,对SQLite的使用没有终点。
除了仅仅作为一个存储容器,SQLite还可以作为一个单纯的数据处理的工具。如果大小和复杂性合适,使用SQLite可以很容易地将应用程序所使用的数据结构转化为表,并保存在一个内在数据库中。用此方法,你可以操作互相关联的数据,可以完成很繁重的任务页不必写自己的算法来对数据结构操作和排序。如果你是一个程序员,想像一下在你的程序中自行完成下面SQL语句所代表的工作需要多少代码:
SELECT AVG(z-y) FROM table GROUP BY x
HAVING x > MIN(z) OR x < MAX(y)
ORDER BY y DESC LIMIT 10 OFFSET 3;
SQLite还是一个很好的学习程序设计的工具,通过它可以研究很多计算机科学的课题。分析器、分词器、虚拟机、Btree算法、高整缓存、程序体系结构,通过这些内容可以搞清楚很多计算机科学的经典概念。SQLite的模块化、小型化和简易性,使你可以很容易地专门研究其中的一个问题。
管理员的数据库
SQLite不仅是程序员的数据库,它对系统管理员也很有用。它很小、紧凑而精致,就像一些Unix的常用工具,如find、rsync或grep。SQLite提供了命令行工具供用户交互操作。
另外,对于关系型数据库的初学者来说,SQLite是一个学习各种关系相关概念的方便的学习工具。它可以很快很容易地安装在各类操作系统中,它的数据库文件可以自由共享页不需要任何转换。它具有关系型数据库的各种特色而又不令人生畏。它的程序和数据库文件仅用U盘就能传递。
SQLite 的历史
从某个角度来说,SQLite最初的构思是在一条军舰上进行的。SQLite的作者D. Richard Hipp当时正在为美国海军编制一种使用在导弹驱逐舰上的程序。那个程序最初是运行在Hewlett-Packard Unix (HPUX)上,后台使用Informix数据库。对那个程序来说,Informix有点儿太强大了。一个有经验的数据库管理员(DBA)可能需要一整天来对它进行安装和升级,如果没经验,这个工作就可能永远也做不完了。
2000年一月,Hipp开始和一个同事讨论关于创建一个简单的内嵌式SQL数据库的想法,这个数据库将使用GNU DBM B-Tree library (gdbm)做后台,同时这个数据库将不需要安装和管理支持。后来,当有些空闲时间时,Hipp就开始实施这项工作,并在2000年的八月份发布了SQLite的1.0版。
按照原定计划,SQLite 1.0用gdbm来做存储管理。但后来,Hipp很快就换成了自己的B-tree,以支持事务和记录按主键的存储。随着最初的升级,SQLite在功能和用户数上都得到了稳步的发展。在2001年中期,很多项目——开源的或商业的——都开始使用SQLite。在那以后的几年中,开源社区的其他成员开始为他们喜欢的程序设计语言编写SQLite扩展。SQLite的ODBC接口可以为Perl、Python、Ruby、Java和其它主流的程序设计语言提供支持,这证明了SQLite有广阔的应用前景。
2004年,SQLite从版本2升级到版本3,这是一次大升级。主要目的是增加内置的对UTF-8、UTF-16及用户定义字符集的支持。While 3.0 was originally slated for release in summer 2005, America Online provided the necessary funding to see that it was completed by July 2004. 除国际化功能外,版本3的其它新特性包括:经过修补的C API,更紧凑的数据库文件格式(比原来节省25%的空间),弱类型,大二进制对象(BLOB)的支持,64-bit的ROWID,autovacuum和改进了的并发控制。尽管增加了这一系列新特性,版本3的运行库仍然小于240K字节。Another improvement in version 3 was a good code cleanup—revisiting and rewriting, or otherwise throwing out extraneous stuff accumulated in the 2.x series.
SQLite持续增长并始终坚持其最初的设计目标:简单、弹性、紧凑、速度和彻底的易用。本书出版时,SQLite已经增加了CHECK约束,下面就要增加外键约束,再下面呢?
谁使用SQLite
当前,SQLite已经被多种软件和产品所使用。它被用在Apple的Mac OS X操作系统中,被用作其CoreData应用程序架构的一部分。它还应用于Safari的Web浏览器、Mail.app的电子邮件程序、RSS的管理、Apple的Aperture照片软件。
尽管SQLite很少做广告,但它还是被用在了多种消费类产品中。
体系结构
SQLite拥有一个精致的、模块化的体系结构,并引进了一些独特的方法进行关系型数据库的管理。它由被组织在3个子系统中的8个独立的模块组成,如图1-2所示。这个模型将查询过程划分为几个不连续的任务,就像在流水线上工作一样。在体系结构栈的顶部编译查询语句,在中部执行它,在底部处理操作系统的存储和接口。
图1-2 SQLite的体系结构
接口(Interface)
接口由SQLite C API组成,也就是说不管是程序、脚本语言还是库文件,最终都是通过它与SQLite交互的(我们经常使用的ODBC/JDBC最后也会转化为相应C API的调用)。
编译器(Compiler)
编译过程从分词器(Tokenizer)和分析器(Parser)开始。它们协作处理文本形式的结构化查询(Structured Query Language, SQL)语句,分析其语法有效性,转化为底层能更方便处理的层次数据结构——语法树,然后把语法树传给代码生成器(code generator)进行处理。SQLite分词器的代码是手工编写的,分析器代码是由SQLite定制的分析器生成器(称为Lemon)生成的。The Lemon parser generator is designed for high performance and takes special precautions to guard against memory leaks. 一旦SQL语句被分解为串值并组织到语法树中,分析器就将该树下传给代码生成器进行处理。而代码生成器根据它生成一种SQLite专用的汇编代码,最后由虚拟机(Virtual Machine)执行。
虚拟机(Virtual Machine)
架构中最核心的部分是虚拟机,或者叫做虚拟数据库引擎(Virtual DataBase Engine,VDBE)。它和Java虚拟机相似,解释执行字节代码。VDBE的字节代码(称为虚拟机语言)由128个操作码(opcodes)构成,主要是进行数据库操作。它的每一条指令或者用来完成特定的数据库操作(比如打开一个表的游标、开始一个事务等),或者为完成这些操作做准备。总之,所有的这些指令都是为了满足SQL命令的要求。VDBE的指令集能满足任何复杂SQL命令的要求。所有的SQLite SQL语句——从选择和修改记录到创建表、视图和索引——都是首先编译成此种虚拟机语言,组成一个独立程序,定义如何完成给定的命令。例如,在SQLite的CLP中执行下面语句:
sqlite> .m col
sqlite> .h on
sqlite> .w 4 15 3 3 3 10 3
sqlite> explain SELECT name FROM episodes LIMIT 10;
SQLite会显示编译后的VDBE汇编程序,如列表1-1所示。
列表1-1 VDBE汇编程序
addr opcode p1 p2 p3 p4 p5 comment
---- --------------- --- --- --- --------------- ---- ----------
0 Trace 0 0 0 00
1 Integer 10 1 0 00
2 MustBeInt 1 0 0 00
3 IfZero 1 13 0 00
4 Goto 0 14 0 00
5 OpenRead 0 2 0 3 00
6 Rewind 0 12 0 00
7 Column 0 2 2 00
8 ResultRow 2 1 0 00
9 AddImm 1 -1 0 00
10 IfZero 1 12 0 00
11 Next 0 7 0 01
12 Close 0 0 0 00
13 Halt 0 0 0 00
14 Transaction 0 0 0 00
15 VerifyCookie 0 40 0 00
16 TableLock 0 2 0 episodes 00
17 Goto 0 5 0 00
程序由17条指令组成。通过对给定的操作数完成特别的操作,这些指令将会返回episodes表前10个记录的name字段的值。episodes表是本书示例数据库的一部分。
从多个方面都可以看出,VDBE是SQLite的核心:它上面的各模块都是用于创建VDBE程序,它下面的各模块都是用于执行VDBE程序,每次执行一条指令。
后端(Back-end)
后端由B-tree、页缓冲(page cache,pager)和操作系统接口(即系统调用)构成。B-tree和page cache共同对数据进行管理。它们操作的是数据库页,这些页具有相同的大小,就像集装箱。页里面的“货物”是表示信息的大量bit,这些信息包括记录、字段和索引入口等。B-tree和pager都不知道信息的具体内容,它们只负责“运输”这些页,页不关心这些“集装箱”里面是什么。
B-tree的主要功能就是索引,它维护着各个页之间的复杂的关系,便于快速找到所需数据。它把页组织成树型的结构(这是它名称的由来),这种树是为查询而高度优化了的。Page为B-tree服务,为它提供页。Pager的主要作用就是通过OS接口在B-tree和磁盘之间传递页。磁盘操作是计算机到目前为止所必须做的最慢的事情。所以,pager 尽力提高速度,其方法是把经常使用的页存放到内存当中的页缓冲区里,从而尽量减少操作磁盘的次数。它使用特殊的算法来预测下面要使用哪些页,从而使B-tree能够更快地工作。
工具和测试代码(Utilities and Test Code)
工具模块中包含各种各样的实用功能,还有一些如内存分配、字符串比较、Unicode转换之类的公共服务也在工具模块中。这个模块就是一个包罗万象的工具箱,很多其它模块都需要调用和共享它。
测试模块中包含了无数的回归测试语句,用来检查数据库代码的每个细微角落。这个模块是SQLite性能如此可靠的原因之一。
SQLite 的特色
尽管SQLite是如此之小,却提供了如此之多的特色和性能。它支持ANSI SQL92的一个大子集(包括事务、视图、检查约束、关联子查询和复合查询等),还支持其它很多关系型数据库的特色,如触发器、索引、自动增长字段和LIMIT/OFFSET子句等。SQLite还有很多独特的特色,如内在数据库、动态类型和冲突解决(下面解释)。
如本章开始时所述,在SQLite的观念和实现中,都遵循着一系列指导原则。下面就来详述这些原则。
零配置
从SQLite的设计之始,就没准备在应用时使用DBA。配置和管理SQLite就像得到它一样简单。SQLite包含了正好适合于一个程序员的脑筋的特色。
兼容性
SQLite在设计时特别注意了兼容性。它可以编译运行在Windows、Linux、BSD、Mac OS X及商用的Unix 系统如Solaris、HPUX和AIX,还可以应用于很多嵌入式平台如QNX、VxWorks、Symbian、Palm OS和Windows CE。它可以无缝地工作在16-bit、32-bit和64-bit体系结构中并且能同时适应字节的大端格式和小端格式。SQLite的兼容性并不只表现在代码上,还表现在其数据库文件上。SQLite的数据库文件在其所支持的所有操作系统、硬件体系结构和字节顺序上都是二进制一致的。你可以在Sun SPARC工作站上创建一个SQLite数据库然后在Mac或Windows的机器上——甚至移动电话上——使用它,而不需要做任何转换和修改。此外,SQLite数据库可以支撑2TB的数据量(受操作系统限制),还内置地同时支持UTF-8和UTF-16编码。
紧凑性
SQLite的设计可以说是功能齐全但体积很小:1个头文件,1个库,不需要扩展的数据库服务。所有的东西,包括客户端、服务器和虚拟机等,都被打包在1/4兆大小之内。如果在编译时去掉一些不需要的特性,程序库可以缩小至170KB (在x86硬件平台上使用GNU C进行编译)。此外,还有一个SQLite的私有版本,大小是69KB,可以运行在智能卡上(参“附加信息”一节)。
空注:我下载的DLL有500多KB。
简单
作为程序库,SQLite的API可以算是最简单最易用的了。SQLite既有很好的文档又很容易望文知意。
适应性
SQLite的几个特性使其成为一个适应性极强的数据库。作为一个内嵌式的数据库,SQLite在以下两个方面都做得最好:强有力而可伸缩的关系型数据库前端,简单而紧凑的B-tree后端。
不受拘束的授权
SQLite的全部代码都在公共域中,不需要授权。SQLite的任何一部分都没有附加版权要求。所有曾经为SQLite项目贡献过代码的人都签署过一个宣誓书将他们的贡献发布到公共域。也就是说,无论你如何使用SQLite的代码都不会有法律方面的限制。你可以修改、合并、发布、出售或将这些代码用于任何目的,商业和中非商业的,不需要支付任何费用,不会受到任何限制。
可靠性
SQLite的源代码不但免费,还编写得很好。SQLite源代码包含大约30000行标准C代码,它是干净的、模块化的和完好注释的。SQLite源代码易理解、易定制。
SQLite的核心软件(库和工具)由约30000行代码组成,但分发的程序中还包含有超过30000行的回归测试代码,它们覆盖了97%的核心代码。也就是说,超过一半的SQLite项目代码是专门用于回归测试的,也就是说,差不多每写一行功能代码,都要写一行测试代码对它进行测试。
易用性
SQLite还提供一些独特的功能来提高易用性,包括动态类型、冲突解决和“附加”多个数据库到一个连接的能力。
性能和限制
SQLite是一个快速数据库。但“快速”这个词本身是一个主观的和不明确的词。诚实地讲,有些事情SQLite能比其它数据库做得快,也有些事情不能。这么说吧,利用SQLite提供的配置参数,SQLite是足够快速和高效的。跟大多数其它数据库一样,SQLite使用B-tree处理索引,使用B+tree处理表数据。因此,在对单表进行查询时,SQLite要快于(或至少相当于)其它数据库的速度。
在一些情况下SQLite可能不如大型数据库快,但大多数这些情况是可理解的。SQLite是一个内嵌式的数据库,设计用于中小规模的应用程序。这些限制是符合设计目的的。很多新用户错误地假设使用SQLite可以代替大型关系型数据库,这有时行,但有时不行,依赖于你准备用SQLite来做什么。一般情况下,SQLite在三个主要的方面具有局限性:
并发。
数据库大小。
网络。
尽管SQLite做得已经很好了,但仍有部分特性未能实现,包括:
外键约束空注:SQLite的最新版本3.6.19好像已经支持了。
完整的触发器支持。
完整的ALTER TABLE支持。
事务嵌套。
RIGHT和FULL OUTER JOIN。
可修改视图。
GRANT和REVOKE。
附加信息
SQLite网站有丰富的信息,包括官方文档、邮件列表、Wiki和其它的一般信息,它的网址是www.sqlite.org。SQLite社区也是很有帮助的,你可能从邮件列表中找到任何你所需要的东西。另外,SQLite的作者提供了SQLite的专业培训和支持,包括定制程序(如移植到嵌入式平台)和增强的SQLite版本,这些版本包括内置了加密功能的版本和为嵌入式应用优化的极小化版本。更多的信息可以从www.hwaci.com/sw/sqlite/prosupport.html中找到。