37

SQLite 架构

 5 years ago
source link: https://www.oschina.net/translate/arch-of-sqlite?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

介绍

本文描述了SQLite库的架构。本文信息对哪些想要理解或修改SQLite内部工作内容的人非常有用。

附图展示了SQLite的主要组件以及他们是如何相互操作的。其下的文字解释了各个组件的角色。

YVvQzuJ.jpg!web

总览

SQLite 编译SQL文本为字节码 bytecode , 然后使用虚拟机运行字节码来工作。

sqlite3_prepare_v2() 以及相关接口扮演着编译器的角色,将SQL文本转换成字节码。  sqlite3_stmt 对象是一个容器。该容器用于包含一个实现某一SQL的字节码程序。  sqlite3_step() 接口向虚拟机传递字节码程序, 并执行字节码程序直到完成,或返回产生的一行结果,或碰到一个致命fatal错误, 或被打断。

接口

大部分的C语言的接口在 main.clegacy.c 和 vdbeapi.c 这些源文件中,尽管一些程序被分散在其他文件中,他们可以通过文件域访问数据结构。 sqlite3_get_table() 程序在  table.c 中实现. sqlite3_mprintf() 程序在  printf.c 中实现。 sqlite3_complete() 接口在  tokenize.c 中.  TCL Interface 在  tclsqlite.c 中实现.

为了避免命名冲突,SQLite库的所有的外部符号都使用 sqlite3 前缀。那些用于外部使用的(换句话说,那些符号形成了SQLite的API)符号增加下划线,因此以  sqlite3_ 开头。 扩展API有时候在下划线前增加扩展名;例如:  sqlite3rbu_ 或  sqlite3session_

分词器

当一个包含SQL句子的字符串被处理时,它首先会被发送到分词器。分词器将SQL文本拆分成词,然后将词一个接一个传递给解析器。分词器代码在 tokenize.c 文件中。 注意在这个设计中,分词器调用解析器。熟悉YACC和BISON的人可能习惯用相反的方式-让解析器调用分词器。让分词器调用解析器的方式更好一些,因为这样能够线程安全,并且执行的更快。

解析器

解析器根据上下文为词赋予意思。SQLite的解析器使用 Lemon parser generator 生成。Lemon和YACC/BISON做同样的事情,但是它使用另一种不容易出错的输入语法。Lemon生成一个可重入的,线程安全的parser。Lemon定义了一个非终端析构的概念,因此在遇到语法错误时它不会内存泄漏。在 parse.y 中可以找到驱动Lemon的语法文件和SQLite理解SQL语言的定义的文件。 因为Lemon这个程序一般不会在开发机器上找到,所以Lemon完整的源代码(就一个C文件)被包含在SQLite分发的“tool”子目录中。

代码生成器

在解析器将词装配成解析树后,代码生成器开始分析解析树,生成执行SQL语句工作的字节码。 prepared statement 对象是包含了这些字节码的容器。 代码生成器有很多文件,包含:  attach.cauth.cbuild.c , delete.cexpr.cinsert.cpragma.cselect.ctrigger.cupdate.cvacuum.cwhere.cwherecode.c , 和  whereexpr.c . 大部分重要的魔法发生在这些文件中。  expr.c 操纵着代码生成器的表达。  where .c * 操纵代码生成器处理SELECT,UPDATE和DELETE的WHERE从句。 attach.cdelete.cinsert.cselect.ctrigger.c update.c , and  vacuum.c 这几个文件操纵代码生成器处理和文件名同名的SQL语句。(所有这些文件必要时在 expr.c 和  where.c 中调用)所有其他SQL语句被编写在  build.cauth.c 文件实现了  sqlite3_set_authorizer() 的功能。

代码生成器,尤其时在 where*.c 和  select.c 中的逻辑,有时候被称为查询计划  query planner 。对于任何具体的SQL语句,可能有成千上百个不同的算法来计算结果。查询计划query planner时一个力求从这么多算法中选择一个最好的算法的人工智能。

字节码引擎

字节码程序被代码生成器生成,运行在虚拟机上。 虚拟机自身完整地包含在单独的 vdbe.c 源文件中。 vdbe.h 头文件在虚拟机和剩余的SQLite库之间定义了接口,  vdbeInt.h 定义的结构和接口是虚拟机自己私有的。其他几个 vdbe*.c 文件是虚拟机的助手。 vdbeaux.c 文件包含虚拟机使用的有效工具和剩余SQLite库用于构建VM程序的接口模块。 vdbeapi.c 文件包含虚拟机的扩展接口,如  sqlite3_bind_int() 和  sqlite3_step() 。独立的值 (strings, integer, floating point numbers, and BLOBs) 被存储在由 vdbemem.c 实现,命名为“Mem”的内部对象中。

SQLite使用C语言程序的callbacks来实现SQL函数。即使内置的SQL函数也是这个方式实现。大部分内置SQL函数(如: abs()count()substr() , 等等) 能够在  func.c 源文件中找到。日期和时间转换函数在  date.c 中找到。 一些函数如 coalesce() 和  typeof() 由代码生成器直接生成字节码实现。

B-Tree

SQLite数据库使用B-tree来保存数据到磁盘,B-tree在源文件 btree.c 中实现。在数据库中的每个表和索引使用单独的B-tree。所有的B-tree存储在同一个磁盘文件中。文件格式 file format 细节是稳定的、明确的、在更新中保证兼容性的。

B-tree子系统和其他SQLite库的交互接口在头文件 btree.h 中定义。

页缓存

B-tree模块以固定大小的页向磁盘请求信息。默认的页大小是4096字节,也可以是512字节和65525字节之间任意的2的n次幂。页缓存负责读、写、缓存这些页。页缓存同时提供回滚和原子提交的抽象,同时负责锁定数据库文件。B-tree驱动从页缓存请求指定的页,当它想修改页、或提交、或回滚修改时,通知页缓存。页缓存确保快速、安全、高效地处理请求的所有混杂细节。 基本的页缓存在 pager.c 文件中实现。 WAL mode 逻辑在单独的 wal.c 中。内存缓存在  pcache.c 和  pcache1.c 文件中实现。在 pager.h 头文件中,实现了页缓存子系统和SQLite库的其他系统交互的接口。

操作系统接口

SQLite使用抽象的 VFS 对象,提供在不同操作系统间的移植能力。每个VFS提供了打开(opening)、读(read)、写(writing)和关闭(closing)磁盘上的文件的方法; 提供具体的操作系统任务,比如查找当前时间,获取随机数用来初始化内置的伪随机数生成器。SQLite当前版本为unix在 os_unix.c 文件提供VFS,为windows在 os_win.c 文件提供VFS。

工具

内存分配程序,不区分大小写的字符串比较程序,可移植的文本转数字程序,以及其他工具在 util.c 文件中。在解析器使用的符号表使用 hash.c 中的hash表来存储。 utf.c 源文件包含了Unicode转换子程序。SQLite在 printf.c 有自己私有的 printf() 实现,包含了一些扩展。SQLite在 random.c 中有自己的伪随机数生成器(PRNG)

测试代码

在"src/"文件夹中,那些以 test 开头的源文件是用来做测试的,不会被包含在构建的库中。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK