SQLite是一个开源的、内嵌式的关系型数据库。它最初发布于2000年,在便携性、易用性、紧凑性、有效性和可靠性方面有突出的表现。
SQLite的历史:
从某个角度来说,SQLite最初的构思是在一条军舰上进行的。SQLite的作者D. RichardHipp当时正在为美国海军编制一种使用在导弹驱逐舰上的程序。那个程序最初是运行在Hewlett-Packard Unix (HPUX)上,后台使用Informix数据库。对那个程序来说,Informix有点儿太强大了。一个有经验的数据库管理员(DBA)可能需要一整天来对它进行安装和升级,如果没经验,这个工作就可能永远也做不完了。
2000年一月,Hipp开始和一个同事讨论关于创建一个简单的内嵌式SQL数据库的想法,这个数据库将使用GNU DBM B-Treelibrary (gdbm)做后台,同时这个数据库将不需要安装和管理支持。后来,当有些空闲时间时,Hipp就开始实施这项工作,并在2000年的八月份发布了SQLite的1.0版。
按照原定计划,SQLite 1.0用gdbm来做存储管理。但后来,Hipp很快就换成了自己的B-tree,以支持事务和记录按主键的存储。随着最初的升级,SQLite在功能和用户数上都得到了稳步的发展。在2001年中期,很多项目——开源的或商业的——都开始使用SQLite。在那以后的几年中,开源社区的其他成员开始为他们喜欢的程序设计语言编写SQLite扩展。SQLite的ODBC接口可以为Perl、Python、Ruby、Java和其它主流的程序设计语言提供支持,这证明了SQLite有广阔的应用前景。
² 内嵌式数据库
SQLite是一个内嵌式的数据库。
数据库服务器就在你的程序中,其好处是不需要网络配置和管理。数据库的服务器和客户端运行在同一个进程中。这样可以减少网络访问的消耗,简化数据库管理,使你的程序部署起来更容易。所有需要你做的都已经和你的程序一起编译好了。
如图1-1所示。一个Perl脚本、一个标准C/C++程序和一个使用PHP编写的Apache进程都使用SQLite。Perl脚本导入DBI::SQLite模板,并通过它来访问C API。PHP采用与C相似的方式访问C API。总之,它们都需要访问CAPI。尽管它们每个进程中都有独立的数据库服务器,但它们可以操作相同的数据库文件。SQLite利用操作系统功能来完成数据的同步和加锁。
图1-1 内嵌的主进程中的SQLite
目前市场上有多种为内嵌应用所设计的关系型数据库产品,如Sybase SQL Anywhere、InterSystems Caché、Pervasive PSQL和微软的Jet Engine。有些厂家从他们的大型数据库产品翻新出内嵌式的变种,如IBM的DB2Everyplace、Oracle的10g和微软的SQL Server Desktop Engine。开源的数据库MySQL和Firebird都提供内嵌式的版本。在所有这些产品中,仅有两个是完全开放源代码的且不收许可证费用——Firebird和SQLite。在这两个当中,仅有一个是专门为内嵌式应用设计的——SQLite。
² 体系结构
SQLite拥有一个精致的、模块化的体系结构,并引进了一些独特的方法进行关系型数据库的管理。它由被组织在3个子系统中的8个独立的模块组成,如图1-2所示。这个模型将查询过程划分为几个不连续的任务,就像在流水线上工作一样。在体系结构栈的顶部编译查询语句,在中部执行它,在底部处理操作系统的存储和接口。
图1-2 SQLite的体系结构
(1)Interface
接口由SQLite C API组成,也就是说不管是程序、脚本语言还是库文件,最终都是通过它与SQLite交互的(我们经常使用的ODBC/JDBC最后也会转化为相应C API的调用)。
(2)Compiler
编译过程从分词器(Tokenizer)和分析器(Parser)开始。它们协作处理文本形式的结构化查询(Structured QueryLanguage, SQL)语句,分析其语法有效性,转化为底层能更方便处理的层次数据结构——语法树,然后把语法树传给代码生成器(code generator)进行处理。SQLite分词器的代码是手工编写的,分析器代码是由SQLite定制的分析器生成器(称为Lemon)生成的。The Lemon parser generator isdesigned for high performance and takes special precautions to guard againstmemory leaks. 一旦SQL语句被分解为串值并组织到语法树中,分析器就将该树下传给代码生成器进行处理。而代码生成器根据它生成一种SQLite专用的汇编代码,最后由虚拟机(VirtualMachine)执行。
(3)Virtual Machine
架构中最核心的部分是虚拟机,或者叫做虚拟数据库引擎(VirtualDataBase 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程序,每次执行一条指令。
(4)后端(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能够更快地工作。