SQLite的第一版不过是在GDBM上套了个壳

SQLite的第一版不过是在GDBM上套了个壳

今天,SQLite可谓是部署范围最广、安装次数最多(据说安装量排名世界第二)的数据库引擎。从平凡的CRUD项目到著名的App,从浏览器到操作系统,从手机到IoT,到处都能看到SQLite的身影。而SQLite的诞生过程却很偶然。

时光倒回到2000年,那时还没有维基百科,人们无法询问Google如何写一个数据库。SQLite的作者D. Richard Hipp失业在家,凭借着之前构建编译器的经验,实现了一个不需要服务器的数据库引擎。这就是SQLite的第一版。

图1 SQLite的作者D. Richard Hipp(美国 1961-04-09~)

SQLite第一版的诞生过程

Richard曾带领团队为一艘全副武装的战列舰开发程序。他们的程序依赖一个不太可靠的Informix数据库服务器。这个数据库服务器正常运行时倒是没有问题,但一旦宕机,就会导致他们的程序无法正常运行,同时还会弹出一个对话框,上面写着"Can't connect to database server"(无法连接数据库服务器)。更糟糕的是,他们对这个数据库服务器没有任何控制权,结果不出意外,谁弹的报错对话框,谁背锅。

为了提升程序的可靠性,Richard团队先将所有数据读入内存,好在他们的程序不需要增、删、改数据,所以只要将数据全部读入内存,就万事大吉了。此时,Richard脑中闪现出一个大胆的想法:既然如此,为什么还需要数据库服务器呢?为什么不能直接从硬盘取数据呢?如果直接从硬盘读数据,那只要计算机能正常运行,程序就能正常运行。

不过当时还没有这样的数据库。这时,一位同事提醒Richard:"那你为什么不自己写一个呢?"

此言一出没过多久,因项目被终止,Richard失业了。但也是时候开始造数据库了。

咔嚓一个大雷,我的妈呀,SQLite诞生了。

图2 SQLite第一版的提交历史记录 sqlite.org/src/timelin...

SQLite第一版其实也是个套壳数据库

就像今天TiDB使用RocksDB作为其存储引擎,SQLite的第一版也只不过是在GDBM------可谓是NoSQL的鼻祖------上套了个壳。SQLite第一版的README文件中这样写道:

SQLite: An SQL Database Built Upon GDBM

GDBM衍生自DBM,而DBM(D ataB ase Manager的缩写)是来自Unix的键值数据库(key-value database),支持通过主键快速访问数据,最初由计算机界的大佬Ken Thompson编写。

不过GDBM实际上只是一个数据库引擎,仅以代码库(library)的形式向用户提供一系列API,并不支持SQL语句。我们可以将GDBM的数据库看作是存储在硬盘上的哈希表。

虽然是套壳数据库,但这个壳套得可不简单。由于有构建编译器的经验,Richard将每个SQL语句都翻译为一个"程序",这个程序是使用类似某种汇编语言(姑且称为SQL字节码)编写的,为此他又编写了一个编译器,将SQL语句转化为字节码,以及一个能够解析执行SQL字节码的虚拟机。

上世纪90年代的确诞生了不少基于字节码或OpCode的语言,也因此出现了各种虚拟机。Java有JVM,PHP有Zend Engine,Python和Ruby也有自己的虚拟机。但现在看来,谁能想到还有人把SQL也编译成字节码放到虚拟机上执行,而Richard当年就是这么做的。

下面我们就来梳理一下SQLite第一版的架构,看看它是怎么套壳GDBM的。

SQLite第一版的架构

如下图所示,SQLite采用了典型的分层架构,一条SQL语句依次经过

  • 用户接口(UI)层 →
  • 词法分析器(Tokenizer)层 →
  • 语法解析器(Parser)层 →
  • 字节码生成器(Code Generator)层 →
  • 虚拟机(虚拟数据库引擎〔V irtual D ataB ase Engine〕)层 →
  • 数据库后端(D ataB ase B ackEnd〔DBBE〕)层

最终到达GDBM数据库层,变为对GDBM数据库的CRUD(通过调用GDBM提供的API)。

这些层在下图中都用方框表示,方框内写有该层的名称,名称下方括号内是实现层的源代码文件(第一版的代码仓库 www.sqlite.org/src/tree?ci...

图3 SQLite第一版的架构

以图中的INSERT INTO语句为例,首先,UI层将用户输入的这条SQL语句传递给Tokenizer(词法分析器),然后,Tokenizer会将一条SQL语句分割为Token的序列(通过sqliteGetToken()函数),类似[INSERT, INTO, examp, VALUES, (, 'Hello, World!'...]

接下来由Parser(语法分析器)根据语法规则(如图中的cmd ::= INSERT INTO...)解析来自Tokenizer的Token的序列,并调用与匹配的语法规则对应的处理函数,这里是INSERT语句的语法规则对应的处理函数sqliteInsert()

值得一提的是,SQLite第一版中的词法分析器和语法分析器(叫做Lemon)都是作者Richard自己编写的,他从一开始就没有采用lex和yacc等成熟的现成工具,这或许是源于他对自由的极度渴望。下一小节将介绍Richard在软件世界中的生存态度,可能到时候我们就更能理解Richard为什么要重复造轮子了。

回到架构图中,在语法规则处理函数sqliteInsert()中(位于字节码生成器〔Code Generator〕层),SQL语句被翻译(通过sqliteVdbeAddOp()函数)成了如图4左侧黄色方框中的字节码(OpCode)(我这里进行了简化,实际的字节码要比这里的复杂一些),可以看到,此时一条INSERT语句被分解成为若干简单的指令:Open examp------打开名为examp的数据表;Integer 99------添加一个整型的数据项,值为99Put------向数据表中写入记录......

图4 SQLite第一版的架构(续)

当虚拟机(也叫VDBE,V irtual D ataB ase E ngine,虚拟数据库引擎)接收到这一串指令后,就会根据指令的类型(是Open、是Put,还是......)调用对应的函数。如对于Put这个指令,就需要调用sqliteDbbePut()来处理。

现在处理流程终于进入到了DBBE(D ataB ase B ackEnd,数据库后端)层,这里才是最贴近GDBM的那层壳。既然Put指令(对应sqliteDbbePut()函数)要插入记录,那就调用gdbm_store()这个GDBM的API完成数据写入。从设计模式的角度看,DBBE层相当于接口,而GDBM相当于实现类;从MySQL架构的角度看,DBBE层相当于MySQL Server层,而GDBM相当于MyISAM或InnoDB,是一种Pluggable Storage Engine。也许Richard一开始就想到了不能在GDBM一棵树上吊死,底层的存储引擎应该是可以替换的。

至此,SQLite第一版就处理完了一条INSERT语句,向examp表中插入了一条新纪录("Hello, World!", 99)

SQLite的创造者Richard

最后我们来谈一谈SQLite的创造者Richard。

世界上有这样一类人,他们在一小块土地上自己种植食物,生活完全靠自给自足,甚至不使用自来水、电、天然气等公共设施。人们称他们为生存主义者(survivalist)或末日准备者(preppers)。

而Richard仿佛也是他们之中的一员,时刻都在为软件世界的"末日"做着准备:

要是现有的语法分析器不能满足我的需求怎么办?------那我自己写一个吧------于是有了Lemon。

要是版本控制系统不好用怎么办?------那我自己写一个吧------于是有了Fossil。

要是文本编辑器......------那我自己写一个吧。

在一档名为FLOSS Weekly的访谈节目的结尾(我搬运到了B站🎬www.bilibili.com/video/BV1Hj...),主持人问了Richard一个例行问题,"你最喜欢的编辑器是?"

"当然是我自己写的那个,哈哈哈"

为了管理SQLite的代码迭代,Richard还自己造了个叫做Fossil的版本控制系统。更有意思的是,SQLite的源代码使用Fossil来管理,而Fossil又依赖SQLite来存储代码仓库的信息,这就产生了一个在后人看来类似先有鸡还是先有蛋的问题。

毫不夸张地说,除了C编译器和libc中的一些东西之外,SQLite基本上只依赖Richard自己构建的软件。而这正是Richard所要的自由------自己编写的组件越多,就越自由,就越少受到束缚。

日本动画片《新世纪福音战士EVA》电视版的第26集也有一段对"自由"的描述(🎬www.bilibili.com/video/BV1yK...),看过后可能会觉得绝对的自由会让人感到不安、寂寞、迷茫。而Richard却坚定地认为,如果想要自由,就得自己动手。如果有人跟你说"让我们为你解决问题",那你就要有所警惕,因为他们真正想说的是,"我们将会剥夺你的一些自由"。Richard不想让别人控制自己的命运,他要牢牢掌握自己的命运,纵使会付出很多努力。

时间来到2001年,Richard又解除了一层枷锁。他从书架上取出高德纳(Donald Knuth)的巨著The Art of Computer Programming(《计算机程序设计艺术》),用B树替代掉了GDBM。

图5 用B树替代GDBM的提交记录 sqlite.org/src/timelin...

图6 用B树替代GDBM后,README文件的diff(sqlite.org/src/info/00...

参考资料

corecursive.com/066-sqlite-... The Untold Story of SQLite With Richard Hipp

www.bilibili.com/video/BV1Hj... FLOSS Weekly 320: Fossil

相关推荐
Wang's Blog34 分钟前
Redis: 集群环境搭建,集群状态检查,分析主从日志,查看集群信息
数据库·redis
容器( ु⁎ᴗ_ᴗ⁎)ु.。oO1 小时前
MySQL事务
数据库·mysql
cyt涛3 小时前
MyBatis 学习总结
数据库·sql·学习·mysql·mybatis·jdbc·lombok
Rookie也要加油3 小时前
01_SQLite
数据库·sqlite
liuxin334455663 小时前
教育技术革新:SpringBoot在线教育系统开发
数据库·spring boot·后端
看山还是山,看水还是。4 小时前
MySQL 管理
数据库·笔记·mysql·adb
fishmemory7sec4 小时前
Koa2项目实战2(路由管理、项目结构优化)
数据库·mongodb·koa
momo小菜pa4 小时前
【MySQL 09】表的内外连接
数据库·mysql
Jasonakeke4 小时前
【重学 MySQL】四十九、阿里 MySQL 命名规范及 MySQL8 DDL 的原子化
数据库·mysql
程序猿小D4 小时前
第二百六十九节 JPA教程 - JPA查询OrderBy两个属性示例
java·开发语言·数据库·windows·jpa