SQLite 不为人知的故事

《形形色色的数据库》里,向大家提到过 SQLite 小型嵌入式数据库。现在人们使用最多的数据库,可能就是 SQLite。在浏览器、手机、汽车、ATM机、飞机……各类设备上它都有广泛部署。

SQLite 数据库以一个独立的文件存在,不占用操作系统进程。其它关系型数据库,一般是客户端/服务端大型的技术架构,需要安装、启动服务,占用端口。

受益于整个数据库(包括结构定义、表、索引、数据本身)都在一个文件里,SQLite 很容易跨平台使用。麻雀虽小,五脏俱全,关系型数据库的关键特性(如事务),它都能良好支持。性能也很出色,同时它做到了 100% 的测试覆盖,稳定性和安全都有保障。

正是因为以上特点,它成为了移动设备中数据库的第一选择。

2022 年 7 月初,SQLite 的作者 Richard 参与了一期播客节目,讲述了 SQLite 是如何诞生和发展的。

这篇文章整理了博客节目的关键内容。看完后也许能理解,为什么我们没有这样的程序员,为什么我们做不出世界级的基础设施软件。

SQLite 如何诞生

Richard 早先是个外包程序员,所在团队承接了一个军工项目,开发军方战舰上使用的故障管理系统。

该系统使用了 IBM 的 Informix 数据库,可能是服务器方面的原因,数据库一直不是很稳定。只要数据库服务器挂掉,他们开发的系统就会无法正常运行,弹出提示:“无法连接到数据库”。

甲方大佬看到他们的系统产生了错误提示,也不关心具体背后是谁出了错,就把锅扣在了他们身上。

国内外的外包程序员都是伤不起啊,遇到这种情况,一般程序员都是选择换个环境,或者祈祷分到一个好侍侯的甲方项目中。

战舰故障管理系统的使用频率比较高,外包团队又没有数据库服务器的管控运维权限,怎么解决无法连接数据库的问题呢?

Richard 有了一个灵感:“为什么一定要从数据库引擎中取数据,而不能从硬盘中直接读?”

只要操作系统和硬盘没问题,程序能正常运行,就可以读取本地文件,而不必依赖数据库引擎的服务正常。

“功能越多,越不稳定。”我记得八十年代的时候,父母看一些工业产品时,就经常说这句话。奥卡姆剃刀原则“若无必要,勿增实体”在产品设计中是一条重要的指导原则:1、系统越简单,故障率越低;2、用户认知负担降低;3、带来了简洁的使用体验;4、成本降低。

他们在市面上找了许久,发现没有现成的 SQL 数据库引擎可以做到这一点。团队其它成员就鼓励 Richard 写一个出来。

查了一下资料,同类型数据库 Microsoft Access 1.1 在 1992 年就有了,但应该不能在 Linux 平台下使用。SQLite 是 2000 年才开始开发。

Richard 并没有立即开始,直到因为政策原因外包项目合同中断,Richard 失业了数月,才正式动手编写 SQLite。

可能也有拖延症。

当时是 2000 年,还没有维基百科,上网也是电话拨号,美国只有 1% 的家庭拥有 ADSL 宽带网络。

在 Google 上几乎搜不到构建一个本地文件型数据库的有用信息。

Richard 有开发编译器的经验,于是他把这项经验应用到了开发数据库上。

他把每条 SQL 语句(DDL 创建库表结构和 DML 读写操作数据,都是SQL语句)看作是一个程序,只要这个程序能执行成功,那就达到了最初的目的。

于是,问题变成了把程序编译成某种可执行代码,这就用到了他开发编译器的经验,他写了一个字节码引擎来运行一个查询。

SQLite 核心原理是通过一个编译器将 SQL 翻译成字节码,就这样,SQLite 诞生了。

面对生活中、工作中的痛苦时,我们习惯咬牙忍受,常常采取迁就、回避、的态度,长期下去就变得麻木,这是我们不擅长创新的重要原因之一。

SQLite 如何发展壮大

SQLite 被发布到网上。

Richard 很激动地宣布,他在掌上电脑中跑起一个 SQL 数据库。就是下面这台 Palm Pilot 掌上电脑。

作为 80 后,我对掌上电脑印象深刻,曾梦寐以求想拥有一台,结果是拥有了电子词典。

SQLite 一发布就受到很多人关注,这让 Richard 大受鼓舞,不断迭代升级他的产品。

一两年后的某天,他接到了摩托罗拉公司的电话。摩托罗拉正在开发新版本的手机操作系统,有些功能适合用 SQLite,希望能把 SQLite 内嵌进去,并让 Richard 提供技术支持。

Richard 很激动,但对方问起价格时,他一时不知道该怎么答复。

放下电话,Richard 对产品定价仍没有头绪,他没有想过系统化地运作这个项目,也没有可参考的定价方式。

最后几乎是一拍脑袋,定了 8 万美金,当时折算人民币近 60 万。这就是 SQLite 的第一笔收入。Richard 又找了三个人,和他一起完成了这个项目。

2001、2002 年时,摩托罗拉公司的体量十分庞大,比今天国内的小米、华为等公司大很多。这样一个巨头,肯为一个小软件的作者致电并付费,这样的商业环境,对知识产权的重视,真是令人羡慕。可以想象,现在某些公司遇到这样的情况时,还是会给某部门的程序员下达一个任务:加班两周抄一个出来!

继摩托罗拉之后,又一笔生意找上门来,这次是 AOL(今天的美国在线,曾被 44 亿美元收购,又和雅虎打包一起 50 亿美元卖出)。

AOL 当时有邮寄 CD 的业务,他们想在CD中使用 SQLite 数据库的一些增强功能,为此 AOL 每月向 Richard 支付 10 美元。

不知道是不是每张CD 每月支付10 美元,原文没有说。如果是,这一下子就可以实现财富自由了。所以,完成从一个消费方到生产方的身份转换很重要,生产一个有价值的东西(无论是实体的,还是虚拟的),即使可能是小众的产品,也会产生回报。

紧接着,Symbian OS(塞班手机操作系统,后被诺基亚收购)和诺基亚分别和 Richard 签订了合同,这时 Richard 已经需要频繁飞往国外和这些公司谈合作了。

Symbian 在数据库选型期间,一共测试了 10 款产品,其中 2 款开源,7 款商业产品,还有 1 款就是 SQLite。考虑到商业公司相对更有保障,Symbian 给了商业产品多次优化调整的机会,但最终还是 SQLite 胜出。

成立 SQLite 联盟是里程碑事件,联盟目的是为项目提供更多的资金并让更多的协作者参与维护项目,以保证产品可以长期使用。

像一直等着生意上门一样,成立联盟这件事也不是 Richard 主动推动的,而是由管理 Mozilla 基金会的一位女士一手推动促成。Mozilla、Symbian 和 Adobe 三家大公司成为了联盟的创始成员,他们和其它联盟成员共同赞助 SQLite 发展,而产品的决策权仍在 Richard 手中。

有人说 Richard 做了个错误的决定,但事实证明,后面 SQLite 一直发展的很好。

再后来,2005年的时候,Google 联系了 Richard,向他展示了安卓的原型机。他们又在安卓系统上展开了 SQLite 的合作。这正是智能手机诞生的前夜。

Richard 回忆当时的一幕,他们正在使用 SQLite 调试适配手机上的一些应用,这时来了电话,电话被接通——Richard 意识到,这不再仅仅是手机。智能手机的时代要来临了,而摩托罗拉、Symbian 、诺基亚都没有意识到这点。

Richard 和 Google 签署了保密协议。他不能告诉摩托罗拉们,时代要变了。

SQLite 如何保持稳定

Richard 一度对 SQLite 很自信,他认为SQLite 绝对没有严重的错误,事实上好像也是如此。但安卓证明了这不是真的。当 SQLite 随着安卓系统在数百万台设备上部署时,出现了很多错误。

Richard 学习了航空电子设备制造商的一些经验。在安全至关重要的航空产品的质量标准里,有一套 DO-178B 标准。其中一个关键是要求对产品 100% 的测试覆盖率。

Richard 把这个理念应用到了 SQLite 的开发中,他决定编写测试代码以使 SQLite 达到 100% 的质量。

在软件开发中,达到 90% 或 95% 的测试覆盖率是比较容易的。想达到最后 5% 非常非常困难,他们花了大概一年的时间才达到了100%的测试覆盖率,测试用例惊人地达到了10万个,在多种芯片、多种操作系统中完整测试一遍将会产生 10 亿次测试。这件事做完,就再也没有收到来自安卓的错误报告。

这一举措发挥了重要的作用,在接下来的八九年里,SQLite 真的没有发生任何错误。

第三方测试平台对各类数据库做了极限测试,除了 SQLite,只有 PostgresSQL 能达到同样的水平。

PostgresSQL 要做到这一点,比 SQLite 更不容易。因为PostgresSQL 正是文章一开始提到的客户端/服务器复杂架构,而且是原生分布式数据库,分布式会引入更多错误的机会。而 SQLite 只有一个文件。

其它数据库,包括 Oracle、DB2 等,都会在这项极限测试中奔溃。

伟大程序员的一些独特理念

毫无疑问,Richard 是一个伟大的程序员,他发明的 SQLite 嵌入式数据库是众多移动设备、移动应用能高效稳定运行的基础设施之一。难以想象,如果有一天 SQLite 不能用了,我们的生活将会受到多么大的影响。

他在播客中也提到了一些理念。

从第一性原理出发

Richard 准备开发 SQLite 时,和普通人一样,先去互联网上搜索了很多资料。遗憾的幸运的是,他什么都没有搜到。

很多计算机核心技术理论,都来自于麻省理工大学、哈佛大学或伯克利大学,他也没有办法从这些大学里得到帮助。

他只能动脑子用原创的方法来实现新的数据库。

奇怪的是,等回过头来再看 PostgreSQL 使用的火山模型和 SQLite 曾经的字节码模型,二者在顶层设计时有着不同的路线,但在同一技术领域中,两条独立的发展路线都给出了同样稳定的结果。

Richard 认为这是运用第一性原理后的结果。

“一点就透”

Richard 讲了一件很有意思的事情。有一次在德国召开一个 PHP 的会议,邀请 Richard 做演讲嘉宾(PHP 已经将 SQLite 集成了进去),另外还有一位演讲嘉宾是 MySQL 早期开发者。

这位开发者讲了 MySQL 一个关键性能实现方案。Richard 听了心想,“哇,这真是一个聪明的主意。” 于是就在回去的飞机上,打开电脑为 SQLite 实现了这一特性。

真正的领域专家,有时候只需要一点点启发,就能立即明白其中的关键,并把它实现出来。如果你怎么都没办法向别人说清楚一件事的时候,不要怀疑自己的能力,要怀疑对方的知识储备。

没有依赖的自由

庄子说御风而行的列子“此虽免乎行,犹有所待者也。”

Richard 可能没读过庄子,但他似乎更明白不依赖于他人的自由之可贵。

SQLite 除了依赖 C 编译器和 libc 中的一些东西,没有依赖任何“轮子”,需要到的“轮子”,全部都自己发明了一遍。

Richard 说,假如当时选择使用 Berkeley DB 作为 SQLite 第二版的存储引擎, 起初它是开源的,但后来就被卖给了Oracle,变成了双源模式,不付许可费就看不到源代码,那肯定会出问题。

SQLite 中的解析器也是作者多年前自己编写的,没有使用现成的 Yak 或 Bison 等产品。

当 SQLite 需要版本控制的时候,同样没有使用 CVS 或Linux之父开发的 Git ,而是自己开发了一套 Fossil 版本控制软件。

Richard 甚至计划自己开发一个邮件服务去替换 Gmail,他不希望邮件内容被厂商记录和控制。

自己做,不依赖第三方,就能掌握自己的命运,就会拥有更多的自由。

SQLite 作者的一点建议

“构建一个没有服务器的数据库引擎,直接与硬盘文件交互,忽略数据类型,这在当时绝对是一个疯狂的想法。”

“如果去问专家,他们都会说:‘那是不可能的,永远也行不通。这个想法太愚蠢了。’”

“幸运的是,我不认识任何专家,我就去做了,事情就做成了。我认为,不要过多地听专家的话,做自己认为有意义的事,就有希望能做成。”