小巧、快速、可靠。
任选其三。

SQLite 作为应用程序文件格式

执行摘要

具有定义架构的 SQLite 数据库文件通常是绝佳的应用程序文件格式。以下列举了十几个原因。

  1. 简化应用程序开发
  2. 单文件文档
  3. 高级查询语言
  4. 可访问的内容
  5. 跨平台
  6. 原子事务
  7. 增量和持续更新
  8. 易于扩展
  9. 性能
  10. 多个进程并发使用
  11. 多种编程语言
  12. 更好的应用程序

在更详细地描述这些要点之前,首先需要更仔细地考虑“应用程序文件格式”的含义。还可以参考 本文档的简短版本

什么是应用程序文件格式?

“应用程序文件格式”是指用于将应用程序状态持久化到磁盘或在程序之间交换信息的格式。如今,有数千种应用程序文件格式在使用。以下是一些示例。

我们区分“文件格式”和“应用程序格式”。文件格式用于存储单个对象。例如,GIF 或 JPEG 文件存储单个图像,XHTML 文件存储文本,因此它们是“文件格式”,而不是“应用程序格式”。相反,EPUB 文件存储文本和图像(作为包含的 XHTML 和 GIF/JPEG 文件),因此它被认为是“应用程序格式”。本文讨论的是“应用程序格式”。

文件格式和应用程序格式之间的界限很模糊。本文将 JPEG 称为文件格式,但对于图像编辑器来说,JPEG 可能被认为是应用程序格式。这在很大程度上取决于上下文。对于本文,我们假设文件格式存储单个对象,而应用程序格式存储许多不同的对象及其相互关系。

大多数应用程序格式属于以下三类之一。

  1. 完全自定义格式。自定义格式是专门为单个应用程序设计的。DOC、DWG、PDF、XLS 和 PPT 是自定义格式的示例。自定义格式通常包含在一个文件中,便于传输。它们通常也是二进制的,尽管 DWG 格式是一个显著例外。自定义文件格式需要专门的应用程序代码才能读取和写入,并且通常无法从常用的工具(例如 Unix 命令行程序和文本编辑器)访问。换句话说,自定义格式通常是“不透明的块”。要访问自定义应用程序文件格式的内容,需要使用专门为读取和/或写入该格式而设计的工具。

  2. 文件堆格式。有时应用程序状态存储为文件的层次结构。Git 是一个典型的例子,尽管这种现象在一次性和定制的应用程序中经常出现。文件堆格式本质上使用文件系统作为键值数据库,将小块信息存储在不同的文件中。这样做的优势在于,它使内容更容易被常见的实用程序(例如文本编辑器或“awk”或“grep”)访问。但即使文件堆格式中的许多文件很容易读取,通常也有一些文件具有自己的自定义格式(例如:Git“Packfiles”,因此是“不透明的块”,无法在没有专门工具的情况下读取或写入。而且,将文件堆从一个地方或机器移动到另一个地方,比移动单个文件要麻烦得多。而且,将文件堆文档作为电子邮件附件也很困难,例如。最后,文件堆格式破坏了“文档隐喻”:用户无法指向“文档”的单个文件。

  3. 封装的文件堆格式。一些应用程序使用一个文件堆,然后将其封装到某种单文件容器中,通常是 ZIP 存档。EPUB、ODT 和 ODP 是这种方法的示例。EPUB 电子书实际上只是一个 ZIP 存档,其中包含书籍章节文本的各种 XHTML 文件、艺术作品的 GIF 和 JPEG 图像,以及一个专门的目录文件,该文件告诉电子书阅读器所有 XML 和图像文件如何组合在一起。OpenOffice 文档(ODT 和 ODP)也是 ZIP 存档,其中包含 XML 和图像,它们表示其内容以及显示组件部分之间相互关系的“目录”文件。

    封装的文件堆格式是完全自定义文件格式和纯文件堆格式之间的折衷方案。封装的文件堆格式不像自定义格式那样是不透明的块,因为组件部分仍然可以使用任何常见的 ZIP 归档工具访问,但该格式不像纯文件堆格式那样容易访问,因为仍然需要 ZIP 归档工具,而且通常不能在文件层次结构上使用像“find”这样的命令行工具,除非先解压缩它。另一方面,封装的文件堆格式通过将所有内容放在单个磁盘文件中,保留了文档隐喻。而且,因为它经过压缩,所以封装的文件堆格式往往更紧凑。

    与自定义文件格式一样,与纯文件堆格式不同,封装的文件堆格式不容易编辑,因为通常需要重新写入整个文件才能更改任何组件部分。

本文旨在为第四种新的应用程序文件格式类别辩护:SQLite 数据库文件。

SQLite 作为应用程序文件格式

任何可以在文件堆中记录的应用程序状态也可以在具有简单键值架构的 SQLite 数据库中记录,如下所示。

CREATE TABLE files(filename TEXT PRIMARY KEY, content BLOB);
如果内容经过压缩,则此类 SQLite 归档 数据库与等效的 ZIP 存档大小相同(±1%),并且具有能够在不重新写入整个文档的情况下更新单个“文件”的优势。

但是,SQLite 数据库并不限于像文件堆数据库那样的简单键值结构。SQLite 数据库可以有几十、几百甚至几千个不同的表,每个表有几十、几百甚至几千个字段,每个字段都有不同的数据类型、约束和特定含义,所有这些都相互交叉引用,并适当地自动索引以快速检索,所有这些都高效且紧凑地存储在单个磁盘文件中。所有这些结构都由 SQL 架构简洁地记录下来,供人类阅读。

换句话说,SQLite 数据库可以完成文件堆或封装的文件堆格式所能做的一切,甚至更多,而且更加清晰明了。SQLite 数据库比键值文件系统或 ZIP 存档更通用。 (有关详细示例,请参阅 OpenOffice 案例研究 文章。)

理论上,可以使用自定义文件格式来实现 SQLite 数据库的功能。但任何与关系数据库一样表达能力强的自定义文件格式,都需要一个庞大的设计规范,以及数万甚至数十万行代码来实现。最后的结果将是“不透明的块”,无法在没有专门工具的情况下访问。

因此,与其他方法相比,使用 SQLite 数据库作为应用程序文件格式具有明显的优势。以下列举并阐述了其中一些优势。

  1. 简化应用程序开发。不需要为读取或写入应用程序文件编写新的代码。只需链接到 SQLite 库,或将 单个“sqlite3.c”源文件 与应用程序 C 代码的其余部分一起包含,SQLite 将负责所有应用程序文件 I/O。这可以将应用程序代码的大小减少数千行,从而相应地节省开发和维护成本。

    SQLite 是世界上 使用最广泛 的软件库之一。每天都有数十亿个 SQLite 数据库文件在使用,包括智能手机、小工具和桌面应用程序。SQLite 经过 严格测试 并被证明是可靠的。它不是一个需要太多调整或调试的组件,使开发人员能够专注于应用程序逻辑。

  2. 单文件文档。SQLite 数据库包含在一个文件中,便于复制、移动或附加。保留了“文档隐喻”。

    SQLite 没有文件命名要求,因此应用程序可以使用任何自定义文件后缀来帮助标识文件“属于”应用程序。SQLite 数据库文件在其标题中包含一个 4 字节的 应用程序 ID,该 ID 可以设置为应用程序定义的值,然后用于标识“类型”文档,以便实用程序(例如 file(1))进一步增强文档隐喻。

  3. 高级查询语言。SQLite 是一个完整的关系数据库引擎,这意味着应用程序可以使用高级查询访问内容。应用程序开发人员无需花费时间考虑如何从文档中检索所需的信息。开发人员编写表达他们想要“什么”信息的 SQL,并让数据库引擎弄清楚如何最佳地检索该内容。这有助于开发人员“抬头”,专注于解决用户的问题,避免花费时间“低头”摆弄低级文件格式细节。

    文件堆格式可以被视为一个键值数据库。键值数据库比没有数据库好。但如果没有事务、索引、高级查询语言或适当的架构,使用键值数据库比使用关系数据库要困难得多,而且更容易出错。

  4. 可访问的内容。SQLite 数据库文件中保存的信息可以使用常用的开源命令行工具访问,这些工具默认安装在 Mac 和 Linux 系统上,并且可以作为独立的 EXE 文件在 Windows 上免费获得。与自定义文件格式不同,不需要特定于应用程序的程序来读取或写入 SQLite 数据库中的内容。SQLite 数据库文件不是不透明的块。确实,像文本编辑器或“grep”或“awk”这样的命令行工具在 SQLite 数据库上没有用,但 SQL 查询语言是检查内容的更强大、更便捷的方法,因此无法使用“grep”和“awk”等工具并不被视为损失。

    SQLite 数据库是一个 定义明确且记录完善 的文件格式,被数百万个应用程序广泛使用,并且向后兼容其 2004 年的首次发布,并承诺在未来几十年内继续保持兼容。SQLite 数据库文件的持久性对于定制应用程序尤其重要,因为它允许在未来很久以后访问文档内容,即使原始应用程序的所有痕迹都已丢失。数据比代码寿命更长。SQLite 数据库 被美国国会图书馆推荐 作为长期保存数字内容的存储格式。

  5. 跨平台. SQLite 数据库文件可在 32 位和 64 位机器之间、大端和小端架构之间以及各种 Windows 和类 Unix 操作系统之间移植。使用 SQLite 应用程序文件格式的应用程序可以存储二进制数值数据,而无需担心整数或浮点数的字节顺序。文本内容可以以 UTF-8、UTF-16LE 或 UTF-16BE 格式读取或写入,SQLite 会自动实时执行任何必要的转换。

  6. 原子事务. 对 SQLite 数据库的写入是 原子性的。它们要么完全发生,要么根本不发生,即使在系统崩溃或断电期间也是如此。因此,即使在写入磁盘时恰好断电,也不会有损坏文档的危险。

    SQLite 是事务性的,这意味着可以将多个更改组合在一起,以便它们要么全部发生,要么全部不发生,并且如果在提交之前发现问题,可以回滚这些更改。这允许应用程序增量地进行更改,然后在将更改提交到磁盘之前对结果数据运行各种完整性和一致性检查。 Fossil DVCS 使用这种技术来验证在每次更改之前都没有丢失任何存储库历史记录。

  7. 增量和连续更新. 当写入 SQLite 数据库文件时,只有实际更改的文件部分才会写入磁盘。这使得写入速度更快,并节省了 SSD 的磨损。与自定义和包装的文件堆格式相比,这是一个巨大的优势,这两种格式通常需要重写整个文档才能更改单个字节。纯文件堆格式在某种程度上也可以进行增量更新,尽管与 SQLite(单个页面)相比,文件堆格式(单个文件)的写入粒度通常更大。

    SQLite 还支持连续更新。而不是在内存中收集更改,然后仅在“文件/保存”操作时将它们写入磁盘,而是可以将更改在发生时写入磁盘。这避免了系统崩溃或断电时的工作丢失。一个 自动撤消/重做堆栈,使用触发器管理,可以保存在磁盘上的数据库中,这意味着撤消/重做可以在会话边界之间发生。

  8. 易于扩展. 随着应用程序的增长,可以通过向模式添加新表或向现有表添加新列来简单地向 SQLite 应用程序文件格式添加新功能。添加列或表不会改变先前查询的含义,因此只要注意确保保留旧有列和表的含义,就可以保持向后兼容性。

    当然,也可以扩展自定义或文件堆格式,但这样做通常要困难得多。如果添加了索引,那么所有更改相应表的应用程序代码都必须找到并修改以保持这些索引最新。如果添加了列,那么所有访问相应表的应用程序代码都必须找到并修改以考虑新列。

  9. 性能. 在许多情况下,SQLite 应用程序文件格式将比 文件堆格式 或自定义格式 更快。除了原始读写速度更快之外,SQLite 通常还可以显着提高启动时间,因为应用程序无需将整个文档读取并解析到内存中,而可以执行查询以仅提取初始屏幕所需的信息。随着应用程序的进行,它只需要加载绘制下一个屏幕所需的信息,并且可以丢弃不再使用的先前屏幕的信息。这有助于控制应用程序的内存占用量。

    文件堆格式可以像 SQLite 一样增量读取。但许多开发人员惊讶地发现,SQLite 可以从其数据库中读取和写入更小的 BLOB(大小小于约 100KB)的速度比从文件系统中读取或写入这些相同的 BLOB 作为单独文件的速度更快。(有关更多信息,请参阅 比文件系统快 35%内部 BLOB 与外部 BLOB)。但是,运行关系型数据库引擎会产生开销,因此不应假设直接文件 I/O 比 SQLite 数据库 I/O 快,因为通常并非如此。

    无论哪种情况,如果 SQLite 应用程序中出现了性能问题,这些问题通常可以通过在模式中添加一两个 CREATE INDEX 语句或运行 ANALYZE 一次来解决,而无需修改任何应用程序代码。但是,如果在自定义或文件堆格式中出现性能问题,则修复通常需要对应用程序代码进行大量更改,以添加和维护新的索引或使用不同的算法提取信息。

  10. 多个进程的并发使用. SQLite 自动协调从多个线程和/或进程对同一文档的并发访问。两个或多个应用程序可以同时连接并从同一文档中读取。写入是串行化的,但由于写入通常只占用几毫秒,因此应用程序只需轮流写入。SQLite 自动确保文档的低级格式没有损坏。相反,使用自定义或文件堆格式来实现相同的目标需要在应用程序中进行广泛的支持。并且支持并发所需的应用程序逻辑是臭名昭著的 bug 陷阱。

  11. 多种编程语言. 虽然 SQLite 本身是用 ANSI-C 编写的,但几乎所有你能想到的其他编程语言都存在接口:C++、C#、Objective-C、Java、Tcl、Perl、Python、Ruby、Erlang、JavaScript 等等。因此,程序员可以使用他们最舒服的语言以及最适合项目需求的语言进行开发。

    SQLite 应用程序文件格式在有多个独立程序的集合或“联合”的情况下是一个很好的选择,这些程序通常是用不同的语言编写,并且由不同的开发团队编写。这在研究或实验室环境中很常见,其中一个团队负责数据采集,而其他团队负责分析的各个阶段。每个团队可以使用他们最舒服的硬件、操作系统、编程语言和开发方法,只要所有程序都使用具有相同模式的 SQLite 数据库,它们就可以相互操作。

  12. 更好的应用程序. 如果应用程序文件格式是 SQLite 数据库,那么该文件格式的完整文档将包括数据库模式,可能还有一些关于每个表和列代表什么内容的额外说明。另一方面,自定义文件格式的描述通常会持续数百页。文件堆格式虽然比完全自定义格式更简单易于描述,但仍然往往比 SQL 模式转储更大更复杂,因为仍然需要描述各个文件的名称和格式。

    这不是一个无关紧要的问题。清晰、简洁、易于理解的文件格式是任何应用程序设计的重要组成部分。弗雷德·布鲁克斯在他的畅销计算机科学著作《人月神话》中说:

    表示是计算机编程的本质。
    ...
    向我展示你的流程图并隐藏你的表格,我将继续迷惑不解。向我展示你的表格,我通常不需要你的流程图;它们将是显而易见的。

    罗伯·派克在他的《编程规则》中以这种方式表达了同样的想法:

    数据占主导地位。如果你选择了正确的数据结构并很好地组织了它们,算法几乎总是显而易见的。数据结构,而不是算法,是编程的核心。

    Linus Torvalds 在 2006 年 6 月 27 日的 Git 邮件列表中使用了不同的词语来表达相同的意思:

    糟糕的程序员担心代码。优秀的程序员担心数据结构及其关系。

    重点是:SQL 数据库模式几乎总是能够更好地定义和组织表格和数据结构及其关系。拥有清晰、简洁、定义明确的表示几乎总是会导致应用程序性能更好、问题更少、更易于开发和维护。

结论

SQLite 并非适用于所有情况的完美应用程序文件格式。但在许多情况下,SQLite 比自定义文件格式、文件堆格式或包装的文件堆格式要好得多。SQLite 是一种高级的、稳定的、可靠的、跨平台的、广泛部署的、可扩展的、高性能的、可访问的、并发文件格式。它值得你在下次应用程序设计时考虑作为标准文件格式。

此页面最后修改于 2022-01-08 05:02:57 UTC