Small. Fast. Reliable.
Choose any three.
SQLite如何工作

1.背景

SQLite是一个软件库 ,可将应用程序生成的高级磁盘I / O请求转换为可由操作系统执行的低级I / O操作。该应用程序使用SQL语言构造高级I / O请求 。SQLite将每个高级SQL语句转换为许多低级I / O请求的序列(打开文件,从文件中读取几个字节,将几个字节写入文件等),这些操作可以完成请求的工作SQL。

应用程序可以通过直接调用操作系统I / O例程或使用诸如Berkeley DBRocksDB(仅举两个)之类的键/值存储引擎来完成其所有磁盘I / O。 但是使用基于SQL语言的更高级别的接口有很多优点。

  1. SQL是一种非常高级的语言。几行SQL可以替换成百上千行的过程代码。因此,SQL减少了开发和维护应用程序所需的工作量,从而有助于减少应用程序中的错误数量。

  2. SQL和SQLite是 事务性的。事务存储系统的使用使人们更容易对应用程序的行为进行推理,即使在遇到软件错误,硬件故障或断电的情况下,也可以编写健壮的应用程序。

  3. SQLite通常 比直接底层I / O更快。这是违反直觉的。人们会期望像SQLite这样的高级接口会带来运行时的损失。而且,从理论上讲,这是正确的。但是实际上,基于SQL的系统(如SQLite)会进行许多幕后优化,以至于应用程序开发人员将永远没有时间来创建和维护,而基于SQL的系统最终会带来净性能提升。

1.1。SQLite与大多数其他SQL数据库不同

除SQLite外,还有许多基于SQL的数据库管理系统可用。常用选项包括MySQL,PostgreSQL和SQL-Server。所有这些系统都使用SQL语言与应用程序进行通信,就像SQLite一样。但是这些其他系统在重要方面与SQLite不同。

  1. SQLite是无服务器的软件库,而其他系统则基于客户端-服务器。使用MySQL,PostgreSQL,SQL-Server和其他应用程序,应用程序会将包含一些SQL的消息发送到单独的服务器线程或进程。该单独的线程或进程执行请求的I / O,然后将结果发送回应用程序。但是SQLite没有单独的线程或进程。SQLite使用相同的程序计数器和堆存储在与应用程序相同的地址空间中运行。SQLite不进行进程间通信(IPC)。当应用程序将SQL语句发送到SQLite(通过调用适当的SQLite库子例程)时,SQLite会在与调用方相同的线程中解释SQL。当SQLite API例程返回时,它不会留下任何与应用程序分开运行的后台任务。

  2. SQLite数据库是磁盘上的单个普通文件(格式明确的文件)。对于其他系统,“数据库”通常是大量单独的文件,隐藏在文件系统的晦涩目录中,甚至散布在多台计算机上。但是使用SQLite,完整的数据库只是普通的磁盘文件。

2. SQL是一种编程语言

理解SQL数据库引擎如何工作的最好方法是将SQL视为一种编程语言,而不是一种“查询语言”。每个SQL语句都是一个单独的程序。应用程序构造SQL程序源文件,并将它们发送到数据库引擎。数据库引擎将SQL源代码编译为可执行文件形式,运行该可执行文件,然后将结果发送回应用程序。

尽管SQL是一种编程语言,但它与其他编程语言(例如C,Javascript,Python或Go)不同,因为SQL是一种 声明性语言 ,而其他则是 命令性语言。这是一个重要的区别,对用于将程序源文本转换为可执行格式的编译器的设计有影响。但是,这些细节不应损害SQL实际上只是另一种编程语言的事实。

2.1。编程语言处理步骤

所有编程语言都分两个步骤处理:

  1. 将程序源文本转换为可执行格式。

  2. 运行在上一步中生成的可执行文件,以执行所需的操作。

所有编程语言都使用这两个基本步骤。主要区别在于可执行文件格式。

诸如C ++和Rust的“已编译”语言会将源文本转换为可以由基础硬件直接执行的机器代码。存在与SQL相同的SQL数据库系统-它们将每个SQL语句直接转换为机器代码。但是这种方法并不常见,不是SQLite采取的方法。

其他语言,例如Java,Perl,Python和TCL,通常会将程序源文本转换为字节码。然后,该字节码通过解释器运行,该解释器读取该字节码并执行所需的操作。SQLite使用此字节码方法。如果您在SQLite中使用“ EXPLAIN ”关键字开头的任何SQL语句,它将向您显示生成的字节码,而不是运行字节码。

另一种方法是将程序源文本转换为内存中的对象树。这棵树是“可执行文件”。解释通过遍历树来运行可执行文件。这是MySQL,PostgreSQL和SQL-Server使用的技术。

当然,并非每种语言都能很好地适应上述类别之一。这适用于SQL数据库引擎和更熟悉的命令式编程语言。Javascript以使用混合执行模型而闻名,该模型最初将代码编译成一个对象树,但可能会进一步将其(使用即时编译)转换为更有效的字节码或机器代码,以作为一种提升方法。表现。

可执行文件的格式实际上只是一个实现细节。关键是所有语言都有一个将程序转换为可执行格式的编译器步骤和一个执行已编译程序的运行步骤。

2.2。编译SQLite程序

将SQL程序提交到SQLite时,第一步是将源文本拆分为“令牌”。令牌可能是:

空格和注释令牌将被丢弃。所有其他令牌都馈入 LALR(1)解析器 ,该解析器分析输入程序的结构并为输入程序生成 抽象语法树(AST)

解析器将AST转发到代码生成器。代码生成器是SQLite的心脏,并且是大多数魔术发生的地方。代码生成器解析AST中的符号名称-将输入SQL中的列和表的名称匹配到数据库的实际列和表中。代码生成器还对AST进行了各种转换,以“优化”它。最后,代码生成器选择适当的算法来实现AST请求的操作,并构造字节码来执行这些操作。

由代码生成器生成的字节码称为“准备语句”。将SQL源文本转换为准备好的语句类似于通过调用gcc或clang将C ++程序转换为机器代码。引入人类可读的源文本(SQL或C ++),然后出现机器可读的可执行文件(字节码或机器代码)。

3.进一步阅读