SQLite的架构(The Architecture Of SQLite)
官方介绍:http://www.sqlite.org/arch.html
最近要研究SQLite,学习过程中翻译了这篇官网的架构介绍,希望对其他想要学习的人有所帮助,由于本人能力有限,若有任何不合适的地方,欢迎提醒与评论以便更加完善。
SQLite的组件与它们之间的关系
介绍(Introduction)
此文档描述了SQLite的架构,对那些想要理解与修改SQLite原理的提供帮助。
上述框图表明了SQlite的主要组件和他们之间的联系。下面的文本将对这些组件提供一个快速的概述。
接口(Interface)
SQLite的很多对外接口的实现函数在main.c,legacy.c和vdbeapi.c中。还有一些散落在其他文件中,sqlite3_get_table()函数实现在table.c,sqlite3_mprintf()函数在printf.c,sqlite3_complete()在tokenize.c中。SQLite的更多接口请访问available separately(http://www.sqlite.org/capi3ref.html)。
为了不和其他代码产生命名冲突,所有的SQLite API命名以sqlite3_为前缀。
词法分析器(Tokenizer)
当一个SQL语句被执行时,接口程序把这个字符串传递给分词器。分词器的任务是把原有的字符串分成一个个单独的词法单元,并把这些词汇传递给语法分析器。分词器的代码存储在tokenizer.c中。
语法分析器(Parser)
语法分析器根据上下文对词法分析出来的词法单元进行语法分析,生成语法树。SQLite的语法分析器是由Lemon LALR(1) parser generator产生的,这个语法分析器是可重入的并且线程安全的。Lemon定义了无终端解除程序的概念,所以即使遇到语法错误的情况,也不会发生内存泄漏的问题。
源文件在parse.c和parse.h中。
代码生成器(Code Generator)
SQL语句被解析成为语法树之后,将会调用代码生成器去产生按照SQL语句的要求工作的虚拟的机器代码,这些机器代码可以在虚拟机中运行。
涉及的源代码:attach.c, auth.c, build.c, delete.c, expr.c, insert.c, pragma.c, select.c, trigger.c, update.c, vacuum.c and where.c。
expr.c为表达生成机器代码;where.c为WHERE(含有SELECT, UPDATE and DELETE语句)生成机器代码;attach.c, delete.c, insert.c, select.c, trigger.c update.c, and vacuum.c为相应的SQL语句生成机器代码。(每一个文件必要地调用在expr.c和where.c中)其他所有的SQL语句在build.c中进行编码。auth.c作为函数sqlite3_set_authorizer()的接口。
虚拟机(Virtual Machine)
也可以称之为虚拟数据库引擎(VDBE)。
代码生成器产生的虚拟机器码由虚拟机来运行。虚拟机是一个为操作数据库而设计的抽象的计算引擎,机器中包含了存储中间数据的存储栈(或寄存器),每条VDBE指令包含一个操作码和三个操作数。
在很多方面上,虚拟机数据库引擎是SQLite的核心部分,所有位于它上层的模块的工作是为了生成VDBE程序,VDBE执行该程序,而位于其下层的模块的工作则是完成数据的存储及查找。
虚拟机本身文件存在于vdbe.c。
也有它自己的头文件:vdbe.h,定义了在虚拟机与其它SQLite库之间的接口;vdbeInt.h定义了虚拟机的私有文件;vdbeaux.h包含了被虚拟机使用的工具程序与剩余的去构造虚拟机程序的库文件的接口模块。
vdbeapi.c文件包含了对虚拟机外部的接口,例如sqlite_bind...系列函数。
独立的类型(如strings,integer,floating,point numbers,BLOBs)被存储在名为“Mem”的内部对象中,被vdbemem.c实现。
SQLite接口SQL函数使用回调到C语言的方法。甚至内置SQL函数。大多数的内置SQL函数(如coalesce(), count(), substr(), and so forth)存储在func.c中。日期和时间转换函数存储在date.c中。
B树(B-Tree)
SQLite使用B树来实现数据库文件的存储,源码存储于btree.c之中。数据库中的每一个表和索引都使用一个单独的B树(表结构采用B+树,索引结构采用B-树),所有的B树都存放在一个磁盘文件中。文件格式的细节在btree.c的开始部分有一个详细的解释。
B树子系统的接口在btree.h中被定义。
页面高速缓存(Page Cache)
B树模块使用固定的块大小从磁盘中请求信息。默认的块大小是1024 bytes,但是会在512到65536 bytes之间变化。页缓存负责读,写和缓存这些块。页缓存也提供了回滚和原子提交的功能抽象和数据库文件的锁操作。B树运行时要从页面缓存中请求特殊的页,并且当它想要修改、提交或回滚改变操作时需要通知页面缓存,页缓存处理所有的繁琐细节,确保请求被快速、安全和高效地处理。
实现页面缓存的源代码包含在pager.c中。页面缓存的接口被定义在pager.h中。
(数据库是按照B树的结构在磁盘上进行存储操作的,通过可调整的页面缓存(pager)可以获得对数据的快速查找和存储。)
OS接口程序(OS Interface)
为了在不同的操作系统中提供可移植性,SQLite的OS接口程序提供了一个用于隐藏不同模块间差异的抽象层,OS抽象层的接口被定义在os.h中,每一个支持的操作系统有它自己的实现(os_unix.c对应Unix,os_win.c对应Windows等等),每一个操作细节的实现通常有它自己的头文件:os_unix.h,os_win.h等等。
工具程序(Utilities)
工具程序负责内存分配、字符串比较、编码转换等工作,该模块基本上可以当作一个回收站模块供其它模块共享使用。测试码负责大量的回归测试,是SQLite提供可靠性的重要保证。
内存分配和字符串比较实现的源代码在util.c中。
被语法分析器parser使用的符号表使用hash表维持,源代码为hash.c。
utf.c源代码包含编码转换子程序。
SQLite有它自己的私有函数printf()在printf.c中,并且有自己的随机数字生成器在random.c中
测试代码(Test Code)
如果算上回归测试脚本,超过一半的SQLite的代码是用来专门测试。在主要的代码文件中有大量的assert()声明。另外,源文件test1.c到test5.c连同md5.c实现扩展仅用于测试目的。os_test.c后端接口是用来模拟断电情况,来验证在寻呼机中的崩溃恢复机制。
个人理解一下:该文档详细介绍了SQLite的架构结构,看完文章我们会发现其实整个系统核心分为三大子系统:
“前段(SQL编译器)” “中层(虚拟机)” “后端(存取、缓冲、操作系统抽象)”