SQLite剖析之体系结构
架构(architecture)
SQLite采用了模块的设计,它由三个子系统,包括8个独立的模块构成。
1.接口(Interface)
接口由SQLite C API组成,也就是说不管是程序、脚本语言还是库文件,最终都是通过它与SQLite交互的(我们通常用得较多的ODBC/JDBC最后也会转化为相应C API的调用)。
2.编译器(Compiler)
在编译器中,分词器(Tokenizer)和分析器(Parser)对SQL进行语法检查,然后把它转化为底层能更方便处理的分层的数据结构---语法树,然后把语法树传给代码生成器(code generator)进行处理。而代码生成器根据它生成一种针对SQLite的汇编代码,最后由虚拟机(Virtual Machine)执行。
3.虚拟机(Virtual Machine)
架构中最核心的部分是虚拟机,或者叫做虚拟数据库引擎(Virtual Database Engine,VDBE)。它和Java虚拟机相似,解释执行字节代码。VDBE的字节代码由128个操作码(opcodes)构成,它们主要集中在数据库操作。它的每一条指令都用来完成特定的数据库操作(比如打开一个表的游标)或者为这些操作栈空间的准备(比如压入参数)。总之,所有的这些指令都是为了满足SQL命令的要求(关于VM,后面会做详细介绍)。
4.后端(Back-End)
后端由B-树(B-tree),页缓存(page cache,pager)和操作系统接口(即系统调用)构成。B-tree和page cache共同对数据进行管理。B-tree的主要功能就是索引,它维护着各个页面之间的复杂的关系,便于快速找到所需数据。而pager的主要作用就是通过OS接口在B-tree和Disk之间传递页面。
SQLite的体系结构
下图是一个体系结构图,显示了SQLite的主要组件以及各组件之间是如何相互关联的。
在内部,SQLite由以下几个组件组成:内核、SQL编译器、后端以及附件。SQLite通过利用虚拟机和虚拟数据库引擎(VDBE),使调试、修改和扩展SQLite的内核变得更加方便。所有SQL语句都被编译成易读的、可以在SQLite虚拟机中执行的程序集。SQLite支持大小高达2 TB的数据库,每个数据库完全存储在单个磁盘文件中。这些磁盘文件可以在不同字节顺序的计算机之间移动。这些数据以B+树(B+tree)数据结构的形式存储在磁盘上。SQLite根据该文件系统获得其数据库权限。
1、公共接口(Interface)
SQLite库的大部分公共接口由main.c, legacy.c和vdbeapi.c源文件中的函数来实现,这些函数依赖于分散在其他文件中的一些程序,因为在这些文件中它们可以访问有文件作用域的数据结构。sqlite3_get_table()例程在table.c中实现,sqlite3_mprintf()可在printf.c中找到,sqlite3_complete()则位于tokenize.c中。Tcl接口在tclsqlite.c中实现。SQLite的C接口信息可参考http://sqlite.org/capi3ref.html。
为了避免和其他软件的名字冲突,SQLite库的所有外部符号都以sqlite3为前缀,这些被用来做外部使用的符号(换句话说,这些符号用来形成SQLite的API)是以sqlite3_开头来命名的。
2、词法分析器(Tokenizer)
当执行一个包含SQL语句的字符串时,接口程序要把这个字符串传递给tokenizer。Tokenizer的任务是把原有字符串分割成一个个标识符(token),并把这些标识符传递给解析器。Tokenizer是用手工编写的,在C文件tokenize.c中。
在这个设计中需要注意的一点是,tokenizer调用parser。熟悉YACC和BISON的人们也许会习惯于用parser调用tokenizer。SQLite的作者已经尝试了这两种方法,并发现用tokenizer调用parser会使程序运行的更好。YACC会使程序更滞后一些。
3、语法分析器(Parser)
语法分析器的工作是在指定的上下文中赋予标识符具体的含义。SQLite的语法分析器使用Lemon LALR(1)分析程序生成器来产生,Lemon做的工作与YACC/BISON相同,但它使用不同的输入句法,这种句法更不易出错。Lemon还产生可重入的并且线程安全的语法分析器。Lemon定义了非终结析构器的概念,当遇到语法错误时它不会泄露内存。驱动Lemon的源文件可在parse.y中找到。
因为lemon是一个在开发机器上不常见的程序,所以lemon的源代码(只是一个C文件)被放在SQLite的"tool"子目录下。 lemon的文档放在"doc"子目录下。
4、代码生成器(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和where.c。这些文件涵盖了大部分最重要、最有意义的事情。expr.c处理SQL中表达式的代码生成。where.c处理SELECT、UPDATE和DELETE语句中WHERE子句的代码生成。文件attach.c, delete.c, insert.c, select.c, trigger.c, update.c和vacuum.c处理同名SQL语句的代码生成(这些文件在必要时都调用expr.c和where.c中的例程)。所有其他SQL语句的代码由build.c生成。文件auth.c实现sqlite3_set_authorizer()的功能。
5、虚拟机(Virtual Machine)
代码生成器生成的代码由虚拟机来执行。关于虚拟机更详细的信息可参考http://sqlite.org/opcode.html。总的来说,虚拟机实现一个专为操作数据库文件而设计的抽象计算引擎。它有一个存储中间数据的存储栈,每条指令包含一个操作码和不超过三个额外的操作数。
虚拟机本身被完整地包含在一个单独的文件vdbe.c中,它也有自己的头文件,其中vdbe.h定义虚拟机与SQLite库其他部分之间的接口,vdbeInt.h定义虚拟机私有的数据结构。文件vdbeaux.c包含被虚拟机使用的一些工具,和被库的其他部分用来构建VM程序的一些接口模块。文件vdbeapi.c包含虚拟机的外部接口,例如sqlite3_bind_...族的函数。单独的值(字符串、整数、浮点数、BLOB对象)被存储在一个叫Mem的内部对象中,在vdbemem.c中可找到它的实现。
SQLite使用回调风格的C语言程序来实现SQL函数,每个内建的SQL函数都用这种方式来实现。大多数内建的SQL函数(例如coalesce(), count(), substr(), 等等)可在func.c中找到。日期和时间转换函数可在date.c中找到。
6、B-树(B-Tree)
一个SQLite数据库使用B-树的形式存储在磁盘上,B-树的实现位于源文件btree.c中。数据库中的每个表和索引使用一棵单独的B-树,所有的B-树存放在同一个磁盘文件中。文件格式的细节被记录在btree.c开头的备注里。B-树子系统的接口在头文件btree.h中定义。
7、页面高速缓存(Page Cache)
B-树模块以固定大小的数据块形式从磁盘上请求信息,默认的块大小是1024个字节,但是可以在512和65536个字节之间变化。页面高速缓存负责读、写和缓存这些数据块。页面高速缓存还提供回滚和原子提交的抽象,并且管理数据文件的锁定。B-树驱动模块从页面高速缓存中请求特定的页,当它想修改页面、想提交或回滚当前修改时,它也会通知页面高速缓存。页面高速缓存处理所有麻烦的细节,以确保请求能够快速、安全而有效地被处理。
页面高速缓存的代码实现被包含在单一的C源文件pager.c中。页面高速缓存子系统的接口在头文件pager.h中定义。
8、OS接口
为了在POSIX和Win32操作系统之间提供移植性,SQLite使用一个抽象层来提供操作系统接口。OS抽象层的接口在os.h中定义,每种支持的操作系统有各自的实现:Unix使用os_unix.c,Windows使用os_win.c,等等。每个特定操作系统的实现通常都有自己的头文件,如os_unix.h, os_win.h等。
9、实用工具(Utilities)
内存分配和字符串比较函数位于util.c中。语法分析器使用的符号表用Hash表来维护,其实现位于hash.c中。源文件utf.c包含Unicode转换子程序。SQLite有自己的printf()实现(带一些扩展功能),在printf.c中,还有自己的随机数生成器,在random.c中。
10、测试代码(Test Code)
如果你计算回归测试脚本,超过一半的SQLite代码将被测试。主要代码文件中有许多assert()语句。另外,源文件test1.c通过test5.c和md5.c实现只用于测试目的的一些扩展。os_test.c后端接口用来模拟断电,以验证页面高速缓存的崩溃恢复机制。
SQLite Version3.3.6源代码文件结构
文件名称 | 大小byte | 备注 | |
API | main.c | 35414 | SQLite Library的大部分接口 |
legacy.c | 3734 | sqlite3_exec的实现 | |
table.c | 5464 | the sqlite3_get_table() and sqlite3_free_table()的实现,它们是sqlite3_exec的包装 | |
preprare.c | 17983 | 主要实现sqlite3_prepare() | |
分词器部分(Tokenizer) | tokenize.c | 14495 | 分词器的实现 |
语法分析器部分(Parser) | parser.c | 116917 | 分析器的实现,由Lemon实现 |
parser.h | 6847 | 分析器内部定义的关键字 | |
代码生成器(Code Generator) | update.c | 23878 | 处理UPDATTE语句 |
delete.c | 21978 | 处理DELETE语句 | |
insert.c | 62026 | 处理INSERT语句 | |
trigger.c | 29065 | 处理TRIGGER语句 | |
attach.c | 15941 | 处理ATTACHT 和DEATTACH语句 | |
select.c | 112084 | 处理SELECT语句 | |
where.c | 75826 | 处理WHERE语句 | |
vacuum.c | 11005 | 处理VACUUM语句 | |
pragma.c | 34289 | 处理PRAGMA命令 | |
expr.c | 73963 | 处理SQL语句中的表达式 | |
auth.c | 7496 | 主要实现sqlite3_set_authorizer() | |
analyze.c | 13149 | 实现ANALYZE命令 | |
alter.c | 18414 | 实现ALTER TABLE功能 | |
build.c | 104052 | 处理以下语法:CREATE TABLE, DROP TABLE, CREATE INDEX,DROP INDEX,creating ID lists,BEGIN TRANSACTION,COMMIT,ROLLBACK | |
func.c | 34335 | 实现SQL语句的函数语句 | |
date.c | 24031 | 与日期和时间转换有关的函数 | |
虚拟机(Virtual Machine) | vdbeapi.c | 23300 | 虚拟机提供上层模块调用的API实现部分 |
vdbe.c | 143552 | 虚拟机的主要实现部分 | |
vdbe.h | 5309 | 定义了VDBE的接口,VdbeOp结构体(代表一条指令) | |
vdbeaux.c | 58741 | Vdbe.h的接口的实现 | |
vdbeInt.h | 17595 | Vdbe.c的私有头文件,定义了VDBE常用的数据结构:Cursor——虚拟机中使用的游标, Mem——vdbe在内部把所有的SQL值当作一个Mem数据结构来处理,Vdbe——虚拟机数据结构 | |
vdbemem.c | 26375 | 操作”Mem”数据结构的函数 | |
vdbefifo.c | 2927 | ||
B-Tree部分 | btree.h | 5260 | 头文件,定义了B-tree提供的操作接口 |
btree.c | 215570 | B-Tree部分的主要实现,并定义了以下数据结构:Btree——Btree handler,BtCursor——使用的游标, BtLock——锁, BtShared——包含了一个打开的数据库的所有信息,MemPage——文件在内存存放在该数据结构中,aCellInfo | |
OS Interface部分 | os.h | 18355 | 定义了为上层模块提供的操作函数,并定义了以下数据结构: |
OsFile——描述一个文件 | |||
IoMethod——OsFile所支持的操作函数(对所有架构都适用的OS Interface) | |||
os.c | 2866 | 对IoMethod中的函数的包装 | |
os_win.c | 42975 | Windows平台下的OS Interface | |
os_unix.c | 60831 | Unix平台下的OS Interface | |
os_os2.c | 28451 | OS2平台下的OS Interface | |
其它部分 | utf.c | 20891 | 与UTF编码有关的函数 |
util.c | 43575 | 一些实用函数,比如: | |
sqlite3Malloc(),sqlite3FreeX() | |||
sqlite3.h | 63873 | SQLite的头文件,定义了提供给应用使用的API和数据结构。 | |
sqliteInt.h | 78886 | 定义了SQLite内部使用的接口和数据结构 | |
printf.c | 29556 | 主要实现与printf有关的函数 | |
random.c | 3078 | 随机数生成 | |
hash.c | 11896 | SQLite使用的hash表 | |
hash.h | 4033 | Hash 表头文件 |
SQLite剖析之体系结构的更多相关文章
- SQLite数据库的体系结构(翻译自sqlite.org)
$1 简介 本文档描述了SQLite库的体系结构,这些信息对那些想理解和修改SQLite的内部工作机制的人是有用的. 下图显示了SQLite的主要组成部件及其相互关系,下面的内容将描述每一 ...
- SQLite剖析之存储模型
前言 SQLite作为嵌入式数据库,通常针对的应用的数据量相对于DBMS的数据量小.所以它的存储模型设计得非常简单,总的来说,SQLite把一个数据文件分成若干大小相等的页面,然后以B树的形式来组织这 ...
- SQLite剖析之设计与概念
1.API 由两部分组成: 核心API(core API)和扩展API(extension API). 核心API的函数实现基本的数据库操作:连接数据库.处理SQL.遍历结果集.它也包括一些实用函数, ...
- SQLite剖析之事务处理技术
前言 事务处理是DBMS中最关键的技术,对SQLite也一样,它涉及到并发控制,以及故障恢复等等.在数据库中使用事务可以保证数据的统一和完整性,同时也可以提高效率.假设需要在一张表内一次插入20个人的 ...
- SQLite剖析之编程接口详解
前言 使用过程根据函数大致分为如下几个过程: sqlite3_open() sqlite3_prepare() sqlite3_step() sqlite3_column() sqlite3_fina ...
- SQLite剖析之异步IO模式、共享缓存模式和解锁通知
1.异步I/O模式 通常,当SQLite写一个数据库文件时,会等待,直到写操作完成,然后控制返回到调用程序.相比于CPU操作,写文件系统是非常耗时的,这是一个性能瓶颈.异步I/O后端是SQLit ...
- SQLite剖析之动态内存分配
SQLite通过动态内存分配来获取各种对象(例如数据库连接和SQL预处理语句)所需内存.建立数据库文件的内存Cache.保存查询结果. 1.特性 SQLite内核和它的内存分配子系统提供以下特性 ...
- SQLite剖析之锁和并发控制
在SQLite中,锁和并发控制机制都是由pager.c模块负责处理的,用于实现ACID(Atomic.Consistent.Isolated和Durable)特性.在含有数据修改的事务中,该模块将确保 ...
- SQLite剖析之临时文件、内存数据库
一.7种临时文件 SQLite中,一个数据库由单个磁盘文件构成,简化了SQLite的使用,因为移动或备份数据库只要拷贝单个文件即可.这也使得SQLite适合用作应用程序文件格式.但是,当在单个文 ...
随机推荐
- iOS基于MBProgressHUD的二次封装,一行搞定,使用超简单
MBProgressHUD的使用,临时总结了几款最常用的使用场景: 1.提示消息 用法: [YJProgressHUD showMessage:@"显示文字,1s隐藏" inVie ...
- jsp html5 video实现在线视频播放源码,支持IE6,7,8,10,11,谷歌,火狐等浏览器
jsp源码: <%@ page language="java" import="java.util.*" pageEncoding="utf-8 ...
- Flash Download Failed-"Cortex-M3"
rror:Flash Download Failed-"Cortex-M3"出现一般有两种情况: 1.SWD模式下,Debug菜单中,Reset菜单选项(Autodetect/HW ...
- Linux 下从头再走 GTK+-3.0 (五)
实践中表明,纯粹利用 gtk 函数来创建 UI 是很繁琐的事,需要编写很多代码.怎样才能快速统一的建立 UI 布局呢? 可喜的是 GTK 提供了一个 GtkBuilder 用于快速创建界面.它读取一个 ...
- Addthis使用
一.使用 https://www.addthis.com/.国外的SNS分享站点较多,适用于英文站点内容的分享.有教程. 我的测试代码如下: <html> <meta charset ...
- 【转】【收藏】LINQ学习心得分享--------(二)LINQ语法详解
原地址:http://blog.csdn.net/xuemoyao/article/details/8053444 通过上一章节的学习,相信大家已经掌握了学习LINQ的前期的准备知识.在这一节里, ...
- 扩展欧几里德算法 cogs.tk 2057. [ZLXOI2015]殉国
2057. [ZLXOI2015]殉国 ★☆ 输入文件:BlackHawk.in 输出文件:BlackHawk.out 评测插件时间限制:0.05 s 内存限制:256 MB [题目描 ...
- 分布式系统理论之Quorum机制
一,Quorum机制介绍 在分布式系统中有个CAP理论,对于P(分区容忍性)而言,是实际存在 从而无法避免的.因为,分布系统中的处理不是在本机,而是网络中的许多机器相互通信,故网络分区.网络通信故障问 ...
- 计算2的N次方&&计算e
2的N次方 注意:这里在处理的时候并没有用循环来处理,而是用移位的做法. n<<4 就是 n*2^4 ,所以在本例中只需要写 1<<time (time是要求的 ...
- C#类型转换运算符之 explicit implicit
类型转换运算符 explicit和implicit用于声明用户定义的类型转换运算符,如果可以确保转换过程不会造成数据丢失,则可使用这两个关键字在用户定义的类型和其他类型之间进行转换. explicit ...