为什么 SQLite 是用 C 编写的

1. C 最好

注意:本文的第 2.0 和 3.0 节是为响应 Hacker NewsReddit上的评论而添加的。

自 2000 年 5 月 29 日成立以来,SQLite 一直在通用 C 中实现。C 曾经并将继续是实现像 SQLite 这样的软件库的最佳语言。目前没有计划用任何其他编程语言重新编写 SQLite。

C 是实现 SQLite 的最佳语言的原因包括:

  • 表现
  • 兼容性
  • 低依赖
  • 稳定

1.1. 表现

像 SQLite 这样的密集使用的低级库需要快速。(而且 SQLite 很快,例如,参见内部 BLOB 与外部 BLOB比文件系统快 35%。)

C 是编写快速代码的绝佳语言。C 有时被描述为“可移植的汇编语言”。它使开发人员能够尽可能接近底层硬件进行编码,同时仍然保持跨平台的可移植性。

其他编程语言有时会声称“和 C 一样快”。但是在通用编程方面,没有其他语言声称比 C 语言更快,因为它们都不是。

1.2. 兼容性

几乎所有的系统都有能力调用用 C 编写的库。其他实现语言则不然。

因此,例如,用 Java 编写的 Android 应用程序能够调用 SQLite(通过适配器)。如果 SQLite 是用 Java 编码的,那么对于 Android 来说可能会更方便,因为这会使界面更简单。然而,在 iPhone 上,应用程序是用 Objective-C 或 Swift 编码的,它们都无法调用用 Java 编写的库。因此,如果 SQLite 是用 Java 编写的,它就无法在 iPhone 上使用。

1.3. 低依赖

用 C 编写的库没有很大的运行时依赖性。在其最低配置中,SQLite 只需要标准 C 库中的以下例程:

  • 内存管理器()
  • 内存文件()
  • 内存移动()
  • 内存集()
   
  • strcmp()
  • strlen()
  • strncmp()

在更完整的构建中,SQLite 还使用诸如 malloc() 和 free() 之类的库例程和操作系统接口来打开、读取、写入和关闭文件。但即便如此,依赖项的数量也非常少。相比之下,其他“现代”语言通常需要加载数以千计的接口的数兆字节运行时。

1.4. 稳定

C 语言古老而乏味。它是一种众所周知且易于理解的语言。这正是开发像 SQLite 这样的模块时想要的。编写一个小型、快速、可靠的数据库引擎已经够难了,因为它不会随着实现语言规范的每次更新而从你的下面改变实现语言。

2.为什么 SQLite 不是用面向对象的语言编写的?

有些程序员无法想象用一种非“面向对象”的语言来开发像 SQLite 这样复杂的系统。那么,为什么 SQLite 不是用 C++ 或 Java 编码的呢?

  1. 用 C++ 或 Java 编写的库通常只能由用相同语言编写的应用程序使用。很难让用 Haskell 或 Java 编写的应用程序调用用 C++ 编写的库。另一方面,用 C 编写的库可以从任何编程语言调用。

  2. 面向对象是一种设计模式,而不是一种编程语言。您可以使用任何您想要的语言进行面向对象编程,包括汇编语言。一些语言(例如:C++ 或 Java)使面向对象更容易。但是你仍然可以用像 C 这样的语言进行面向对象的编程。

  3. 面向对象并不是唯一有效的设计模式。许多程序员被教导要纯粹根据对象来思考。而且,公平地说,对象通常是分解问题的好方法。但对象并不是唯一的方式,也不总是分解问题的最佳方式。有时,好的旧过程代码比面向对象的代码更容易编写、更容易维护和理解,而且速度更快。

  4. 当 SQLite 刚开始开发时,Java 是一门年轻且不成熟的语言。C++ 比较老,但正在经历成长的痛苦,以至于很难找到任何两个工作方式相同的 C++ 编译器。所以当 SQLite 刚开始开发时,C 绝对是更好的选择。现在情况不那么严峻了,但此时重新编码 SQLite 几乎没有任何好处。

3.为什么 SQLite 不是用“安全”语言编写的?

最近人们对 Rust 或 Go 等“安全”编程语言很感兴趣,在这些语言中,不可能或至少很难犯内存泄漏或数组溢出等常见编程错误。所以经常会出现这样的问题,即为什么 SQLite 不是用“安全”语言编写的。

  1. 在 SQLite 存在的头 10 年里,没有一种安全的编程语言存在。SQLite 可以用 Go 或 Rust 重新编码,但这样做可能会引入比修复更多的错误,而且似乎也可能导致代码变慢。

  2. 安全语言插入额外的机器分支来做一些事情,比如验证数组访问是否在边界内。在正确的代码中,这些分支永远不会被采用。这意味着机器代码不能 100% 进行分支测试,这是 SQLite 质量策略的重要组成部分。

  3. 安全语言通常希望在遇到内存不足 (OOM) 情况时中止。SQLite 旨在从 OOM 中优雅地恢复。目前尚不清楚如何在当前的安全语言中实现这一点。

  4. 所有现有的安全语言都是新的。SQLite 的开发人员对计算机语言研究人员在尝试开发更易于安全编程的语言方面所做的努力表示赞赏。我们鼓励这些努力继续下去。在实现 SQLite 时,我们自己对古老而乏味的语言更感兴趣。

综上所述,SQLite 有朝一日可能会用 Rust 重新编码。在 Go 中重新编码 SQLite 是不可能的,因为 Go 讨厌 assert()。但 Rust 是一种可能性。在 Rust 中重新编码 SQLite 之前必须发生的一些先决条件包括:

  1. Rust 需要再成熟一点,停止变化如此之快,并进一步走向陈旧和乏味。
  2. Rust 需要证明它可用于创建可从所有其他编程语言调用的通用库。
  3. Rust 需要证明它可以生成可在不知名的嵌入式设备(包括缺少操作系统的设备)上运行的目标代码。
  4. Rust 需要选择必要的工具,使人们能够对已编译的二进制文件进行 100% 的分支覆盖测试。
  5. Rust 需要一种机制来优雅地从 OOM 错误中恢复。
  6. Rust 需要证明它可以完成 C 在 SQLite 中所做的工作,而不会显着降低速度。

如果你是一个“rustacean”并且觉得 Rust 已经满足上面列出的前提条件,并且 SQLite 应该用 Rust 重新编码,那么欢迎并鼓励你私下联系 SQLite 开发人员并争论你的情况。